From 64f7bc444288a98410da299b6aa7b1e62b9a3110 Mon Sep 17 00:00:00 2001 From: Aria Date: Tue, 19 Aug 2025 09:12:29 -0500 Subject: [PATCH 1/3] Working version of duck on iOS safari --- packages/superdough/helpers.mjs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index c09acf1a5..1217b5163 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -211,7 +211,14 @@ export function getVibratoOscillator(param, value, t) { export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { const constantNode = new ConstantSourceNode(audioContext); + // Safari requires audio nodes to be connected in order for their onended events + // to fire, so we _mute it_ and then connect it to the destination + const zeroGain = gainNode(0); + zeroGain.connect(audioContext.destination); + constantNode.connect(zeroGain); constantNode.start(startTime); + + // Schedule the `onComplete` callback to occur at `stopTime` constantNode.stop(stopTime); constantNode.onended = () => { onComplete(); From 659071a4ee6b7a67f0e1a5c46a10c0f854bd3ffa Mon Sep 17 00:00:00 2001 From: Aria Date: Tue, 19 Aug 2025 16:18:48 -0500 Subject: [PATCH 2/3] Ensure zeroGain is cleaned up after use; move start/stop to after callback assigned for safety --- packages/superdough/helpers.mjs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 1217b5163..53922aa2c 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -211,18 +211,25 @@ export function getVibratoOscillator(param, value, t) { export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { const constantNode = new ConstantSourceNode(audioContext); - // Safari requires audio nodes to be connected in order for their onended events + // Certain browsers requires audio nodes to be connected in order for their onended events // to fire, so we _mute it_ and then connect it to the destination const zeroGain = gainNode(0); zeroGain.connect(audioContext.destination); constantNode.connect(zeroGain); - constantNode.start(startTime); // Schedule the `onComplete` callback to occur at `stopTime` - constantNode.stop(stopTime); constantNode.onended = () => { + // Ensure garbage collection + try { + zeroGain.disconnect(); + } catch {} + try { + constantNode.disconnect(); + } catch {} onComplete(); }; + constantNode.start(startTime); + constantNode.stop(stopTime); return constantNode; } const mod = (freq, range = 1, type = 'sine') => { From d64a0ef0eb8d79d90ff8fb51dce0ef47631f6d1b Mon Sep 17 00:00:00 2001 From: Aria Date: Tue, 19 Aug 2025 16:21:00 -0500 Subject: [PATCH 3/3] Lint requires no empty blocks: pass comment --- packages/superdough/helpers.mjs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 53922aa2c..81bec9399 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -222,10 +222,14 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { // Ensure garbage collection try { zeroGain.disconnect(); - } catch {} + } catch { + // pass + } try { constantNode.disconnect(); - } catch {} + } catch { + // pass + } onComplete(); }; constantNode.start(startTime);