From e2d48bdc1cc438f6f4374be738b92be714b2a1e4 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 27 Apr 2024 11:09:30 +0200 Subject: [PATCH] WaveEffect --- editor/index.js | 4 +- editor/visualizers/effects/DataEffect.js | 2 +- editor/visualizers/effects/WaveEffect.js | 65 +++++++++++++++--------- src/ByteBeatNode.js | 21 +++++--- 4 files changed, 58 insertions(+), 34 deletions(-) diff --git a/editor/index.js b/editor/index.js index 80bc69f..dc7b70f 100644 --- a/editor/index.js +++ b/editor/index.js @@ -14,7 +14,7 @@ import DataEffect from './visualizers/effects/DataEffect.js'; import FFTEffect from './visualizers/effects/FFTEffect.js'; //import SampleEffect from './visualizers/effects/SampleEffect.js'; import VSAEffect from './visualizers/effects/VSAEffect.js'; -//import WaveEffect from './visualizers/effects/WaveEffect.js'; +import WaveEffect from './visualizers/effects/WaveEffect.js'; import songList from './songList.js'; @@ -254,7 +254,7 @@ async function main() { const effects = [ new DataEffect(gl), // ...(showSample ? [new SampleEffect(gl)] : []), - //new WaveEffect(gl), + new WaveEffect(gl), new FFTEffect(gl), ]; g_visualizers = [ diff --git a/editor/visualizers/effects/DataEffect.js b/editor/visualizers/effects/DataEffect.js index 7c3ef0c..6092192 100644 --- a/editor/visualizers/effects/DataEffect.js +++ b/editor/visualizers/effects/DataEffect.js @@ -87,7 +87,7 @@ export default class DataEffect { const numChannels = byteBeat.getNumChannels(); const dataP = []; for (let channel = 0; channel < numChannels; ++channel) { - dataP.push(byteBeat.getSamplesForTimeRange(start, start + kChunkSize, 1, this.dataContext, this.dataStack, channel)); + dataP.push(byteBeat.getSamplesForTimeRange(start, start + kChunkSize, kChunkSize, this.dataContext, this.dataStack, channel)); } const data = await Promise.all(dataP); const chunkId = start / kChunkSize; diff --git a/editor/visualizers/effects/WaveEffect.js b/editor/visualizers/effects/WaveEffect.js index 2130010..1647139 100644 --- a/editor/visualizers/effects/WaveEffect.js +++ b/editor/visualizers/effects/WaveEffect.js @@ -1,5 +1,4 @@ import * as twgl from '../../../js/twgl-full.module.js'; -import ByteBeatNode from '../../../src/ByteBeatNode.js'; import { drawEffect } from './effect-utils.js'; const colorRed = new Float32Array([1, 0, 0, 1]); @@ -44,13 +43,11 @@ export default class WaveEffect { this.position = 0; } resize(gl) { - this.beatContext = ByteBeatNode.createContext(); - this.beatStack = ByteBeatNode.createStack(); - const width = gl.drawingBufferWidth; const lineHeight = new Float32Array(width); const column = new Float32Array(width); + this.state = 'init'; this.width = width; this.position = 0; this.then = performance.now(); @@ -80,8 +77,46 @@ export default class WaveEffect { this.bufferInfoR.numElements = width; } } + async #update(gl, byteBeat, elapsedTimeMS) { + if (this.state === 'init') { + this.state = 'initializing'; + if (this.beatContext) { + byteBeat.destroyContext(this.beatContext); + byteBeat.destroyStack(this.beatStack); + } + this.beatContext = await byteBeat.createContext(); + this.beatStack = await byteBeat.createStack(); + this.state = 'running'; + } + if (this.state === 'running') { + this.state = 'waiting'; + const {bufferInfoL, bufferInfoR, beatContext: context, beatStack: stack} = this; + const numChannels = byteBeat.getNumChannels(); + const startTime = this.position; + const endTime = startTime + elapsedTimeMS * 0.001 * byteBeat.getDesiredSampleRate() | 0; + this.position = endTime; + const dataP = []; + for (let channel = 0; channel < numChannels; ++channel) { + dataP.push(byteBeat.getSamplesForTimeRange( + startTime, + endTime, + this.lineHeightL.length, + context, + stack, + channel, + )); + } + const data = await Promise.all(dataP); + for (let channel = 0; channel < numChannels; ++channel) { + const bufferInfo = channel ? bufferInfoR : bufferInfoL; + gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.height.buffer); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, data[channel].subarray(0, this.lineHeightL.length)); + } + this.state = 'running'; + } + } render(gl, commonUniforms, byteBeat) { - const {uniforms, programInfo, bufferInfoL, bufferInfoR, beatContext: context, beatStack: stack} = this; + const {uniforms, programInfo, bufferInfoL, bufferInfoR} = this; const numChannels = byteBeat.getNumChannels(); const targetTimeMS = 1000 / (48000 / 4096); @@ -90,24 +125,8 @@ export default class WaveEffect { const run = elapsedTimeMS >= targetTimeMS; if (run) { this.then = now; - if (byteBeat.isRunning() ) { - const startTime = this.position; - const endTime = startTime + elapsedTimeMS * 0.001 * byteBeat.getDesiredSampleRate() | 0; - const duration = (endTime - startTime); - - this.position = endTime; - for (let channel = 0; channel < numChannels; ++channel) { - const bufferInfo = channel ? bufferInfoR : bufferInfoL; - - const dst = channel ? this.lineHeightR : this.lineHeightL; - for (let i = 0; i < dst.length; ++i) { - const position = startTime + i * duration / dst.length | 0; - dst[i] = byteBeat.getSampleForTime(position, context, stack, channel); - } - - gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.height.buffer); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, dst); - } + if (byteBeat.isRunning()) { + this.#update(gl, byteBeat, elapsedTimeMS); } } diff --git a/src/ByteBeatNode.js b/src/ByteBeatNode.js index 25ea232..9c59344 100644 --- a/src/ByteBeatNode.js +++ b/src/ByteBeatNode.js @@ -62,8 +62,13 @@ class BeatWorkletProcessor extends AudioWorkletProcessor { const transferables = []; try { result = this[fn].call(this, ...args); - if (result instanceof Float32Array) { - //transferables.push(result); + if (result && result.length) { + for (let i = 0; i < result.length; ++i) { + const o = result[i]; + if (o instanceof Float32Array) { + transferables.push(o); + } + } } } catch (e) { error = e; @@ -166,14 +171,14 @@ class BeatWorkletProcessor extends AudioWorkletProcessor { this.#deregisterObj(id); } - getSamplesForTimeRange(start, end, step, contextId, stackId, channel = 0) { + getSamplesForTimeRange(start, end, numSamples, contextId, stackId, channel = 0) { const context = this.idToObj.get(contextId); const stack = this.idToObj.get(stackId); - const len = Math.ceil((end - start) / step); - const data = new Float32Array(len); - let cursor = 0; - for (let time = start; time < end; time += step) { - data[cursor++] = this.byteBeat.getSampleForTime(time, context, stack, channel); + const data = new Float32Array(numSamples); + const duration = end - start; + for (let i = 0; i < numSamples; ++i) { + const time = start + duration * i / numSamples | 0; + data[i] = this.byteBeat.getSampleForTime(time, context, stack, channel); } return data; }