import { options } from './optionsStorage' import { isCypress } from './standaloneUtils' import { reportWarningOnce } from './utils' let audioContext: AudioContext const sounds: Record = {} // load as many resources on page load as possible instead on demand as user can disable internet connection after he thinks the page is loaded const loadingSounds = [] as string[] const convertedSounds = [] as string[] export async function loadSound (path: string) { if (loadingSounds.includes(path)) return true loadingSounds.push(path) const res = await window.fetch(path) if (!res.ok) { const error = `Failed to load sound ${path}` if (isCypress()) throw new Error(error) else console.warn(error) return } const data = await res.arrayBuffer() sounds[path] = data loadingSounds.splice(loadingSounds.indexOf(path), 1) } export const loadOrPlaySound = async (url, soundVolume = 1) => { const soundBuffer = sounds[url] if (!soundBuffer) { const start = Date.now() const cancelled = await loadSound(url) if (cancelled || Date.now() - start > 500) return } await playSound(url) } export async function playSound (url, soundVolume = 1) { const volume = soundVolume * (options.volume / 100) if (!volume) return try { audioContext ??= new window.AudioContext() } catch (err) { reportWarningOnce('audioContext', 'Failed to create audio context. Some sounds will not play') return } for (const [soundName, sound] of Object.entries(sounds)) { if (convertedSounds.includes(soundName)) continue sounds[soundName] = await audioContext.decodeAudioData(sound) convertedSounds.push(soundName) } const soundBuffer = sounds[url] if (!soundBuffer) { console.warn(`Sound ${url} not loaded yet`) return } const gainNode = audioContext.createGain() const source = audioContext.createBufferSource() source.buffer = soundBuffer source.connect(gainNode) gainNode.connect(audioContext.destination) gainNode.gain.value = volume source.start(0) }