class AudioFormatReader_WAV extends AudioFormatReader implements IAudioFormatReader { private readonly BatchDuration : number ; private readonly ExtraEdgeDuration : number ; private GotHeader : boolean ; private RiffHeader : Uint8Array ; private WaveSampleRate : number ; private WaveBitsPerSample : number ; private WaveBytesPerSample : number ; private WaveBlockAlign : number ; private WaveChannels : number ; private BatchSamples : number ; private BatchBytes : number ; private ExtraEdgeSamples : number ; private TotalBatchSampleSize : number ; private TotalBatchByteSize : number ; private SampleBudget : number ; constructor ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback , batchDuration , extraEdgeDuration ) { super ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback ) ; this . _OnDecodeSuccess = this . OnDecodeSuccess . bind ( this ) ; this . _OnDecodeError = this . OnDecodeError . bind ( this ) ; this . BatchDuration = batchDuration ; this . ExtraEdgeDuration = extraEdgeDuration ; this . GotHeader = false ; this . RiffHeader = null ; this . WaveSampleRate = 0 ; this . WaveBitsPerSample = 0 ; this . WaveBytesPerSample = 0 ; this . WaveBlockAlign = 0 ; this . WaveChannels = 0 ; this . BatchSamples = 0 ; this . BatchBytes = 0 ; this . ExtraEdgeSamples = 0 ; this . TotalBatchSampleSize = 0 ; this . TotalBatchByteSize = 0 ; this . SampleBudget = 0 ; } public PurgeData ( ) { super . PurgeData ( ) ; this . SampleBudget = 0 ; } public Reset ( ) { super . Reset ( ) ; this . GotHeader = false ; this . RiffHeader = null ; this . WaveSampleRate = 0 ; this . WaveBitsPerSample = 0 ; this . WaveBytesPerSample = 0 ; this . WaveBlockAlign = 0 ; this . WaveChannels = 0 ; this . BatchSamples = 0 ; this . BatchBytes = 0 ; this . ExtraEdgeSamples = 0 ; this . TotalBatchSampleSize = 0 ; this . TotalBatchByteSize = 0 ; this . SampleBudget = 0 ; } protected ExtractAll ( ) { if ( ! this . GotHeader ) this . FindAndExtractHeader ( ) ; else { while ( this . CanExtractSamples ( ) ) { let tmpSamples = this . ExtractIntSamples ( ) ; let id = this . Id ++ ; if ( ! this . OnBeforeDecode ( id , this . BatchDuration ) ) continue ; let samplesBuffer = new Uint8Array ( this . RiffHeader . length + tmpSamples . length ) ; let offset = 0 ; samplesBuffer . set ( this . RiffHeader , offset ) ; offset += this . RiffHeader . length ; samplesBuffer . set ( tmpSamples , offset ) ; this . Audio . decodeAudioData ( samplesBuffer . buffer , ( function ( decodedData ) { let _id = id ; this . _OnDecodeSuccess ( decodedData , _id ) ; } ) . bind ( this ) , this . _OnDecodeError ) ; } } } private FindAndExtractHeader ( ) { let curpos = 0 ; if ( ! ( ( curpos + 4 ) < this . DataBuffer . length ) ) return ; if ( ! ( this . DataBuffer [ curpos ] == 0x52 && this . DataBuffer [ curpos + 1 ] == 0x49 && this . DataBuffer [ curpos + 2 ] == 0x46 && this . DataBuffer [ curpos + 3 ] == 0x46 ) ) return ; curpos += 8 ; if ( ! ( ( curpos + 4 ) < this . DataBuffer . length ) ) return ; if ( ! ( this . DataBuffer [ curpos ] == 0x57 && this . DataBuffer [ curpos + 1 ] == 0x41 && this . DataBuffer [ curpos + 2 ] == 0x56 && this . DataBuffer [ curpos + 3 ] == 0x45 ) ) return ; curpos += 4 ; if ( ! ( ( curpos + 4 ) < this . DataBuffer . length ) ) return ; if ( ! ( this . DataBuffer [ curpos ] == 0x66 && this . DataBuffer [ curpos + 1 ] == 0x6d && this . DataBuffer [ curpos + 2 ] == 0x74 && this . DataBuffer [ curpos + 3 ] == 0x20 ) ) return ; curpos += 4 ; if ( ! ( ( curpos + 4 ) < this . DataBuffer . length ) ) return ; let subChunkSize = this . DataBuffer [ curpos ] | this . DataBuffer [ curpos + 1 ] << 8 | this . DataBuffer [ curpos + 2 ] << 16 | this . DataBuffer [ curpos + 3 ] << 24 ; if ( ! ( ( curpos + 4 + subChunkSize ) < this . DataBuffer . length ) ) return ; curpos += 6 ; this . WaveChannels = this . DataBuffer [ curpos ] | this . DataBuffer [ curpos + 1 ] << 8 ; curpos += 2 ; this . WaveSampleRate = this . DataBuffer [ curpos ] | this . DataBuffer [ curpos + 1 ] << 8 | this . DataBuffer [ curpos + 2 ] << 16 | this . DataBuffer [ curpos + 3 ] << 24 ; curpos += 8 ; this . WaveBlockAlign = this . DataBuffer [ curpos ] | this . DataBuffer [ curpos + 1 ] << 8 ; curpos += 2 ; this . WaveBitsPerSample = this . DataBuffer [ curpos ] | this . DataBuffer [ curpos + 1 ] << 8 ; this . WaveBytesPerSample = this . WaveBitsPerSample / 8 ; curpos += subChunkSize - 14 ; while ( true ) { if ( ( curpos + 8 ) < this . DataBuffer . length ) { subChunkSize = this . DataBuffer [ curpos + 4 ] | this . DataBuffer [ curpos + 5 ] << 8 | this . DataBuffer [ curpos + 6 ] << 16 | this . DataBuffer [ curpos + 7 ] << 24 ; if ( this . DataBuffer [ curpos ] == 0x64 && this . DataBuffer [ curpos + 1 ] == 0x61 && this . DataBuffer [ curpos + 2 ] == 0x74 && this . DataBuffer [ curpos + 3 ] == 0x61 ) break ; else curpos += 8 + subChunkSize ; } else return ; } curpos += 8 ; this . RiffHeader = new Uint8Array ( this . DataBuffer . buffer . slice ( 0 , curpos ) ) ; this . BatchSamples = Math . ceil ( this . BatchDuration * this . WaveSampleRate ) ; this . ExtraEdgeSamples = Math . ceil ( this . ExtraEdgeDuration * this . WaveSampleRate ) ; this . BatchBytes = this . BatchSamples * this . WaveBlockAlign ; this . TotalBatchSampleSize = ( this . BatchSamples + this . ExtraEdgeSamples ) ; this . TotalBatchByteSize = this . TotalBatchSampleSize * this . WaveBlockAlign ; let chunkSize = this . RiffHeader . length + this . TotalBatchByteSize - 8 ; this . RiffHeader [ 4 ] = chunkSize & 0xFF ; this . RiffHeader [ 5 ] = ( chunkSize & 0xFF00 ) >>> 8 ; this . RiffHeader [ 6 ] = ( chunkSize & 0xFF0000 ) >>> 16 ; this . RiffHeader [ 7 ] = ( chunkSize & 0xFF000000 ) >>> 24 ; this . RiffHeader [ this . RiffHeader . length - 4 ] = ( this . TotalBatchByteSize & 0xFF ) ; this . RiffHeader [ this . RiffHeader . length - 3 ] = ( this . TotalBatchByteSize & 0xFF00 ) >>> 8 ; this . RiffHeader [ this . RiffHeader . length - 2 ] = ( this . TotalBatchByteSize & 0xFF0000 ) >>> 16 ; this . RiffHeader [ this . RiffHeader . length - 1 ] = ( this . TotalBatchByteSize & 0xFF000000 ) >>> 24 ; this . GotHeader = true ; } private CanExtractSamples ( ) { if ( this . DataBuffer . length >= this . TotalBatchByteSize ) return true ; else return false ; } private ExtractIntSamples ( ) { let intSampleArray = new Uint8Array ( this . DataBuffer . buffer . slice ( 0 , this . TotalBatchByteSize ) ) ; this . DataBuffer = new Uint8Array ( this . DataBuffer . buffer . slice ( this . BatchBytes ) ) ; return intSampleArray ; } private readonly _OnDecodeSuccess : ( decodedData : AudioBuffer , id : number ) => void ; private OnDecodeSuccess ( decodedData , id ) { let pickSize = this . BatchDuration * decodedData . sampleRate ; this . SampleBudget += ( pickSize - Math . ceil ( pickSize ) ) ; pickSize = Math . ceil ( pickSize ) ; let pickOffset = ( decodedData . length - pickSize ) / 2.0 ; if ( pickOffset < 0 ) pickOffset = 0 ; else pickOffset = Math . floor ( pickOffset ) ; if ( this . SampleBudget < - 1.0 ) { let correction = - 1.0 * Math . floor ( Math . abs ( this . SampleBudget ) ) ; this . SampleBudget -= correction ; pickSize += correction ; } else if ( this . SampleBudget > 1.0 ) { let correction = Math . floor ( this . SampleBudget ) ; this . SampleBudget -= correction ; pickSize += correction ; } let audioBuffer = this . Audio . createBuffer ( decodedData . numberOfChannels , pickSize , decodedData . sampleRate ) ; for ( let i = 0 ; i < decodedData . numberOfChannels ; i ++ ) audioBuffer . getChannelData ( i ) . set ( decodedData . getChannelData ( i ) . slice ( pickOffset , - pickOffset ) ) ; this . OnDataReady ( id , audioBuffer ) ; } private readonly _OnDecodeError : ( error : DOMException ) => void ; private OnDecodeError ( _error ) { this . ErrorCallback ( ) ; } }