class MPEGFrameInfo { public readonly Data : Uint8Array ; public readonly SampleCount : number ; public readonly SampleRate : number ; constructor ( data , sampleCount , sampleRate ) { this . Data = data ; this . SampleCount = sampleCount ; this . SampleRate = sampleRate ; } } class AudioFormatReader_MPEG extends AudioFormatReader implements IAudioFormatReader { private static readonly MPEG_versions : Array < number > = new Array ( 25 , 0 , 2 , 1 ) ; private static readonly MPEG_layers : Array < number > = new Array ( 0 , 3 , 2 , 1 ) ; private static MPEG_bitrates : Array < Array < Array < number > > > = new Array ( new Array ( new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 0 ) , new Array ( 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 0 ) , new Array ( 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 224 , 256 , 0 ) ) , new Array ( new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) , new Array ( new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 0 ) , new Array ( 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 0 ) , new Array ( 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 224 , 256 , 0 ) ) , new Array ( new Array ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) , new Array ( 0 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , 0 ) , new Array ( 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , 384 , 0 ) , new Array ( 0 , 32 , 64 , 96 , 128 , 160 , 192 , 224 , 256 , 288 , 320 , 352 , 384 , 416 , 448 , 0 ) ) ) ; private static MPEG_srates : Array < Array < number > > = new Array ( new Array ( 11025 , 12000 , 8000 , 0 ) , new Array ( 0 , 0 , 0 , 0 ) , new Array ( 22050 , 24000 , 16000 , 0 ) , new Array ( 44100 , 48000 , 32000 , 0 ) ) ; private static MPEG_frame_samples : Array < Array < number > > = new Array ( new Array ( 0 , 576 , 1152 , 384 ) , new Array ( 0 , 0 , 0 , 0 ) , new Array ( 0 , 576 , 1152 , 384 ) , new Array ( 0 , 1152 , 1152 , 384 ) ) ; private static Id3v2Tag : Uint8Array = new Uint8Array ( new Array ( 0x49 , 0x44 , 0x33 , 0x03 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0D , 0x54 , 0x49 , 0x54 , 0x32 , 0x00 , 0x00 , 0x00 , 0x02 , 0x00 , 0x00 , 0x00 , 0x20 , 0x00 ) ) ; private static readonly MPEG_slot_size : Array < number > = new Array ( 0 , 1 , 1 , 4 ) ; private readonly AddId3Tag : boolean ; private readonly MinDecodeFrames : number ; private Frames : Array < MPEGFrameInfo > ; private FrameStartIdx : number ; private FrameEndIdx : number ; private FrameSamples : number ; private FrameSampleRate : number ; private TimeBudget : number ; constructor ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback , addId3Tag , minDecodeFrames ) { super ( audio , logger , errorCallback , beforeDecodeCheck , dataReadyCallback ) ; this . _OnDecodeSuccess = this . OnDecodeSuccess . bind ( this ) ; this . _OnDecodeError = this . OnDecodeError . bind ( this ) ; this . AddId3Tag = addId3Tag ; this . MinDecodeFrames = minDecodeFrames ; this . Frames = new Array ( ) ; this . FrameStartIdx = - 1 ; this . FrameEndIdx = - 1 ; this . FrameSamples = 0 ; this . FrameSampleRate = 0 ; this . TimeBudget = 0 ; } public PurgeData ( ) { super . PurgeData ( ) ; this . Frames = new Array ( ) ; this . FrameStartIdx = - 1 ; this . FrameEndIdx = - 1 ; this . FrameSamples = 0 ; this . FrameSampleRate = 0 ; this . TimeBudget = 0 ; } protected ExtractAll ( ) { this . FindFrame ( ) ; while ( this . CanExtractFrame ( ) ) { this . Frames . push ( this . ExtractFrame ( ) ) ; this . FindFrame ( ) ; } if ( this . Frames . length >= this . MinDecodeFrames ) { let bufferLength = 0 ; let expectedTotalPlayTime = 0 ; expectedTotalPlayTime += this . Frames [ 0 ] . SampleCount / this . Frames [ 0 ] . SampleRate / 2.0 ; bufferLength += this . Frames [ 0 ] . Data . length ; for ( let i = 1 ; i < this . Frames . length - 1 ; i ++ ) { expectedTotalPlayTime += this . Frames [ i ] . SampleCount / this . Frames [ i ] . SampleRate ; bufferLength += this . Frames [ i ] . Data . length ; } expectedTotalPlayTime += this . Frames [ this . Frames . length - 1 ] . SampleCount / this . Frames [ this . Frames . length - 1 ] . SampleRate / 2.0 ; bufferLength += this . Frames [ this . Frames . length - 1 ] . Data . length ; if ( this . AddId3Tag ) { bufferLength += AudioFormatReader_MPEG . Id3v2Tag . length ; } let decodeBuffer = new Uint8Array ( bufferLength ) ; let offset = 0 ; if ( this . AddId3Tag ) { decodeBuffer . set ( AudioFormatReader_MPEG . Id3v2Tag , offset ) ; offset += AudioFormatReader_MPEG . Id3v2Tag . length ; } for ( let i = 0 ; i < this . Frames . length ; i ++ ) { decodeBuffer . set ( this . Frames [ i ] . Data , offset ) ; offset += this . Frames [ i ] . Data . length ; } this . Frames . splice ( 0 , this . Frames . length - 1 ) ; let id = this . Id ++ ; if ( ! this . OnBeforeDecode ( id , expectedTotalPlayTime ) ) return ; this . Audio . decodeAudioData ( decodeBuffer . buffer , ( function ( decodedData ) { let _id = id ; let _expectedTotalPlayTime = expectedTotalPlayTime ; this . _OnDecodeSuccess ( decodedData , _id , _expectedTotalPlayTime ) ; } ) . bind ( this ) , this . _OnDecodeError . bind ( this ) ) ; } } private FindFrame ( ) { if ( this . FrameStartIdx < 0 ) { let i = 0 ; while ( ( i + 1 ) < this . DataBuffer . length ) { if ( this . DataBuffer [ i ] == 0xFF && ( this . DataBuffer [ i + 1 ] & 0xE0 ) == 0xE0 ) { this . FrameStartIdx = i ; break ; } i ++ ; } } if ( this . FrameStartIdx >= 0 && this . FrameEndIdx < 0 ) { if ( ( this . FrameStartIdx + 2 ) < this . DataBuffer . length ) { let ver = ( this . DataBuffer [ this . FrameStartIdx + 1 ] & 0x18 ) >>> 3 ; let lyr = ( this . DataBuffer [ this . FrameStartIdx + 1 ] & 0x06 ) >>> 1 ; let pad = ( this . DataBuffer [ this . FrameStartIdx + 2 ] & 0x02 ) >>> 1 ; let brx = ( this . DataBuffer [ this . FrameStartIdx + 2 ] & 0xF0 ) >>> 4 ; let srx = ( this . DataBuffer [ this . FrameStartIdx + 2 ] & 0x0C ) >>> 2 ; let bitrate = AudioFormatReader_MPEG . MPEG_bitrates [ ver ] [ lyr ] [ brx ] * 1000 ; let samprate = AudioFormatReader_MPEG . MPEG_srates [ ver ] [ srx ] ; let samples = AudioFormatReader_MPEG . MPEG_frame_samples [ ver ] [ lyr ] ; let slot_size = AudioFormatReader_MPEG . MPEG_slot_size [ lyr ] ; let bps = samples / 8.0 ; let fsize = ( ( bps * bitrate ) / samprate ) + ( ( pad == 1 ) ? slot_size : 0 ) ; let frameSize = Math . floor ( fsize ) ; this . FrameSamples = samples ; this . FrameSampleRate = samprate ; this . FrameEndIdx = this . FrameStartIdx + frameSize ; } } } private CanExtractFrame ( ) { if ( this . FrameStartIdx < 0 || this . FrameEndIdx < 0 ) return false ; else if ( this . FrameEndIdx <= this . DataBuffer . length ) return true ; else return false ; } private ExtractFrame ( ) { let frameArray = this . DataBuffer . buffer . slice ( this . FrameStartIdx , this . FrameEndIdx ) ; if ( ( this . FrameEndIdx + 1 ) < this . DataBuffer . length ) this . DataBuffer = new Uint8Array ( this . DataBuffer . buffer . slice ( this . FrameEndIdx ) ) ; else this . DataBuffer = new Uint8Array ( 0 ) ; this . FrameStartIdx = 0 ; this . FrameEndIdx = - 1 ; return new MPEGFrameInfo ( new Uint8Array ( frameArray ) , this . FrameSamples , this . FrameSampleRate ) ; } private readonly _OnDecodeSuccess : ( decodedData : AudioBuffer , id : number , expectedTotalPlayTime : number ) => void ; private OnDecodeSuccess ( decodedData , id , expectedTotalPlayTime ) { let extractSampleCount ; let extractSampleOffset ; if ( expectedTotalPlayTime > decodedData . duration ) { extractSampleCount = decodedData . length ; extractSampleOffset = 0 ; this . TimeBudget += ( expectedTotalPlayTime - decodedData . duration ) ; } else if ( expectedTotalPlayTime < decodedData . duration ) { extractSampleCount = Math . ceil ( expectedTotalPlayTime * decodedData . sampleRate ) ; let budgetSamples = this . TimeBudget * decodedData . sampleRate ; if ( budgetSamples > 1.0 ) { if ( budgetSamples > decodedData . length - extractSampleCount ) { budgetSamples = decodedData . length - extractSampleCount ; } extractSampleCount += budgetSamples ; this . TimeBudget -= ( budgetSamples / decodedData . sampleRate ) ; } extractSampleOffset = Math . floor ( ( decodedData . length - extractSampleCount ) / 2 ) ; } else { extractSampleCount = decodedData . length ; extractSampleOffset = 0 ; } let audioBuffer = this . Audio . createBuffer ( decodedData . numberOfChannels , extractSampleCount , decodedData . sampleRate ) ; for ( let i = 0 ; i < decodedData . numberOfChannels ; i ++ ) audioBuffer . getChannelData ( i ) . set ( decodedData . getChannelData ( i ) . subarray ( extractSampleOffset , extractSampleOffset + extractSampleCount ) ) ; this . OnDataReady ( id , audioBuffer ) ; } private readonly _OnDecodeError : ( error : DOMException ) => void ; private OnDecodeError ( _error ) { this . ErrorCallback ( ) ; } }