Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 47 additions & 70 deletions targets/wasm/js/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,36 +225,9 @@ function updateBypassUi() {


const realtimeState = {
isOfflineProcessing: false,
ptrInL: 0,
ptrInR: 0,
ptrOutL: 0,
ptrOutR: 0,
blockSize: 0
isOfflineProcessing: false
};

function ensureRealtimeBuffers(blockSize) {
if (!module) {
return;
}

if (realtimeState.blockSize >= blockSize && realtimeState.ptrInL && realtimeState.ptrInR && realtimeState.ptrOutL && realtimeState.ptrOutR) {
return;
}

if (realtimeState.ptrInL) module._free(realtimeState.ptrInL);
if (realtimeState.ptrInR) module._free(realtimeState.ptrInR);
if (realtimeState.ptrOutL) module._free(realtimeState.ptrOutL);
if (realtimeState.ptrOutR) module._free(realtimeState.ptrOutR);

const bytes = blockSize * 4; // Float32Array.BYTES_PER_ELEMENT
realtimeState.ptrInL = module._malloc(bytes);
realtimeState.ptrInR = module._malloc(bytes);
realtimeState.ptrOutL = module._malloc(bytes);
realtimeState.ptrOutR = module._malloc(bytes);
realtimeState.blockSize = blockSize;
}

async function ensurePreviewGraph() {
if (previewGraph.audioCtx) return;
const audioCtx = new AudioContext({ sampleRate: DEFAULT_UI_SAMPLE_RATE });
Expand All @@ -267,63 +240,56 @@ async function ensurePreviewGraph() {
dryGain.gain.value = 0;
wetGain.gain.value = 1;

const scriptNode = audioCtx.createScriptProcessor(BLOCK_SIZE, 2, 2);

ensureRealtimeBuffers(BLOCK_SIZE);

if (api) {
api.reset(uiSampleRate);
applyParams();
try {
await audioCtx.audioWorklet.addModule('./worklet-processor.js');
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error('Falha ao carregar módulo AudioWorklet.', err);
els.status.textContent = `Erro no módulo AudioWorklet: ${message}`;
throw err;
}

scriptNode.onaudioprocess = (e) => {
const inputBuffer = e.inputBuffer;
const outputBuffer = e.outputBuffer;
let orbitNode;
try {
orbitNode = new AudioWorkletNode(audioCtx, 'orbit-delay-processor');
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error('Falha ao criar AudioWorkletNode orbit-delay-processor.', err);
els.status.textContent = `Erro ao criar AudioWorkletNode: ${message}`;
throw err;
}

if (realtimeState.isOfflineProcessing || !api) {
for (let channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
const inputData = inputBuffer.getChannelData(channel);
const outputData = outputBuffer.getChannelData(channel);
outputData.set(inputData);
}
orbitNode.port.onmessage = (event) => {
if (event.data?.type === 'orbit_diag') {
console.debug('[orbit_diag]', event.data);
return;
}

const numSamples = inputBuffer.length;
if (numSamples <= 0) {
return;
}
ensureRealtimeBuffers(numSamples);
if (!realtimeState.ptrInL || !realtimeState.ptrInR || !realtimeState.ptrOutL || !realtimeState.ptrOutR) {
if (event.data?.type === 'ready') {
console.debug('[orbit-worklet] WASM pronto.');
return;
}

const processLen = numSamples;

const inL = inputBuffer.getChannelData(0);
const inR = inputBuffer.numberOfChannels > 1 ? inputBuffer.getChannelData(1) : inL;
const outL = outputBuffer.getChannelData(0);
const outR = outputBuffer.numberOfChannels > 1 ? outputBuffer.getChannelData(1) : outL;

module.HEAPF32.set(inL.subarray(0, processLen), realtimeState.ptrInL >> 2);
module.HEAPF32.set(inR.subarray(0, processLen), realtimeState.ptrInR >> 2);

api.process(realtimeState.ptrInL, realtimeState.ptrInR, realtimeState.ptrOutL, realtimeState.ptrOutR, processLen);

outL.set(module.HEAPF32.subarray(realtimeState.ptrOutL >> 2, (realtimeState.ptrOutL >> 2) + processLen));
if (outputBuffer.numberOfChannels > 1) {
outR.set(module.HEAPF32.subarray(realtimeState.ptrOutR >> 2, (realtimeState.ptrOutR >> 2) + processLen));
} };
if (event.data?.type === 'error') {
const message = event.data?.message || 'Erro desconhecido no worklet.';
console.error('[orbit-worklet:error]', event.data);
els.status.textContent = `Erro no worklet: ${message}`;
}
};

drySource.connect(dryGain).connect(audioCtx.destination);
drySource.connect(scriptNode).connect(wetGain).connect(audioCtx.destination);
drySource.connect(orbitNode).connect(wetGain).connect(audioCtx.destination);

previewGraph.audioCtx = audioCtx;
previewGraph.dryElement = els.preview;

previewGraph.dryGain = dryGain;
previewGraph.wetGain = wetGain;
previewGraph.scriptNode = scriptNode;
previewGraph.orbitNode = orbitNode;

if (api) {
api.reset(uiSampleRate);
applyParams();
}
}


Expand Down Expand Up @@ -912,12 +878,23 @@ async function decodeToStereoFloat(file) {
}

function setParam(key) {
if (!api) return;
const el = els[key];
const converter = paramConverters[key];
if (!el || !converter) return;

const value = converter(el.value);

if (previewGraph.orbitNode && previewGraph.audioCtx && !realtimeState.isOfflineProcessing) {
const param = previewGraph.orbitNode.parameters.get(key);
if (param) {
param.setValueAtTime(value, previewGraph.audioCtx.currentTime);
} else {
console.warn(`Parâmetro "${key}" não encontrado no AudioWorkletNode.`);
}
return;
}
Comment on lines +887 to +895

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Há uma incompatibilidade entre os parâmetros suportados pelo OrbitDelayProcessor (definidos em worklet-processor.js) e os parâmetros enviados por setParam.

Os parâmetros shimmerMode, feedbackTapMixerEnabled e feedbackTapMixerAmount estão presentes em PARAM_KEYS e são atualizados via UI, mas não estão declarados em parameterDescriptors nem são tratados em applyParams dentro do worklet-processor.js.

Como a função setParam retorna imediatamente quando previewGraph.orbitNode está ativo (mesmo se o parâmetro for undefined), essas configurações serão silenciosamente ignoradas e nunca serão aplicadas ao áudio em tempo real.

Para corrigir isso, você precisa:

  1. Adicionar esses parâmetros ao parameterDescriptors e ao método applyParams no arquivo targets/wasm/js/worklet-processor.js.
  2. Adicionar um aviso no console caso um parâmetro não seja encontrado no nó do Worklet para facilitar a depuração.
Suggested change
if (previewGraph.orbitNode && previewGraph.audioCtx && !realtimeState.isOfflineProcessing) {
const param = previewGraph.orbitNode.parameters.get(key);
if (param) {
param.setValueAtTime(value, previewGraph.audioCtx.currentTime);
}
return;
}
if (previewGraph.orbitNode && previewGraph.audioCtx && !realtimeState.isOfflineProcessing) {
const param = previewGraph.orbitNode.parameters.get(key);
if (param) {
param.setValueAtTime(value, previewGraph.audioCtx.currentTime);
} else {
console.warn(`Parâmetro "${key}" não encontrado no AudioWorkletNode.`);
}
return;
}


if (!api) return;
switch (key) {
case 'orbit': api.setOrbit(value); break;
case 'offsetSamples': api.setOffsetSamples(value); break;
Expand Down
11 changes: 10 additions & 1 deletion targets/wasm/js/worklet-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ class OrbitDelayProcessor extends AudioWorkletProcessor {
{ name: 'toneHz', defaultValue: 6500.0, minValue: 20.0, maxValue: 20000.0 },
{ name: 'smearAmount', defaultValue: 0.25, minValue: 0.0, maxValue: 1.0 },
{ name: 'diffuserStages', defaultValue: 4.0, minValue: 0.0, maxValue: 8.0 },
{ name: 'dcBlockEnabled', defaultValue: 1.0, minValue: 0.0, maxValue: 1.0 }
{ name: 'dcBlockEnabled', defaultValue: 1.0, minValue: 0.0, maxValue: 1.0 },
{ name: 'shimmerMode', defaultValue: 0.0, minValue: 0.0, maxValue: 1.0 },
{ name: 'feedbackTapMixerEnabled', defaultValue: 0.0, minValue: 0.0, maxValue: 1.0 },
{ name: 'feedbackTapMixerAmount', defaultValue: 0.0, minValue: 0.0, maxValue: 1.0 }
];
}

Expand Down Expand Up @@ -135,6 +138,9 @@ class OrbitDelayProcessor extends AudioWorkletProcessor {
set_diffuser_stages: this.module.cwrap('orbit_wasm_set_diffuser_stages', 'number', ['number']),
set_dc_block_enabled: this.module.cwrap('orbit_wasm_set_dc_block_enabled', 'number', ['number']),
set_read_mode: this.module.cwrap('orbit_wasm_set_read_mode', 'number', ['number']),
set_shimmer_mode: this.module.cwrap('orbit_wasm_set_shimmer_mode', 'number', ['number']),
set_feedback_tap_mixer_enabled: this.module.cwrap('orbit_wasm_set_feedback_tap_mixer_enabled', 'number', ['number']),
set_feedback_tap_mixer_amount: this.module.cwrap('orbit_wasm_set_feedback_tap_mixer_amount', 'number', ['number']),
get_diag: this.module.cwrap('orbit_wasm_get_last_block_diagnostics', 'number', ['number','number','number','number','number','number','number','number','number','number'])
};

Expand Down Expand Up @@ -182,6 +188,9 @@ class OrbitDelayProcessor extends AudioWorkletProcessor {
this.api.set_diffuser_stages(Math.round(at('diffuserStages')));
this.api.set_dc_block_enabled(at('dcBlockEnabled') >= 0.5 ? 1 : 0);
this.api.set_read_mode(Math.round(at('readMode')));
this.api.set_shimmer_mode(Math.round(at('shimmerMode')));
this.api.set_feedback_tap_mixer_enabled(at('feedbackTapMixerEnabled') >= 0.5 ? 1 : 0);
this.api.set_feedback_tap_mixer_amount(at('feedbackTapMixerAmount'));
}

process(inputs, outputs, parameters) {
Expand Down
Loading