From 288c51aa0de82d1b7eed965b5ef942ae23595258 Mon Sep 17 00:00:00 2001 From: Alexey Zinoviev <alexey.zinoviev@xored.com> Date: Tue, 15 Oct 2024 21:31:49 +0400 Subject: [PATCH] uberf-8485: fix sounds (#6944) Signed-off-by: Alexey Zinoviev <alexey.zinoviev@xored.com> --- packages/presentation/src/sound.ts | 70 ++++++++++--------- .../src/components/RequestPopup.svelte | 11 +-- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/packages/presentation/src/sound.ts b/packages/presentation/src/sound.ts index 1be62940a7..eff939be10 100644 --- a/packages/presentation/src/sound.ts +++ b/packages/presentation/src/sound.ts @@ -3,10 +3,10 @@ import { type Asset, getMetadata, getResource } from '@hcengineering/platform' import { getClient } from '.' import notification from '@hcengineering/notification' -const sounds = new Map<Asset, AudioBufferSourceNode>() +const sounds = new Map<Asset, AudioBuffer>() const context = new AudioContext() -export async function prepareSound (key: string, _class?: Ref<Class<Doc>>, loop = false, play = false): Promise<void> { +export async function prepareSound (key: string, _class?: Ref<Class<Doc>>): Promise<void> { if (_class === undefined) return const client = getClient() @@ -23,39 +23,45 @@ export async function prepareSound (key: string, _class?: Ref<Class<Doc>>, loop try { const soundUrl = getMetadata(key as Asset) as string - const audioBuffer = await fetch(soundUrl) - .then(async (res) => await res.arrayBuffer()) - .then(async (ArrayBuffer) => await context.decodeAudioData(ArrayBuffer)) + const rawAudio = await fetch(soundUrl) + const rawBuffer = await rawAudio.arrayBuffer() + const decodedBuffer = await context.decodeAudioData(rawBuffer) + + sounds.set(key as Asset, decodedBuffer) + } catch (err) { + console.error('Sound not found', key) + } +} + +export async function playSound ( + soundKey: string, + _class?: Ref<Class<Doc>>, + loop = false +): Promise<(() => void) | null> { + const soundAssetKey = soundKey as Asset + if (!sounds.has(soundAssetKey)) { + await prepareSound(soundKey, _class) + } + + const sound = sounds.get(soundKey as Asset) + if (sound === undefined) { + console.error('Cannot prepare audio buffer', soundKey) + return null + } + + try { const audio = context.createBufferSource() - audio.buffer = audioBuffer + audio.buffer = sound audio.loop = loop - sounds.set(key as Asset, audio) - if (play) { - playSound(key) + audio.connect(context.destination) + audio.start() + + return (): void => { + audio.stop() + audio.disconnect(context.destination) } } catch (err) { - console.error('sound not found', key) - } -} - -export function playSound (soundKey: string, _class?: Ref<Class<Doc>>, loop = false): void { - const sound = sounds.get(soundKey as Asset) - if (sound !== undefined) { - try { - sound.connect(context.destination) - sound.start() - } catch (err) { - console.error('error happened during sound play', soundKey, err) - } - } else { - void prepareSound(soundKey, _class, loop, true) - } -} - -export function stopSound (soundKey: string): void { - const sound = sounds.get(soundKey as Asset) - if (sound !== undefined && sound?.context.state === 'running') { - sound.stop() - sound.disconnect(context.destination) + console.error('Error when playing sound back', soundKey, err) + return null } } diff --git a/plugins/love-resources/src/components/RequestPopup.svelte b/plugins/love-resources/src/components/RequestPopup.svelte index cd285c49cd..650761715d 100644 --- a/plugins/love-resources/src/components/RequestPopup.svelte +++ b/plugins/love-resources/src/components/RequestPopup.svelte @@ -16,7 +16,7 @@ import { PersonAccount, formatName } from '@hcengineering/contact' import { Avatar, personByIdStore } from '@hcengineering/contact-resources' import { getCurrentAccount } from '@hcengineering/core' - import { getClient, playSound, stopSound } from '@hcengineering/presentation' + import { getClient, playSound } from '@hcengineering/presentation' import { Button, Label } from '@hcengineering/ui' import { JoinRequest, RequestStatus } from '@hcengineering/love' import love from '../plugin' @@ -29,6 +29,7 @@ $: person = $personByIdStore.get(request.person) const client = getClient() + let stopSound: (() => void) | null = null async function accept (): Promise<void> { await client.update(request, { status: RequestStatus.Approved }) @@ -43,11 +44,13 @@ async function decline (): Promise<void> { await client.update(request, { status: RequestStatus.Rejected }) } - onMount(() => { - playSound(love.sound.Knock, love.class.JoinRequest, true) + + onMount(async () => { + stopSound = await playSound(love.sound.Knock, love.class.JoinRequest, true) }) + onDestroy(() => { - stopSound(love.sound.Knock) + stopSound?.() }) </script>