From 4c00805a8e9cd3e58c36256f6501013be709ec2e Mon Sep 17 00:00:00 2001 From: Matt Hillsdon <44397098+microbit-matt-hillsdon@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:09:36 +0000 Subject: [PATCH] Older Safari: Handle speech sample rate errors, reuse audio context (#111) - Workaround for https://github.com/microbit-foundation/micropython-microbit-v2-simulator/issues/110 where we've observed that the sample rate of 19000 isn't supported. We'll aim to increase the sample rate in a separate PR but this will at least mean the simulator isn't put in a bad state. - Reuse the audio context created after the user interaction, as there are limits to how many you can create on Safari 13 (4 in my BrowserStack testing). I think we always intended to do this, but careful review needed as this is a more significant change than I was anticipating. We already had clean up code for the nodes when the board stops. We never cleaned up the context. So I think we were assuming it lived forever. If this doesn't work out then an alternative is to try closing the old one before creating the new one. --- src/board/audio/index.ts | 10 ++++++---- src/jshal.js | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/board/audio/index.ts b/src/board/audio/index.ts index 3b14dd53..9bb7f5f9 100644 --- a/src/board/audio/index.ts +++ b/src/board/audio/index.ts @@ -67,10 +67,12 @@ export class Audio { } async createAudioContextFromUserInteraction(): Promise { - this.context = new (window.AudioContext || window.webkitAudioContext)({ - // The highest rate is the sound expression synth. - sampleRate: 44100, - }); + this.context = + this.context ?? + new (window.AudioContext || window.webkitAudioContext)({ + // The highest rate is the sound expression synth. + sampleRate: 44100, + }); if (this.context.state === "suspended") { return this.context.resume(); } diff --git a/src/jshal.js b/src/jshal.js index 39d3cf08..3cc18cd1 100644 --- a/src/jshal.js +++ b/src/jshal.js @@ -240,14 +240,22 @@ mergeInto(LibraryManager.library, { /** @type {number} */ buf, /** @type {number} */ num_samples ) { + /** @type {AudioBuffer | undefined} */ let webAudioBuffer; + try { + // @ts-expect-error + webAudioBuffer = Module.board.audio.speech.createBuffer(num_samples); + } catch (e) { + // Swallow error on older Safari to keep the sim in a good state. + // @ts-expect-error + if (e.name === "NotSupportedError") { + return; + } else { + throw e; + } + } // @ts-expect-error Module.board.audio.speech.writeData( - Module.conversions.convertAudioBuffer( - Module.HEAPU8, - buf, - // @ts-expect-error - Module.board.audio.speech.createBuffer(num_samples) - ) + Module.conversions.convertAudioBuffer(Module.HEAPU8, buf, webAudioBuffer) ); },