diff --git a/CHANGELOG.md b/CHANGELOG.md index 733e7321..a234f823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## v1.0.8 - 2026-02-14 + +- Fix: AudioRenderCapacity.stop cf. #167 +- Fix: More robust debug build loading +- Docs: Faust WASM examples + +## v1.0.7 - 2025-11-18 ## v1.0.6 - 2025-11-18 - Fix `mjs` exports diff --git a/examples/offline-worklet.js b/examples/offline-worklet.js new file mode 100644 index 00000000..424f22f6 --- /dev/null +++ b/examples/offline-worklet.js @@ -0,0 +1,33 @@ +import { + AudioContext, + AudioBufferSourceNode, + AudioWorkletNode, + OfflineAudioContext, +} from '../index.mjs'; + +import { sleep } from '@ircam/sc-utils'; + +console.log('> Process 1 sec sine piped into "pass-through" worklet'); + +const offline = new OfflineAudioContext(2, 48000, 48000); +await offline.audioWorklet.addModule('./worklets/pass-through.js'); + +const osc = offline.createOscillator(); +const passThrough = new AudioWorkletNode(offline, 'pass-through'); + +osc.connect(passThrough).connect(offline.destination); +osc.start(0); +osc.stop(1); + +const buffer = await offline.startRendering(); + +console.log(`> Playback resulting buffer (duration: ${buffer.duration}sec)`); + +const online = new AudioContext({ sampleRate: 48000 }); +const src = new AudioBufferSourceNode(online, { buffer }); +src.connect(online.destination); +src.start(); + +await sleep(1); + +await online.close(); diff --git a/examples/worklets/pass-through.js b/examples/worklets/pass-through.js new file mode 100644 index 00000000..f6ea2b1a --- /dev/null +++ b/examples/worklets/pass-through.js @@ -0,0 +1,29 @@ +class PassThrough extends AudioWorkletProcessor { + // this parameter is not used, by allows to ensure that the params buffers + // recycling logic works as expected in `examples/offline-worklet.js` + // cf. https://github.com/ircam-ismm/node-web-audio-api/issues/170 + static get parameterDescriptors() { + return [ + { + name: "dummy", + defaultValue: 1, + minValue: 0, + maxValue: 1, + automationRate: "a-rate", + }, + ]; + } + + process(inputs, outputs) { + if (inputs[0] && inputs[0][0] && outputs[0] && outputs[0][0]) { + for (let ch = 0; ch < outputs[0].length; ch++) { + if (inputs[0][ch]) { + outputs[0][ch].set(inputs[0][ch]); + } + } + } + return true; + } +} + +registerProcessor('pass-through', PassThrough); diff --git a/js/lib/events.js b/js/lib/events.js index a99023c8..7306caf5 100644 --- a/js/lib/events.js +++ b/js/lib/events.js @@ -5,6 +5,6 @@ module.exports.propagateEvent = function propagateEvent(eventTarget, event) { if (isFunction(eventTarget[`on${event.type}`])) { eventTarget[`on${event.type}`](event); } - // then distach to add event listeners + // then dispatch to `addEventListener` callbacks eventTarget.dispatchEvent(event); } diff --git a/src/audio_worklet_node.rs b/src/audio_worklet_node.rs index cc98c746..79895269 100644 --- a/src/audio_worklet_node.rs +++ b/src/audio_worklet_node.rs @@ -244,11 +244,21 @@ fn recycle_processor(env: &Env, processor: JsObject) -> Result<()> { let k_worklet_params_cache = env.symbol_for("node-web-audio-api:worklet-params-cache")?; let js_params_cache = processor.get_property::(k_worklet_params_cache)?; - let param_cache_128 = js_params_cache.get_element::(0)?; - let _ = recycle_buffer.call1::(param_cache_128)?; + let js_params_properties = js_params_cache.get_property_names()?; + let len = js_params_properties.get_array_length()?; - let param_cache_1 = js_params_cache.get_element::(1)?; - let _ = recycle_buffer_1.call1::(param_cache_1)?; + for i in 0..len { + let js_property_name: JsString = js_params_properties.get_element(i)?; + let utf8_str = js_property_name.into_utf8()?.into_owned()?; + let property_name = utf8_str.as_str(); + let cache: JsObject = js_params_cache.get_named_property(property_name)?; + + let param_cache_128 = cache.get_element::(0)?; + let _ = recycle_buffer.call1::(param_cache_128)?; + + let param_cache_1 = cache.get_element::(1)?; + let _ = recycle_buffer_1.call1::(param_cache_1)?; + } Ok(()) }