import { clamp } from "./util" ; import { ControllerManager } from "./input/ControllerManager" ; const AudioContext = window . AudioContext ?? ( window as any ) . webkitAudioContext as AudioContext ; let audioContext = null ; let globalGainNode = null ; export function getAudioContext ( ) { const controllerManager = ControllerManager . getInstance ( ) ; if ( audioContext == null ) { audioContext = new AudioContext ( ) ; if ( audioContext . state === "suspended" ) { const resume = ( ) => { audioContext ?. resume ( ) ; } ; controllerManager . onButtonDown . connect ( resume ) ; document . addEventListener ( "pointerdown" , resume ) ; audioContext . addEventListener ( "statechange" , ( ) => { if ( audioContext ?. state === "running" ) { controllerManager . onButtonDown . disconnect ( resume ) ; document . removeEventListener ( "pointerdown" , resume ) ; } } ) ; } } return audioContext ; } export function getGlobalGainNode ( ) { if ( globalGainNode == null ) { const audioContext = getAudioContext ( ) ; globalGainNode = audioContext . createGain ( ) ; globalGainNode . connect ( audioContext . destination ) ; } return globalGainNode ; } export class Sound { private readonly gainNode : GainNode ; private source : AudioBufferSourceNode | null = null ; private loop : boolean = false ; private constructor ( private readonly buffer ) { this . gainNode = getAudioContext ( ) . createGain ( ) ; this . gainNode . connect ( getGlobalGainNode ( ) ) ; } public static async load ( url ) { const arrayBuffer = await ( await fetch ( url ) ) . arrayBuffer ( ) ; return new Promise ( ( resolve , reject ) => { getAudioContext ( ) . decodeAudioData ( arrayBuffer , buffer => resolve ( new Sound ( buffer ) ) , error => reject ( error ) ) ; } ) ; } public isPlaying ( ) { return this . source != null ; } public play ( ) { if ( ! this . isPlaying ( ) ) { const source = getAudioContext ( ) . createBufferSource ( ) ; source . buffer = this . buffer ; source . loop = this . loop ; source . connect ( this . gainNode ) ; source . addEventListener ( "ended" , ( ) => { if ( this . source === source ) { this . source = null ; } } ) ; this . source = source ; source . start ( ) ; } } public stop ( ) { if ( this . source ) { try { this . source . stop ( ) ; } catch ( e ) { } this . source = null ; } } public setLoop ( loop ) { this . loop = loop ; if ( this . source ) { this . source . loop = loop ; } } public setVolume ( volume ) { const gain = this . gainNode . gain ; gain . value = clamp ( volume , gain . minValue , gain . maxValue ) ; } }