import { Filter } from './filters/Filter' ; import { HTMLAudioMedia } from './htmlaudio/HTMLAudioMedia' ; import { getInstance } from './instance' ; import { IMedia , IMediaContext , IMediaInstance } from './interfaces' ; import { SoundSprite , SoundSpriteData , SoundSprites } from './SoundSprite' ; import { resolveUrl } from './utils/resolveUrl' ; import { WebAudioMedia } from './webaudio/WebAudioMedia' ; /**
 * Options to use for creating sounds.
 */ interface Options { default autoPlay ? : boolean ; default singleInstance ? : boolean ; default volume ? : number ; default speed ? : number ; type { Function } complete ? : CompleteCallback ; type { Function } loaded ? : LoadedCallback ; /**
     * `true` to immediately start preloading if loading from `url`.
     */ preload ? : boolean ; default loop ? : boolean ; /**
     * The source of the file being loaded
     */ url ? : string ; /**
     * If sound is already preloaded, available.
     */ source ? : ArrayBuffer | AudioBuffer | HTMLAudioElement ; /**
     * The map of sprite data. Where a sprite is an object
     * with a `start` and `end`, which are the times in seconds. Optionally, can include
     * a `speed` amount where 1 is 100% speed.
     */ sprites ? : Record < string , SoundSpriteData > ; } /**
 * Options used for sound playback.
 */ interface PlayOptions { default start ? : number ; /**
     * End time in seconds.
     */ end ? : number ; /**
     * Override default speed, default to the Sound's speed setting.
     */ speed ? : number ; /**
    * Override default loop, default to the Sound's loop setting.
    */ loop ? : boolean ; /**
     * Override default volume, default to the Sound's volume setting.
     */ volume ? : number ; /**
     * The sprite to play.
     */ sprite ? : string ; default muted ? : boolean ; /**
     * Filters that apply to play.
     * Only supported with WebAudio.
     */ filters ? : Filter [ ] ; type { Function } complete ? : CompleteCallback ; type { Function } loaded ? : LoadedCallback ; /**
     * Setting `true` will stop any playing instances. This is the same as
     * the singleInstance property on Sound, but is play-specific.
     */ singleInstance ? : boolean ; } ignore param { Error } err param { Sound } sound param { IMediaInstance } instance type LoadedCallback = ( err : Error , sound ? : Sound , instance ? : IMediaInstance ) => void ; ignore param { Sound } sound type CompleteCallback = ( sound : Sound ) => void ; type SoundSpriteDataMap = Record < string , SoundSpriteData > ; /**
 * Sound represents a single piece of loaded media. When playing a sound  IMediaInstance objects
 * are created. Properties such a `volume`, `pause`, `mute`, `speed`, etc will have an effect on all instances.
  class Sound { /** Pool of instances */ private static _pool : IMediaInstance [ ] = [ ] ; default public isLoaded : boolean ; default readonly public isPlaying : boolean ; default readonly public autoPlay : boolean ; default public singleInstance : boolean ; default readonly public preload : boolean ; readonly public url : string ; readonly public options : Options ; /** The audio source */ public media : IMedia ; /** The collection of instances being played. */ private _instances : IMediaInstance [ ] ; /** The user defined sound sprites. */ private _sprites : SoundSprites ; /** The options when auto-playing. */ private _autoPlayOptions : PlayOptions ; /** The internal volume. */ private _volume : number ; /** The internal paused state. */ private _paused : boolean ; /** The internal muted state. */ private _muted : boolean ; /** The internal volume. */ private _loop : boolean ; /** The internal playbackRate */ private _speed : number ; param source return public static from ( source ) { let options = { } ; if ( typeof source === 'string' ) { options . url = source as string ; } else if ( source instanceof ArrayBuffer || source instanceof AudioBuffer || source instanceof HTMLAudioElement ) { options . source = source ; } else { options = source ; } options = { autoPlay : false , singleInstance : false , url : null , source : null , preload : false , volume : 1 , speed : 1 , complete : null , loaded : null , loop : false , ... options } ; if ( options . url ) { options . url = resolveUrl ( options . url ) ; } Object . freeze ( options ) ; const media = getInstance ( ) . useLegacy ? new HTMLAudioMedia ( ) : new WebAudioMedia ( ) ; return new Sound ( media , options ) ; } ignore constructor ( media , options ) { this . media = media ; this . options = options ; this . _instances = [ ] ; this . _sprites = { } ; this . media . init ( this ) ; const complete = options . complete ; this . _autoPlayOptions = complete ? { complete } : null ; this . isLoaded = false ; this . isPlaying = false ; this . autoPlay = options . autoPlay ; this . singleInstance = options . singleInstance ; this . preload = options . preload || this . autoPlay ; this . url = options . url ; this . speed = options . speed ; this . volume = options . volume ; this . loop = options . loop ; if ( options . sprites ) { this . addSprites ( options . sprites ) ; } if ( this . preload ) { this . _preload ( options . loaded ) ; } } /** Instance of the media context. */ public get context ( ) : IMediaContext { return getInstance ( ) . context ; } /** Stops all the instances of this sound from playing. */ public pause ( ) { this . isPlaying = false ; this . paused = true ; return this ; } /** Resuming all the instances of this sound from playing */ public resume ( ) { this . isPlaying = this . _instances . length > 0 ; this . paused = false ; return this ; } /** Stops all the instances of this sound from playing. */ public get paused ( ) : boolean { return this . _paused ; } public set paused ( paused ) { this . _paused = paused ; this . refreshPaused ( ) ; } /** The playback rate. */ public get speed ( ) : number { return this . _speed ; } public set speed ( speed ) { this . _speed = speed ; this . refresh ( ) ; } /** Set the filters. Only supported with WebAudio. */ public get filters ( ) : Filter [ ] { return this . media . filters ; } public set filters ( filters ) { this . media . filters = filters ; } param alias param data public addSprites ( alias , data ) ; param data return public addSprites ( data ) ; ignore public addSprites ( source , data ? ) { if ( typeof source === 'object' ) { const results = { } ; for ( const alias in source ) { results [ alias ] = this . addSprites ( alias , source [ alias ] ) ; } return results ; } console . assert ( ! this . _sprites [ source ] , `Alias ${ source } is already taken` ) ; const sprite = new SoundSprite ( this , data ) ; this . _sprites [ source ] = sprite ; return sprite ; } /** Destructor, safer to use `SoundLibrary.remove(alias)` to remove this sound. */ public destroy ( ) { this . _removeInstances ( ) ; this . removeSprites ( ) ; this . media . destroy ( ) ; this . media = null ; this . _sprites = null ; this . _instances = null ; } param alias public removeSprites ( alias ? ) { if ( ! alias ) { for ( const name in this . _sprites ) { this . removeSprites ( name ) ; } } else { const sprite = this . _sprites [ alias ] ; if ( sprite !== undefined ) { sprite . destroy ( ) ; delete this . _sprites [ alias ] ; } } return this ; } /** If the current sound is playable (loaded). */ public get isPlayable ( ) : boolean { return this . isLoaded && this . media && this . media . isPlayable ; } /** Stops all the instances of this sound from playing. */ public stop ( ) { if ( ! this . isPlayable ) { this . autoPlay = false ; this . _autoPlayOptions = null ; return this ; } this . isPlaying = false ; for ( let i = this . _instances . length - 1 ; i >= 0 ; i -- ) { this . _instances [ i ] . stop ( ) ; } return this ; } method instance param alias param { Function } callback return public play ( alias , callback ? ) ; method instance param { Function | PlayOptions } source param { Function } callback return public play ( source ? , callback ? ) ; public play ( source ? , complete ? ) { let options ; if ( typeof source === 'string' ) { const sprite = source as string ; options = { sprite , loop : this . loop , complete } ; } else if ( typeof source === 'function' ) { options = { } ; options . complete = source as CompleteCallback ; } else { options = source as PlayOptions ; } options = { complete : null , loaded : null , sprite : null , end : null , start : 0 , volume : 1 , speed : 1 , muted : false , loop : false , ... ( options || { } ) } ; if ( options . sprite ) { const alias = options . sprite ; console . assert ( ! ! this . _sprites [ alias ] , `Alias ${ alias } is not available` ) ; const sprite = this . _sprites [ alias ] ; options . start = sprite . start + ( options . start || 0 ) ; options . end = sprite . end ; options . speed = sprite . speed || 1 ; options . loop = sprite . loop || options . loop ; delete options . sprite ; } if ( ( options as any ) . offset ) { options . start = ( options as any ) . offset as number ; } if ( ! this . isLoaded ) { return new Promise < IMediaInstance > ( ( resolve , reject ) => { this . autoPlay = true ; this . _autoPlayOptions = options ; this . _preload ( ( err , sound , media ) => { if ( err ) { reject ( err ) ; } else { if ( options . loaded ) { options . loaded ( err , sound , media ) ; } resolve ( media ) ; } } ) ; } ) ; } if ( this . singleInstance || options . singleInstance ) { this . _removeInstances ( ) ; } const instance = this . _createInstance ( ) ; this . _instances . push ( instance ) ; this . isPlaying = true ; instance . once ( 'end' , ( ) => { if ( options . complete ) { options . complete ( this ) ; } this . _onComplete ( instance ) ; } ) ; instance . once ( 'stop' , ( ) => { this . _onComplete ( instance ) ; } ) ; instance . play ( options ) ; return instance ; } /** Internal only, speed, loop, volume change occured. */ public refresh ( ) { const len = this . _instances . length ; for ( let i = 0 ; i < len ; i ++ ) { this . _instances [ i ] . refresh ( ) ; } } /** Handle changes in paused state. Internal only. */ public refreshPaused ( ) { const len = this . _instances . length ; for ( let i = 0 ; i < len ; i ++ ) { this . _instances [ i ] . refreshPaused ( ) ; } } /** Gets and sets the volume. */ public get volume ( ) : number { return this . _volume ; } public set volume ( volume ) { this . _volume = volume ; this . refresh ( ) ; } /** Gets and sets the muted flag. */ public get muted ( ) : boolean { return this . _muted ; } public set muted ( muted ) { this . _muted = muted ; this . refresh ( ) ; } /** Gets and sets the looping. */ public get loop ( ) : boolean { return this . _loop ; } public set loop ( loop ) { this . _loop = loop ; this . refresh ( ) ; } /** Starts the preloading of sound. */ private _preload ( callback ? ) { this . media . load ( callback ) ; } /** Gets the list of instances that are currently being played of this sound. */ public get instances ( ) : IMediaInstance [ ] { return this . _instances ; } /** Get the map of sprites. */ public get sprites ( ) : SoundSprites { return this . _sprites ; } /** Get the duration of the audio in seconds. */ public get duration ( ) : number { return this . media . duration ; } /** Auto play the first instance. */ public autoPlayStart ( ) { let instance ; if ( this . autoPlay ) { instance = this . play ( this . _autoPlayOptions ) as IMediaInstance ; } return instance ; } /** Removes all instances. */ private _removeInstances ( ) { for ( let i = this . _instances . length - 1 ; i >= 0 ; i -- ) { this . _poolInstance ( this . _instances [ i ] ) ; } this . _instances . length = 0 ; } param instance private _onComplete ( instance ) { if ( this . _instances ) { const index = this . _instances . indexOf ( instance ) ; if ( index > - 1 ) { this . _instances . splice ( index , 1 ) ; } this . isPlaying = this . _instances . length > 0 ; } this . _poolInstance ( instance ) ; } /** Create a new instance. */ private _createInstance ( ) { if ( Sound . _pool . length > 0 ) { const instance = Sound . _pool . pop ( ) ; instance . init ( this . media ) ; return instance ; } return this . media . create ( ) ; } param instance private _poolInstance ( instance ) { instance . destroy ( ) ; if ( Sound . _pool . indexOf ( instance ) < 0 ) { Sound . _pool . push ( instance ) ; } } } export { Sound } ; export type { Options , PlayOptions , LoadedCallback , CompleteCallback , SoundSpriteDataMap } ;