From be621b4b2b74f2ca606409895d86abc48a550f27 Mon Sep 17 00:00:00 2001 From: dselman Date: Wed, 18 Dec 2024 13:00:10 +0000 Subject: [PATCH 1/6] fix(audiocontext): works on both Safari and Chrome Signed-off-by: dselman --- src/lib/utils.ts | 51 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index da140b5fd..9ee366347 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -20,47 +20,20 @@ export type GetAudioContextOptions = AudioContextOptions & { const map: Map = new Map(); -export const audioContext: ( - options?: GetAudioContextOptions, -) => Promise = (() => { - const didInteract = new Promise((res) => { - window.addEventListener("pointerdown", res, { once: true }); - window.addEventListener("keydown", res, { once: true }); - }); - - return async (options?: GetAudioContextOptions) => { - try { - const a = new Audio(); - a.src = - "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; - await a.play(); - if (options?.id && map.has(options.id)) { - const ctx = map.get(options.id); - if (ctx) { - return ctx; - } - } - const ctx = new AudioContext(options); - if (options?.id) { - map.set(options.id, ctx); - } - return ctx; - } catch (e) { - await didInteract; - if (options?.id && map.has(options.id)) { - const ctx = map.get(options.id); - if (ctx) { - return ctx; - } +export async function audioContext(options?: GetAudioContextOptions,) : Promise { + if (options?.id && map.has(options.id)) { + const ctx = map.get(options.id); + if (ctx) { + return ctx; } - const ctx = new AudioContext(options); - if (options?.id) { - map.set(options.id, ctx); - } - return ctx; } - }; -})(); + + const ctx = new AudioContext(options); + if (options?.id) { + map.set(options.id, ctx); + } + return ctx; +} export const blobToJSON = (blob: Blob) => new Promise((resolve, reject) => { From d79b18591d8a7b90621f870822bdbdcae646777a Mon Sep 17 00:00:00 2001 From: dselman Date: Thu, 2 Jan 2025 12:11:38 +0000 Subject: [PATCH 2/6] revert Signed-off-by: dselman --- src/lib/utils.ts | 53 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 9ee366347..d06161af1 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -20,20 +20,47 @@ export type GetAudioContextOptions = AudioContextOptions & { const map: Map = new Map(); -export async function audioContext(options?: GetAudioContextOptions,) : Promise { - if (options?.id && map.has(options.id)) { - const ctx = map.get(options.id); - if (ctx) { - return ctx; - } - } +export const audioContext: ( + options?: GetAudioContextOptions, +) => Promise = (() => { + const didInteract = new Promise((res) => { + window.addEventListener("pointerdown", () => {res(true);}, { once: true }); + window.addEventListener("keydown", () => {res(true);}, { once: true }); + }); - const ctx = new AudioContext(options); - if (options?.id) { - map.set(options.id, ctx); + return async (options?: GetAudioContextOptions) => { + try { + const a = new Audio(); + a.src = + "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; + await a.play(); + if (options?.id && map.has(options.id)) { + const ctx = map.get(options.id); + if (ctx) { + return ctx; + } + } + const ctx = new AudioContext(options); + if (options?.id) { + map.set(options.id, ctx); + } + return ctx; + } catch (e) { + await didInteract; + if (options?.id && map.has(options.id)) { + const ctx = map.get(options.id); + if (ctx) { + return ctx; + } + } + const ctx = new AudioContext(options); + if (options?.id) { + map.set(options.id, ctx); + } + return ctx; } - return ctx; -} + }; +})(); export const blobToJSON = (blob: Blob) => new Promise((resolve, reject) => { @@ -56,4 +83,4 @@ export function base64ToArrayBuffer(base64: string) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; -} +} \ No newline at end of file From 570bd41b4bb91f550f042d776e61e761ec6a3b12 Mon Sep 17 00:00:00 2001 From: dselman Date: Thu, 2 Jan 2025 17:00:16 +0000 Subject: [PATCH 3/6] works in Chrome and Safari Signed-off-by: dselman --- src/lib/utils.ts | 53 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d06161af1..c4ae044d5 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -19,48 +19,49 @@ export type GetAudioContextOptions = AudioContextOptions & { }; const map: Map = new Map(); +let interacted = false; -export const audioContext: ( - options?: GetAudioContextOptions, -) => Promise = (() => { - const didInteract = new Promise((res) => { - window.addEventListener("pointerdown", () => {res(true);}, { once: true }); - window.addEventListener("keydown", () => {res(true);}, { once: true }); - }); +/** + * Waits for a user interfaction and then resumes an audio context. + * The web-browser prevents audio context from being used before user interaction + * to stop web sites abusing auto play, or auto record. + * @param audioCtx the audio context to unlock + */ +function unlockAudioContext(audioCtx:AudioContext) { + if (audioCtx.state !== 'suspended') { + return; + } + if(interacted) { + audioCtx.resume(); + return; + } + const events = ['touchstart','touchend', 'mousedown','keydown', 'pointerdown']; + events.forEach(e => window.addEventListener(e, unlock, false)); + function unlock() { interacted=true; console.log('unlock');audioCtx.resume().then(clean); } + function clean() { events.forEach(e => window.removeEventListener(e, unlock)); } +} - return async (options?: GetAudioContextOptions) => { +export async function audioContext(options?: GetAudioContextOptions) : Promise { try { - const a = new Audio(); - a.src = - "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; - await a.play(); if (options?.id && map.has(options.id)) { const ctx = map.get(options.id); if (ctx) { + unlockAudioContext(ctx); return ctx; } } const ctx = new AudioContext(options); + console.log('new audio context 1', options); if (options?.id) { map.set(options.id, ctx); } + unlockAudioContext(ctx); return ctx; } catch (e) { - await didInteract; - if (options?.id && map.has(options.id)) { - const ctx = map.get(options.id); - if (ctx) { - return ctx; - } - } - const ctx = new AudioContext(options); - if (options?.id) { - map.set(options.id, ctx); - } - return ctx; + console.log(e); + throw e; } - }; -})(); +} export const blobToJSON = (blob: Blob) => new Promise((resolve, reject) => { From de47730a5c1ded8e5c2810bf5a103e798c5522ee Mon Sep 17 00:00:00 2001 From: dselman Date: Thu, 2 Jan 2025 17:11:03 +0000 Subject: [PATCH 4/6] chore(console) remove debug Signed-off-by: dselman --- src/lib/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c4ae044d5..d0fd3196b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -37,7 +37,7 @@ function unlockAudioContext(audioCtx:AudioContext) { } const events = ['touchstart','touchend', 'mousedown','keydown', 'pointerdown']; events.forEach(e => window.addEventListener(e, unlock, false)); - function unlock() { interacted=true; console.log('unlock');audioCtx.resume().then(clean); } + function unlock() { interacted=true;audioCtx.resume().then(clean); } function clean() { events.forEach(e => window.removeEventListener(e, unlock)); } } From fcf6a39c46314a9774c5f00505aadcfb6541bf81 Mon Sep 17 00:00:00 2001 From: dselman Date: Thu, 2 Jan 2025 17:11:48 +0000 Subject: [PATCH 5/6] chore(console) remove debug Signed-off-by: dselman --- src/lib/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d0fd3196b..479f05978 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -51,7 +51,6 @@ export async function audioContext(options?: GetAudioContextOptions) : Promise Date: Thu, 2 Jan 2025 17:13:26 +0000 Subject: [PATCH 6/6] chore(console) remove debug Signed-off-by: dselman --- src/lib/utils.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 479f05978..ce8be6eb6 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -42,24 +42,19 @@ function unlockAudioContext(audioCtx:AudioContext) { } export async function audioContext(options?: GetAudioContextOptions) : Promise { - try { - if (options?.id && map.has(options.id)) { - const ctx = map.get(options.id); - if (ctx) { - unlockAudioContext(ctx); - return ctx; - } + if (options?.id && map.has(options.id)) { + const ctx = map.get(options.id); + if (ctx) { + unlockAudioContext(ctx); + return ctx; } - const ctx = new AudioContext(options); - if (options?.id) { - map.set(options.id, ctx); - } - unlockAudioContext(ctx); - return ctx; - } catch (e) { - console.log(e); - throw e; } + const ctx = new AudioContext(options); + if (options?.id) { + map.set(options.id, ctx); + } + unlockAudioContext(ctx); + return ctx; } export const blobToJSON = (blob: Blob) =>