interface IAudioFormatReader { PushData ( data ) : void ; SamplesAvailable ( ) : boolean ; PopSamples ( ) : AudioBuffer ; PurgeData ( ) : void ; Reset ( ) : void ; Poke ( ) : void ; } abstract class AudioFormatReader implements IAudioFormatReader { protected readonly Audio : AudioContext ; protected readonly Logger : Logging ; protected readonly ErrorCallback : ( ) => void ; protected readonly BeforeDecodeCheck : ( length : number ) => boolean ; protected readonly DataReadyCallback : ( ) => void ; protected Id : number ; protected LastPushedId : number ; protected Samples : Array < AudioBuffer > ; protected BufferStore : Record < number , AudioBuffer > ; protected DataBuffer : Uint8Array ; constructor ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback ) { if ( ! audio ) throw new Error ( 'AudioFormatReader: audio must be specified' ) ; if ( typeof errorCallback !== 'function' ) throw new Error ( 'AudioFormatReader: errorCallback must be specified' ) ; if ( typeof beforeDecodeCheck !== 'function' ) throw new Error ( 'AudioFormatReader: beforeDecodeCheck must be specified' ) ; if ( typeof dataReadyCallback !== 'function' ) throw new Error ( 'AudioFormatReader: dataReadyCallback must be specified' ) ; this . Audio = audio ; this . Logger = logger ; this . ErrorCallback = errorCallback ; this . BeforeDecodeCheck = beforeDecodeCheck ; this . DataReadyCallback = dataReadyCallback ; this . Id = 0 ; this . LastPushedId = - 1 ; this . Samples = new Array ( ) ; this . BufferStore = { } ; this . DataBuffer = new Uint8Array ( 0 ) ; } public PushData ( data ) { this . DataBuffer = this . ConcatUint8Array ( this . DataBuffer , data ) ; this . ExtractAll ( ) ; } public SamplesAvailable ( ) { return ( this . Samples . length > 0 ) ; } public PopSamples ( ) { if ( this . Samples . length > 0 ) { return this . Samples . shift ( ) ; } else return null ; } public PurgeData ( ) { this . Id = 0 ; this . LastPushedId = - 1 ; this . Samples = new Array ( ) ; this . BufferStore = { } ; this . DataBuffer = new Uint8Array ( 0 ) ; } public Poke ( ) { this . ExtractAll ( ) ; } public Reset ( ) { this . PurgeData ( ) ; } protected ExtractAll ( ) { } protected OnBeforeDecode ( id , duration ) { return true ; } protected OnDataReady ( id , audioBuffer ) { if ( this . LastPushedId + 1 == id ) { this . Samples . push ( audioBuffer ) ; this . LastPushedId ++ ; while ( this . BufferStore [ this . LastPushedId + 1 ] ) { this . Samples . push ( this . BufferStore [ this . LastPushedId + 1 ] ) ; delete this . BufferStore [ this . LastPushedId + 1 ] ; this . LastPushedId ++ ; } this . DataReadyCallback ( ) ; } else { this . BufferStore [ id ] = audioBuffer ; } } protected ConcatUint8Array ( a , b ) { let tmp = new Uint8Array ( a . length + b . length ) ; tmp . set ( a , 0 ) ; tmp . set ( b , a . length ) ; return tmp ; } public static CanDecodeTypes ( mimeTypes ) { let audioTag = new Audio ( ) ; let result = false ; for ( let i = 0 ; i < mimeTypes . length ; i ++ ) { let mimeType = mimeTypes [ i ] ; let answer = audioTag . canPlayType ( mimeType ) ; if ( answer != "probably" && answer != "maybe" ) continue ; result = true ; break ; } audioTag = null ; return result ; } public static DefaultSettings ( ) { let settings = { } ; settings [ "wav" ] = { } ; settings [ "wav" ] [ "BatchDuration" ] = 1 / 10 ; settings [ "wav" ] [ "ExtraEdgeDuration" ] = 1 / 300 ; settings [ "mpeg" ] = { } ; settings [ "mpeg" ] [ "AddID3Tag" ] = false ; if ( isAndroid ) settings [ "mpeg" ] [ "MinDecodeFrames" ] = 17 ; else settings [ "mpeg" ] [ "MinDecodeFrames" ] = 3 ; return settings ; } public static Create ( mime , audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback , settings = null ) { if ( typeof mime !== "string" ) throw new Error ( 'CreateAudioFormatReader: Invalid MIME-Type, must be string' ) ; if ( ! settings ) settings = this . DefaultSettings ( ) ; let fullMime = mime ; if ( mime . indexOf ( "audio/pcm" ) == 0 ) mime = "audio/pcm" ; switch ( mime . replace ( /\s/g , "" ) ) { case "audio/mpeg" : case "audio/MPA" : case "audio/mpa-robust" : if ( ! AudioFormatReader . CanDecodeTypes ( new Array ( "audio/mpeg" , "audio/MPA" , "audio/mpa-robust" ) ) ) throw new Error ( 'CreateAudioFormatReader: Browser can not decode specified MIME-Type (' + mime + ')' ) ; return new AudioFormatReader_MPEG ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback , < boolean > settings [ "mpeg" ] [ "AddID3Tag" ] , < number > settings [ "mpeg" ] [ "MinDecodeFrames" ] ) ; break ; case "audio/vnd.wave" : case "audio/wav" : case "audio/wave" : case "audio/x-wav" : if ( ! AudioFormatReader . CanDecodeTypes ( new Array ( "audio/wav" , "audio/wave" ) ) ) throw new Error ( 'CreateAudioFormatReader: Browser can not decode specified MIME-Type (' + mime + ')' ) ; return new AudioFormatReader_WAV ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback , < number > settings [ "wav" ] [ "BatchDuration" ] , < number > settings [ "wav" ] [ "ExtraEdgeDuration" ] ) ; break ; default : throw new Error ( 'CreateAudioFormatReader: Specified MIME-Type (' + mime + ') not supported' ) ; break ; } } }