refactor all the things, add congo line of planets that bob to music

This commit is contained in:
Silas 2022-03-16 01:13:55 -04:00
parent 91e1761497
commit 53efb64c84
Signed by: silentsilas
GPG Key ID: 4199EFB7DAA34349
19 changed files with 404 additions and 287 deletions

64
src/actors/Hand.ts Normal file
View File

@ -0,0 +1,64 @@
import {
AudioAnalyser,
BufferAttribute,
BufferGeometry,
Color,
Float32BufferAttribute,
InterleavedBufferAttribute,
Mesh,
MeshToonMaterial,
} from "three";
import { RandomNumberGenerator } from "..";
export class Hand {
mesh: Mesh<BufferGeometry, MeshToonMaterial>;
originalPositions?: BufferAttribute | InterleavedBufferAttribute;
distortions?: Array<number>;
analyser?: AudioAnalyser;
positions?: BufferAttribute | InterleavedBufferAttribute;
distortionMax: number;
distortionPower: number;
constructor(mesh) {
this.mesh = mesh;
this.distortionMax = 4;
this.distortionPower = 3;
}
initialize(rng: RandomNumberGenerator, analyser: AudioAnalyser) {
this.analyser = analyser;
// set up distortion for each vertex
this.originalPositions = this.mesh.geometry.getAttribute("position");
this.distortions = (this.originalPositions.array as Array<number>)
.slice(0)
.map(() => rng() * 2 - 1);
this.positions = this.mesh.geometry.getAttribute("position");
this.mesh.parent.position.x = this.mesh.parent.position.x + 2.8;
this.mesh.parent.position.y = this.mesh.parent.position.y + 0;
this.mesh.parent.position.z = this.mesh.parent.position.z + 2.2;
this.mesh.parent.rotateZ(Math.PI / 2);
this.mesh.geometry.center();
this.mesh.material = new MeshToonMaterial({
color: new Color(0, 0.3, 0),
});
this.mesh.material.wireframe = true;
}
update() {
const sound = Math.pow(
(this.analyser.getAverageFrequency() / 255) * this.distortionMax,
this.distortionPower
);
const newPositions = new Float32BufferAttribute(
(this.positions.array as Array<number>).map((_position, index) => {
const distortion = this.distortions[index] * sound;
return distortion / 10 + this.originalPositions.array[index];
}),
3
);
this.mesh.geometry.setAttribute("position", newPositions);
}
}

94
src/actors/Jukebox.ts Normal file
View File

@ -0,0 +1,94 @@
import { AudioAnalyser, AudioListener } from "three";
import { LoadAudio } from "../audio";
export class Jukebox {
parser: SRTParser;
listener: AudioListener;
srts?: SRT[];
elapsedTime: number;
subtitleDiv: HTMLElement;
constructor(parser: SRTParser, listener: AudioListener) {
this.parser = parser;
this.listener = listener;
this.elapsedTime = 0;
this.subtitleDiv = document.getElementById("subtitles");
}
async loadSRT(): Promise<SRT[]> {
const response = await fetch("static/music/who_am_i_with_music.srt");
const responseText = await response.text();
return this.parser(responseText);
}
async initializeAudio(clickCallback: ClickCallback) {
const silence = new Audio("/static/music/silence.mp3");
const music = await LoadAudio(
this.listener,
"/static/music/space_chillout.mp3",
1,
"music",
true
);
const poem = await LoadAudio(
this.listener,
"/static/music/who_am_i.mp3",
1,
"poem",
false
);
this.srts = await this.loadSRT();
const musicAnalyser = new AudioAnalyser(music, 512);
const poemAnalyser = new AudioAnalyser(poem, 512);
const startButton = document.getElementById("startButton");
startButton.style.display = "block";
startButton.addEventListener("click", () => {
silence.play();
music.play();
poem.play();
// CORE.playing = true;
startButton.remove();
clickCallback();
});
return {
music,
musicAnalyser,
poem,
poemAnalyser,
};
}
update(delta: number) {
this.elapsedTime += delta;
let found = false;
this.srts.forEach((srt) => {
if (this.elapsedTime > srt.start && this.elapsedTime < srt.end) {
found = true;
this.subtitleDiv.innerHTML = srt.text;
}
});
if (!found) this.subtitleDiv.innerHTML = "";
if (this.elapsedTime > 70) {
document.getElementById("credits").style.display = "block";
}
}
}
type SRTParser = {
(srt: string): SRT[];
};
type SRT = {
id: number;
start: number;
end: number;
text: string;
};
type ClickCallback = () => void;

24
src/actors/OrbitCamera.ts Normal file
View File

@ -0,0 +1,24 @@
import { MathUtils, PerspectiveCamera, Vector3 } from "three";
export class OrbitCamera {
angle: number;
angularSpeed: number;
radius: number;
camera: PerspectiveCamera;
constructor(camera: PerspectiveCamera) {
this.camera = camera;
this.angularSpeed = MathUtils.degToRad(20);
this.angle = 0;
this.radius = 5;
console.log(this.angularSpeed);
}
update(delta: number, lookAt: Vector3) {
this.camera.position.x = Math.sin(this.angle) * this.radius;
this.camera.position.z = Math.cos(this.angle) * this.radius;
this.camera.position.y = Math.cos(this.angle) * 1.5 + 1;
this.angle += this.angularSpeed * delta;
this.camera.lookAt(lookAt);
}
}

38
src/actors/OrbitLight.ts Normal file
View File

@ -0,0 +1,38 @@
import {
MathUtils,
Mesh,
MeshToonMaterial,
PointLight,
SphereBufferGeometry,
} from "three";
export class OrbitLight {
angle: number;
angularSpeed: number;
radius: number;
light: PointLight;
constructor(light: PointLight) {
this.light = light;
this.angle = 30;
this.angularSpeed = MathUtils.degToRad(35);
this.radius = 1.5;
this.initialize();
}
initialize() {
const geometry = new SphereBufferGeometry(0.1);
const material = new MeshToonMaterial({ color: 0xffff00 });
const sphere = new Mesh(geometry, material);
this.light.add(sphere);
this.update(0);
}
update(delta: number) {
this.light.position.x = Math.cos(this.angle) * this.radius * 2;
this.light.position.z = Math.sin(this.angle) * this.radius;
this.light.position.y = Math.cos(this.angle) * 5;
this.angle += this.angularSpeed * delta;
}
}

67
src/actors/Planets.ts Normal file
View File

@ -0,0 +1,67 @@
import {
AudioAnalyser,
Color,
Mesh,
MeshToonMaterial,
SphereBufferGeometry,
Vector3,
} from "three";
import { calculateSoundAverages } from "../audio";
export class Planets {
meshes: Array<Planet>;
analyser: AudioAnalyser;
constructor(analyser: AudioAnalyser) {
this.meshes = this.initializePlanets();
this.analyser = analyser;
}
initializePlanets() {
const radius = 2;
const geometry = new SphereBufferGeometry(0.2);
const material = new MeshToonMaterial({
color: new Color(0, 0.3, 0),
wireframe: true,
});
return [...Array(8)].map((_, idx) => {
const sphere = <Planet>new Mesh(geometry, material);
const x = Math.sin((idx / 4) * Math.PI) * radius;
const z = Math.cos((idx / 4) * Math.PI) * radius;
sphere.position.set(x + 0.25, -1, z + 0.1);
sphere.originalPosition = sphere.position.clone();
sphere.offsetY = 0;
sphere.angleSpeed = 2;
sphere.angle = 0;
return sphere;
});
}
updatePlanets(delta: number) {
const sounds = calculateSoundAverages(this.analyser, 12);
const radius = 2;
this.meshes.forEach((planet, idx) => {
planet.offsetY = (sounds[idx] / 255) * 500 * (idx + 1);
planet.position.setX(
Math.cos(planet.angle + (idx / 4) * Math.PI) * radius + 0.25
);
planet.position.setY(
planet.originalPosition.y + Math.max(planet.offsetY, -0.05)
);
planet.position.setZ(
Math.sin(planet.angle + (idx / 4) * Math.PI) * radius + 0.1
);
planet.angle += delta * planet.angleSpeed;
});
}
}
export interface Planet extends Mesh {
geometry: SphereBufferGeometry;
material: MeshToonMaterial;
originalPosition?: Vector3;
offsetY?: number;
angleSpeed?: number;
angle?: number;
}

View File

@ -1,22 +1,31 @@
import { Audio, AudioAnalyser, AudioListener, AudioLoader } from "three"; import {
import { SoundAverages } from "./types"; Audio,
import { avg } from "./utils"; AudioAnalyser,
AudioListener,
AudioLoader,
PositionalAudio,
} from "three";
import { avg, chunk } from "./utils";
const loader = new AudioLoader(); const loader = new AudioLoader();
export function LoadAudio( export function LoadAudio(
listener: AudioListener, listener: AudioListener,
url: string, url: string,
volume: number volume: number,
): Promise<Audio> { type: string,
positional?: boolean
): Promise<PositionalAudio | Audio> {
const loadingDiv = document.getElementById("loader"); const loadingDiv = document.getElementById("loader");
loadingDiv.innerHTML = "Loading audio: 0%"; loadingDiv.innerHTML = `Loading ${type}: 0%`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
loader.load( loader.load(
url, url,
(audio) => { (audio) => {
const sound = new Audio(listener); const sound = positional
? new PositionalAudio(listener)
: new Audio(listener);
sound.setBuffer(audio); sound.setBuffer(audio);
sound.setLoop(false); sound.setLoop(false);
sound.setVolume(volume); sound.setVolume(volume);
@ -36,51 +45,12 @@ export function LoadAudio(
} }
export function calculateSoundAverages( export function calculateSoundAverages(
audioAnalyser: AudioAnalyser audioAnalyser: AudioAnalyser,
): SoundAverages { chunkAmount: number
): number[] {
const soundArray = audioAnalyser.getFrequencyData(); const soundArray = audioAnalyser.getFrequencyData();
const soundAvg = avg(soundArray) / soundArray.length; const chunkedArray = chunk(soundArray, chunkAmount);
const averages = chunkedArray.map((arr) => avg(arr) / 255);
const lowArray = soundArray.slice(0, soundArray.length / 5 - 1); return averages;
const lowMidArray = soundArray.slice(
soundArray.length / 5 - 1,
2 * (soundArray.length / 5) - 1
);
const midArray = soundArray.slice(
(2 * soundArray.length) / 5 - 1,
3 * (soundArray.length / 5) - 1
);
const highMidArray = soundArray.slice(
(3 * soundArray.length) / 5 - 1,
4 * (soundArray.length / 5) - 1
);
const highArray = soundArray.slice(
4 * (soundArray.length / 5) - 1,
soundArray.length - 1
);
const lowAvg = avg(lowArray) / lowArray.length;
const lowMidAvg = avg(lowMidArray) / lowMidArray.length;
const midAvg = avg(midArray) / midArray.length;
const highMidAvg = avg(highMidArray) / highMidArray.length;
const highAvg = avg(highArray) / highArray.length;
document.getElementById("debug").innerHTML = `lows: ${lowAvg.toFixed(
2
)}, low mids: ${lowMidAvg.toFixed(2)}, mids: ${midAvg.toFixed(
2
)}, high mids: ${highMidAvg.toFixed(2)}, highs: ${highAvg.toFixed(2)}`;
return {
soundAvg,
lowAvg,
lowMidAvg,
midAvg,
highMidAvg,
highAvg,
};
} }

View File

@ -1,63 +1,42 @@
import { import {
Scene, Scene,
PerspectiveCamera,
WebGLRenderer, WebGLRenderer,
PointLight, PointLight,
Float32BufferAttribute, Float32BufferAttribute,
AudioListener, AudioListener,
AudioAnalyser, AudioAnalyser,
Clock, Clock,
Mesh,
MeshBasicMaterial,
MathUtils,
BufferGeometry, BufferGeometry,
Points, Points,
SphereGeometry, Color,
PerspectiveCamera,
} from "three"; } from "three";
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader"; import { GLTF } from "three/examples/jsm/loaders/GLTFLoader";
import { calculateSoundAverages, LoadAudio } from "./audio";
import { LoadModel } from "./model"; import { LoadModel } from "./model";
import { SETTINGS } from "./settings";
import { CORE, DISTORTION, DistortionMesh, SoundAverages, SRT } from "./types";
import { prng_alea } from "esm-seedrandom"; import { prng_alea } from "esm-seedrandom";
import parseSRT from "parse-srt"; import parseSRT from "parse-srt";
import { Planets } from "./actors/Planets";
import { OrbitCamera } from "./actors/OrbitCamera";
import { Hand } from "./actors/Hand";
import { OrbitLight } from "./actors/OrbitLight";
import { Jukebox } from "./actors/Jukebox";
const CORE: CORE = { const CORE: CORE = {
renderer: null, renderer: null,
scene: null, scene: null,
camera: null,
clock: null, clock: null,
rng: prng_alea( rng: prng_alea(
"Polka-Jovial-Makeover-Wieldable-Spirited-Footprint-Recall-Handpick-Sacrifice-Jester" "Polka-Jovial-Makeover-Wieldable-Spirited-Footprint-Recall-Handpick-Sacrifice-Jester"
), ),
avgs: null,
subtitleDiv: document.getElementById("subtitles"),
parser: parseSRT,
srt: null,
elapsedTime: 0,
playing: false, playing: false,
};
const DISTORTION: DISTORTION = {
light: null,
hand: null,
positions: null,
audioListener: null, audioListener: null,
audioAnalyser: null,
}; };
const ROTATION = { let ORBIT_CAMERA: OrbitCamera;
cam: { let ORBIT_LIGHT: OrbitLight;
angle: 0, let HAND: Hand;
angularSpeed: MathUtils.degToRad(20), let PLANETS: Planets;
radius: 5, let JUKEBOX: Jukebox;
},
light: {
angle: 30,
angularSpeed: MathUtils.degToRad(40),
radius: 2,
},
};
init().then(() => animate()); init().then(() => animate());
@ -65,90 +44,51 @@ async function init() {
const container = document.getElementById("container"); const container = document.getElementById("container");
CORE.scene = new Scene(); CORE.scene = new Scene();
CORE.camera = new PerspectiveCamera( ORBIT_CAMERA = new OrbitCamera(
75, new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000)
window.innerWidth / window.innerHeight,
1,
10000
); );
CORE.scene.add(CORE.camera); CORE.scene.add(ORBIT_CAMERA.camera);
CORE.audioListener = new AudioListener();
ORBIT_CAMERA.camera.add(CORE.audioListener);
DISTORTION.light = new PointLight(0x119911, 1); ORBIT_LIGHT = new OrbitLight(new PointLight(new Color(1, 1, 1), 2, 10));
DISTORTION.light.counter = 0; CORE.scene.add(ORBIT_LIGHT.light);
const geometry = new SphereGeometry(0.2);
const material = new MeshBasicMaterial({ color: 0xcccc00 });
const sphere = new Mesh(geometry, material);
DISTORTION.light.add(sphere);
CORE.scene.add(DISTORTION.light);
CORE.renderer = new WebGLRenderer(); CORE.renderer = new WebGLRenderer();
CORE.renderer.setPixelRatio(window.devicePixelRatio); CORE.renderer.setPixelRatio(window.devicePixelRatio);
CORE.renderer.setSize(window.innerWidth, window.innerHeight); CORE.renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(CORE.renderer.domElement); container.appendChild(CORE.renderer.domElement);
window.addEventListener("resize", onWindowResize); window.addEventListener("resize", onWindowResize);
DISTORTION.audioListener = new AudioListener();
CORE.scene.add(DISTORTION.audioListener);
CORE.clock = new Clock(); CORE.clock = new Clock();
try { try {
JUKEBOX = new Jukebox(parseSRT, CORE.audioListener);
const { music, musicAnalyser, poem, poemAnalyser } =
await JUKEBOX.initializeAudio(() => {
CORE.playing = true;
});
CORE.scene.add(poem);
ORBIT_LIGHT.light.add(music);
const model = await LoadModel(); const model = await LoadModel();
initializeModel(model); initializeModel(model, poemAnalyser);
updateRotations(0);
initializeStars(); initializeStars();
ORBIT_CAMERA.update(0, HAND.mesh.position);
const audio = await LoadAudio( PLANETS = new Planets(musicAnalyser);
DISTORTION.audioListener, PLANETS.meshes.forEach((planet) => CORE.scene.add(planet));
"/static/who_am_i_with_music.mp3",
1
);
CORE.srt = await loadSRT();
DISTORTION.audioAnalyser = new AudioAnalyser(audio, 512);
const startButton = document.getElementById("startButton");
startButton.style.display = "block";
startButton.addEventListener("click", () => {
audio.play();
CORE.playing = true;
startButton.remove();
});
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);
} }
} }
async function loadSRT(): Promise<SRT[]> { function initializeModel(model: GLTF, analyser: AudioAnalyser) {
const response = await fetch("static/who_am_i_with_music.srt");
const responseText = await response.text();
return CORE.parser(responseText);
}
function initializeModel(model: GLTF) {
// remove second hand with text above it // remove second hand with text above it
const objToRemove = model.scene.getObjectByName("Object_3"); const objToRemove = model.scene.getObjectByName("Object_3");
objToRemove.parent.remove(objToRemove); objToRemove.parent.remove(objToRemove);
// turn remaining hand into wireframe HAND = new Hand(model.scene.getObjectByName("Object_4"));
DISTORTION.hand = <DistortionMesh>model.scene.getObjectByName("Object_4"); HAND.initialize(CORE.rng, analyser);
DISTORTION.hand.material.wireframe = true;
// set up distortion for each vertex
DISTORTION.hand.originalPositions =
DISTORTION.hand.geometry.getAttribute("position");
DISTORTION.hand.distortions = (
DISTORTION.hand.originalPositions.array as Array<number>
)
.slice(0)
.map(() => CORE.rng() * 2 - 1);
DISTORTION.positions = DISTORTION.hand.geometry.getAttribute("position");
DISTORTION.hand.parent.position.x = DISTORTION.hand.parent.position.x + 2.8;
DISTORTION.hand.parent.position.y = DISTORTION.hand.parent.position.y + 0;
DISTORTION.hand.parent.position.z = DISTORTION.hand.parent.position.z + 2.2;
DISTORTION.hand.parent.rotateZ(Math.PI / 2);
DISTORTION.hand.geometry.center();
CORE.scene.add(model.scene); CORE.scene.add(model.scene);
} }
@ -172,76 +112,37 @@ function animate() {
requestAnimationFrame(animate); requestAnimationFrame(animate);
const delta = CORE.clock.getDelta(); const delta = CORE.clock.getDelta();
CORE.avgs = calculateSoundAverages(DISTORTION.audioAnalyser); render(delta);
render(delta, CORE.avgs);
} }
function render(delta: number, soundAvg: SoundAverages) { function render(delta: number) {
// modulate light intensity
DISTORTION.light.counter += delta + SETTINGS.lightSpeed;
DISTORTION.light.intensity =
Math.sin(DISTORTION.light.counter) / 2 + SETTINGS.lightMin;
const sound = Math.pow(
soundAvg.soundAvg * SETTINGS.distortionMax,
SETTINGS.distortionPower
);
if (CORE.playing) { if (CORE.playing) {
updateRotations(delta); ORBIT_LIGHT.update(delta);
updateSubtitles(delta); HAND.update();
JUKEBOX.update(delta);
PLANETS.updatePlanets(delta);
} }
const newPositions = new Float32BufferAttribute(
(DISTORTION.positions.array as Array<number>).map((_position, index) => {
const distortion = DISTORTION.hand.distortions[index] * sound;
return distortion / 10 + DISTORTION.hand.originalPositions.array[index];
}),
3
);
DISTORTION.hand.geometry.setAttribute("position", newPositions); CORE.renderer.render(CORE.scene, ORBIT_CAMERA.camera);
CORE.camera.lookAt(DISTORTION.hand.position); ORBIT_CAMERA.update(delta, HAND.mesh.position);
CORE.renderer.render(CORE.scene, CORE.camera);
}
function updateRotations(delta: number) {
DISTORTION.hand.parent.rotateZ(3 / 1000);
CORE.camera.position.x = Math.sin(ROTATION.cam.angle) * ROTATION.cam.radius;
CORE.camera.position.z = Math.cos(ROTATION.cam.angle) * ROTATION.cam.radius;
CORE.camera.position.y = Math.cos(ROTATION.cam.angle) * 1.5;
ROTATION.cam.angle += ROTATION.cam.angularSpeed * delta;
DISTORTION.light.position.x =
Math.cos(ROTATION.light.angle) * ROTATION.light.radius;
DISTORTION.light.position.z =
Math.sin(ROTATION.light.angle) * ROTATION.light.radius;
DISTORTION.light.position.y = Math.cos(ROTATION.light.angle) * 5;
ROTATION.light.angle += ROTATION.light.angularSpeed * delta;
}
function updateSubtitles(delta: number) {
CORE.elapsedTime += delta;
let found = false;
CORE.srt.forEach((srt) => {
if (CORE.elapsedTime > srt.start && CORE.elapsedTime < srt.end) {
found = true;
CORE.subtitleDiv.innerHTML = srt.text;
}
});
if (!found) CORE.subtitleDiv.innerHTML = "";
if (CORE.elapsedTime > 70) {
document.getElementById("credits").style.display = "block";
}
} }
function onWindowResize() { function onWindowResize() {
CORE.camera.aspect = window.innerWidth / window.innerHeight; ORBIT_CAMERA.camera.aspect = window.innerWidth / window.innerHeight;
CORE.camera.updateProjectionMatrix(); ORBIT_CAMERA.camera.updateProjectionMatrix();
CORE.renderer.setSize(window.innerWidth, window.innerHeight); CORE.renderer.setSize(window.innerWidth, window.innerHeight);
} }
interface CORE {
renderer: WebGLRenderer;
scene: Scene;
clock: Clock;
rng: RandomNumberGenerator;
playing: boolean;
audioListener: AudioListener;
}
export type RandomNumberGenerator = {
(): number;
};

View File

@ -1,13 +1,18 @@
import { Texture, TextureLoader } from "three";
import { GLTFLoader, GLTF } from "three/examples/jsm/loaders/GLTFLoader.js"; import { GLTFLoader, GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
const loader = new GLTFLoader(); const gltfLoader = new GLTFLoader();
const textureLoader = new TextureLoader();
export function LoadModel(): Promise<GLTF> { export function LoadModel(): Promise<GLTF> {
const loadingDiv = document.getElementById("loader"); const loadingDiv = document.getElementById("loader");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
loader.load( gltfLoader.load(
"/static/scene.gltf", "/static/models/scene.gltf",
(gltf: GLTF) => resolve(gltf), (gltf: GLTF) => {
loadingDiv.innerHTML = "";
resolve(gltf);
},
(progress) => (progress) =>
(loadingDiv.innerHTML = `Loading model: ${ (loadingDiv.innerHTML = `Loading model: ${
(progress.loaded / progress.total) * 100 (progress.loaded / progress.total) * 100
@ -19,3 +24,21 @@ export function LoadModel(): Promise<GLTF> {
); );
}); });
} }
export function LoadTexture(url): Promise<Texture> {
const loadingDiv = document.getElementById("loader");
return new Promise((resolve, reject) => {
textureLoader.load(
url,
(texture: Texture) => resolve(texture),
(progress) =>
(loadingDiv.innerHTML = `Loading texture: ${
(progress.loaded / progress.total) * 100
}%`),
(error: ErrorEvent) => {
console.log(error.target);
reject(error);
}
);
});
}

View File

@ -1,6 +0,0 @@
export const SETTINGS = {
lightSpeed: 0.02,
lightMin: 1.25,
distortionMax: 4,
distortionPower: 3,
};

View File

@ -1,69 +0,0 @@
import {
AudioAnalyser,
AudioListener,
BufferAttribute,
Clock,
InterleavedBufferAttribute,
Mesh,
MeshBasicMaterial,
PerspectiveCamera,
PointLight,
Scene,
WebGLRenderer,
} from "three";
export interface CORE {
renderer: WebGLRenderer;
scene: Scene;
camera: PerspectiveCamera;
clock: Clock;
rng: RandomNumberGenerator;
avgs: SoundAverages;
subtitleDiv: HTMLElement;
parser: SRTParser;
srt: SRT[];
elapsedTime: number;
playing: boolean;
}
export interface DISTORTION {
light: CountingLight;
hand: DistortionMesh;
positions: BufferAttribute | InterleavedBufferAttribute;
audioListener: AudioListener;
audioAnalyser: AudioAnalyser;
}
export interface CountingLight extends PointLight {
counter?: number;
}
export interface DistortionMesh extends Mesh {
material: MeshBasicMaterial;
originalPositions: BufferAttribute | InterleavedBufferAttribute;
distortions: Array<number>;
}
export type SoundAverages = {
soundAvg: number;
lowAvg: number;
lowMidAvg: number;
midAvg: number;
highMidAvg: number;
highAvg: number;
};
type RandomNumberGenerator = {
(): number;
};
type SRTParser = {
(srt: string): SRT[];
};
export type SRT = {
id: number;
start: number;
end: number;
text: string;
};

View File

@ -1,3 +1,14 @@
export function avg(list: Uint8Array) { export function avg(list: Uint8Array) {
return list.reduce((prev, curr) => prev + curr) / list.length; return list.reduce((prev, curr) => prev + curr) / list.length;
} }
export function chunk(arr: [] | Uint8Array, len: number) {
const chunks = [];
const n = arr.length;
let i = 0;
while (i < n) {
chunks.push(arr.slice(i, (i += len)));
}
return chunks;
}

BIN
static/music/silence.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
static/music/who_am_i.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.