function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch = true, $ScanAsCBR = false, $FastMPEGheaderScan = false) { static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; static $MPEGaudioFrequencyLookup; static $MPEGaudioChannelModeLookup; static $MPEGaudioModeExtensionLookup; static $MPEGaudioEmphasisLookup; if (empty($MPEGaudioVersionLookup)) { $MPEGaudioVersionLookup = MPEGaudioVersionArray(); $MPEGaudioLayerLookup = MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); $MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); $MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray(); $MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray(); $MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray(); } if ($offset >= $ThisFileInfo['avdataend']) { $ThisFileInfo['error'] .= "\n" . 'end of file encounter looking for MPEG synch'; return false; } fseek($fd, $offset, SEEK_SET); $headerstring = fread($fd, 1441); // worse-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame // MP3 audio frame structure: // $aa $aa $aa $aa [$bb $bb] $cc... // where $aa..$aa is the four-byte mpeg-audio header (below) // $bb $bb is the optional 2-byte CRC // and $cc... is the audio data $head4 = substr($headerstring, 0, 4); static $MPEGaudioHeaderDecodeCache = array(); if (isset($MPEGaudioHeaderDecodeCache[$head4])) { $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; } else { $MPEGheaderRawArray = MPEGaudioHeaderDecode($head4); $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; } static $MPEGaudioHeaderValidCache = array(); // Not in cache if (!isset($MPEGaudioHeaderValidCache[$head4])) { $MPEGaudioHeaderValidCache[$head4] = MPEGaudioHeaderValid($MPEGheaderRawArray); } if ($MPEGaudioHeaderValidCache[$head4]) { $ThisFileInfo['mpeg']['audio']['raw'] = $MPEGheaderRawArray; } else { $ThisFileInfo['error'] .= "\n" . 'Invalid MPEG audio header at offset ' . $offset; return false; } if (!$FastMPEGheaderScan) { $ThisFileInfo['mpeg']['audio']['version'] = $MPEGaudioVersionLookup[$ThisFileInfo['mpeg']['audio']['raw']['version']]; $ThisFileInfo['mpeg']['audio']['layer'] = $MPEGaudioLayerLookup[$ThisFileInfo['mpeg']['audio']['raw']['layer']]; $ThisFileInfo['mpeg']['audio']['channelmode'] = $MPEGaudioChannelModeLookup[$ThisFileInfo['mpeg']['audio']['raw']['channelmode']]; $ThisFileInfo['mpeg']['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono' ? 1 : 2; $ThisFileInfo['mpeg']['audio']['sample_rate'] = $MPEGaudioFrequencyLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['raw']['sample_rate']]; $ThisFileInfo['mpeg']['audio']['protection'] = !$ThisFileInfo['mpeg']['audio']['raw']['protection']; $ThisFileInfo['mpeg']['audio']['private'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['private']; $ThisFileInfo['mpeg']['audio']['modeextension'] = $MPEGaudioModeExtensionLookup[$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['modeextension']]; $ThisFileInfo['mpeg']['audio']['copyright'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['copyright']; $ThisFileInfo['mpeg']['audio']['original'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['original']; $ThisFileInfo['mpeg']['audio']['emphasis'] = $MPEGaudioEmphasisLookup[$ThisFileInfo['mpeg']['audio']['raw']['emphasis']]; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; if ($ThisFileInfo['mpeg']['audio']['protection']) { $ThisFileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); } } if ($ThisFileInfo['mpeg']['audio']['raw']['bitrate'] == 15) { // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 $ThisFileInfo['warning'] .= "\n" . 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; $ThisFileInfo['mpeg']['audio']['raw']['bitrate'] = 0; } $ThisFileInfo['mpeg']['audio']['padding'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['padding']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $MPEGaudioBitrateLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['bitrate']]; if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' && $offset == $ThisFileInfo['avdataoffset']) { // only skip multiple frame check if free-format bitstream found at beginning of file // otherwise is quite possibly simply corrupted data $recursivesearch = false; } // For Layer II there are some combinations of bitrate and mode which are not allowed. if (!$FastMPEGheaderScan && $ThisFileInfo['mpeg']['audio']['layer'] == 'II') { $ThisFileInfo['audio']['dataformat'] = 'mp2'; switch ($ThisFileInfo['mpeg']['audio']['channelmode']) { case 'mono': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] <= 192) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; case 'stereo': case 'joint stereo': case 'dual channel': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] == 64 || $ThisFileInfo['mpeg']['audio']['bitrate'] >= 96) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; } } if ($ThisFileInfo['audio']['sample_rate'] > 0) { $ThisFileInfo['mpeg']['audio']['framelength'] = MPEGaudioFrameLength($ThisFileInfo['mpeg']['audio']['bitrate'], $ThisFileInfo['mpeg']['audio']['version'], $ThisFileInfo['mpeg']['audio']['layer'], (int) $ThisFileInfo['mpeg']['audio']['padding'], $ThisFileInfo['audio']['sample_rate']); } if ($ThisFileInfo['mpeg']['audio']['bitrate'] != 'free') { $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['bitrate']; if (isset($ThisFileInfo['mpeg']['audio']['framelength'])) { $nextframetestoffset = $offset + $ThisFileInfo['mpeg']['audio']['framelength']; } else { $ThisFileInfo['error'] .= "\n" . 'Frame at offset(' . $offset . ') is has an invalid frame length.'; return false; } } $ExpectedNumberOfAudioBytes = 0; //////////////////////////////////////////////////////////////////////////////////// // Variable-bitrate headers if (substr($headerstring, 4 + 32, 4) == 'VBRI') { // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer'; $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; $SideInfoData = substr($headerstring, 4 + 2, 32); $FraunhoferVBROffset = 36; $ThisFileInfo['mpeg']['audio']['VBR_encoder_version'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); $ThisFileInfo['mpeg']['audio']['VBR_encoder_delay'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); $ThisFileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); //$ThisFileInfo['mpeg']['audio']['reserved'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 4)); // hardcoded $00 $01 $00 $02 - purpose unknown $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets_stride'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); $ExpectedNumberOfAudioBytes = $ThisFileInfo['mpeg']['audio']['VBR_bytes']; $previousbyteoffset = $offset; for ($i = 0; $i < $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']; $i++) { $Fraunhofer_OffsetN = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, 2)); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_offsets_relative'][$i] = $Fraunhofer_OffsetN; $ThisFileInfo['mpeg']['audio']['VBR_offsets_absolute'][$i] = $Fraunhofer_OffsetN + $previousbyteoffset; $previousbyteoffset += $Fraunhofer_OffsetN; } } else { // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) // depending on MPEG layer and number of channels if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-1 (mono) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } else { // MPEG-1 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 32; // 0x24 $SideInfoData = substr($headerstring, 4 + 2, 32); } } else { // 2 or 2.5 if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-2, MPEG-2.5 (mono) $VBRidOffset = 4 + 9; // 0x0D $SideInfoData = substr($headerstring, 4 + 2, 9); } else { // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } } if (substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing' || substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info') { // 'Xing' is traditional Xing VBR frame // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Xing'; $ThisFileInfo['mpeg']['audio']['xing_flags_raw'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); $ThisFileInfo['mpeg']['audio']['xing_flags']['frames'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x1); $ThisFileInfo['mpeg']['audio']['xing_flags']['bytes'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x2); $ThisFileInfo['mpeg']['audio']['xing_flags']['toc'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x4); $ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x8); if ($ThisFileInfo['mpeg']['audio']['xing_flags']['frames']) { $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']) { $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); } if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' && !empty($ThisFileInfo['mpeg']['audio']['VBR_frames']) && !empty($ThisFileInfo['mpeg']['audio']['VBR_bytes'])) { $framelengthfloat = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']; if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4 - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate'] / 12; } else { // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate'] / 144; } $ThisFileInfo['mpeg']['audio']['framelength'] = floor($framelengthfloat); } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['toc']) { $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); for ($i = 0; $i < 100; $i++) { $ThisFileInfo['mpeg']['audio']['toc'][$i] = ord($LAMEtocData[$i]); } } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale']) { $ThisFileInfo['mpeg']['audio']['VBR_scale'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); } // http://gabriel.mp3-tech.org/mp3infotag.html if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { $ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); $ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], 0, 9); $ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "Uª"); if ($ThisFileInfo['mpeg']['audio']['LAME']['short_version'] >= 'LAME3.90.') { // It the LAME tag was only introduced in LAME v3.90 // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html // are assuming a 'Xing' identifier offset of 0x24, which is the case for // MPEG-1 non-mono, but not for other combinations $LAMEtagOffsetContant = $VBRidOffset - 0x24; // byte $9B VBR Quality // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. // Actually overwrites original Xing bytes unset($ThisFileInfo['mpeg']['audio']['VBR_scale']); $ThisFileInfo['mpeg']['audio']['LAME']['vbr_quality'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9b, 1)); // bytes $9C-$A4 Encoder short VersionString $ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9c, 9); $ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; // byte $A5 Info Tag revision + VBR method $LAMEtagRevisionVBRmethod = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xa5, 1)); $ThisFileInfo['mpeg']['audio']['LAME']['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xf0) >> 4; $ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] = $LAMEtagRevisionVBRmethod & 0xf; $ThisFileInfo['mpeg']['audio']['LAME']['vbr_method'] = LAMEvbrMethodLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method']); // byte $A6 Lowpass filter value $ThisFileInfo['mpeg']['audio']['LAME']['lowpass_frequency'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xa6, 1)) * 100; // bytes $A7-$AE Replay Gain // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = BigEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xa7, 4)); $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xab, 2)); $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xad, 2)); if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] == 0) { $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = false; } if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] != 0) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0xe000) >> 13; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x1c00) >> 10; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x200) >> 9; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x1ff; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit']); if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) { $ThisFileInfo['replay_gain']['radio']['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; } $ThisFileInfo['replay_gain']['radio']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator']; $ThisFileInfo['replay_gain']['radio']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db']; } if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] != 0) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0xe000) >> 13; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x1c00) >> 10; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x200) >> 9; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x1ff; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit']); if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) { $ThisFileInfo['replay_gain']['audiophile']['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; } $ThisFileInfo['replay_gain']['audiophile']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator']; $ThisFileInfo['replay_gain']['audiophile']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db']; } // byte $AF Encoding flags + ATH Type $EncodingFlagsATHtype = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xaf, 1)); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); $ThisFileInfo['mpeg']['audio']['LAME']['ath_type'] = $EncodingFlagsATHtype & 0xf; // byte $B0 if ABR {specified bitrate} else {minimal bitrate} $ABRbitrateMinBitrate = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb0, 1)); if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 2) { // Average BitRate (ABR) $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_abr'] = $ABRbitrateMinBitrate; } elseif ($ABRbitrateMinBitrate > 0) { // Variable BitRate (VBR) - minimum bitrate $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] = $ABRbitrateMinBitrate; } // bytes $B1-$B3 Encoder delays $EncoderDelays = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb1, 3)); $ThisFileInfo['mpeg']['audio']['LAME']['encoder_delay'] = ($EncoderDelays & 0xfff000) >> 12; $ThisFileInfo['mpeg']['audio']['LAME']['end_padding'] = $EncoderDelays & 0xfff; // byte $B4 Misc $MiscByte = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb4, 1)); $ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping'] = $MiscByte & 0x3; $ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode'] = ($MiscByte & 0x1c) >> 2; $ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; $ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq'] = ($MiscByte & 0xc0) >> 6; $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping']; $ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode'] = LAMEmiscStereoModeLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode']); $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality'] = (bool) $ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality']; $ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq'] = LAMEmiscSourceSampleFrequencyLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq']); // byte $B5 MP3 Gain $ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb5, 1), false, true); $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] = 1.5 * $ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain']; $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_factor'] = pow(2, $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] / 6); // bytes $B6-$B7 Preset and surround info $PresetSurroundBytes = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb6, 2)); // Reserved = ($PresetSurroundBytes & 0xC000); $ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info'] = $PresetSurroundBytes & 0x3800; $ThisFileInfo['mpeg']['audio']['LAME']['surround_info'] = LAMEsurroundInfoLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info']); $ThisFileInfo['mpeg']['audio']['LAME']['preset_used_id'] = $PresetSurroundBytes & 0x7ff; // bytes $B8-$BB MusicLength $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xb8, 4)); $ExpectedNumberOfAudioBytes = $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] > 0 ? $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] : $ThisFileInfo['mpeg']['audio']['VBR_bytes']; // bytes $BC-$BD MusicCRC $ThisFileInfo['mpeg']['audio']['LAME']['music_crc'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xbc, 2)); // bytes $BE-$BF CRC-16 of Info Tag $ThisFileInfo['mpeg']['audio']['LAME']['lame_tag_crc'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xbe, 2)); // LAME CBR if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 1) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if (empty($ThisFileInfo['mpeg']['audio']['bitrate']) || $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] != 255) { $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min']; } } } } } else { // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if ($recursivesearch) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; if (RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { $recursivesearch = false; $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') { $ThisFileInfo['warning'] .= "\n" . 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; } } } } if ($ExpectedNumberOfAudioBytes > 0 && $ExpectedNumberOfAudioBytes != $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) { if ($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) == 1) { $ThisFileInfo['warning'] .= "\n" . 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; } elseif ($ExpectedNumberOfAudioBytes > $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) { $ThisFileInfo['warning'] .= "\n" . 'Probable truncated file: expecting ' . $ExpectedNumberOfAudioBytes . ' bytes of audio data, only found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) . ' (short by ' . ($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) . ' bytes)'; } else { $ThisFileInfo['warning'] .= "\n" . 'Too much data in file: expecting ' . $ExpectedNumberOfAudioBytes . ' bytes of audio data, found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) . ' (' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - $ExpectedNumberOfAudioBytes) . ' bytes too many)'; } } if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' && empty($ThisFileInfo['audio']['bitrate'])) { if ($offset == $ThisFileInfo['avdataoffset'] && empty($ThisFileInfo['mpeg']['audio']['VBR_frames'])) { $framebytelength = FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); if ($framebytelength > 0) { $ThisFileInfo['mpeg']['audio']['framelength'] = $framebytelength; if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 $ThisFileInfo['audio']['bitrate'] = ($framebytelength / 4 - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate'] / 12; } else { // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 $ThisFileInfo['audio']['bitrate'] = ($framebytelength - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate'] / 144; } } else { $ThisFileInfo['error'] .= "\n" . 'Error calculating frame length of free-format MP3 without Xing/LAME header'; } } } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr' && isset($ThisFileInfo['mpeg']['audio']['VBR_frames']) && $ThisFileInfo['mpeg']['audio']['VBR_frames'] > 1) { $ThisFileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame if ($ThisFileInfo['mpeg']['audio']['version'] == '1' && $ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 384) / 1000; } elseif (($ThisFileInfo['mpeg']['audio']['version'] == '2' || $ThisFileInfo['mpeg']['audio']['version'] == '2.5') && $ThisFileInfo['mpeg']['audio']['layer'] == 'III') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 576) / 1000; } else { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 1152) / 1000; } if ($ThisFileInfo['mpeg']['audio']['VBR_bitrate'] > 0) { $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; // to avoid confusion } } // End variable-bitrate headers //////////////////////////////////////////////////////////////////////////////////// if ($recursivesearch) { if (!RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { return false; } } //if (false) { // // experimental side info parsing section - not returning anything useful yet // // $SideInfoBitstream = BigEndian2Bin($SideInfoData); // $SideInfoOffset = 0; // // if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { // if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // // MPEG-1 (mono) // $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); // $SideInfoOffset += 9; // $SideInfoOffset += 5; // } else { // // MPEG-1 (stereo, joint-stereo, dual-channel) // $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); // $SideInfoOffset += 9; // $SideInfoOffset += 3; // } // } else { // 2 or 2.5 // if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // // MPEG-2, MPEG-2.5 (mono) // $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); // $SideInfoOffset += 8; // $SideInfoOffset += 1; // } else { // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) // $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); // $SideInfoOffset += 8; // $SideInfoOffset += 2; // } // } // // if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { // $ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 2; // } // } // } // for ($granule = 0; $granule < (($ThisFileInfo['mpeg']['audio']['version'] == '1') ? 2 : 1); $granule++) { // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { // $ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); // $SideInfoOffset += 12; // $ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); // $SideInfoOffset += 9; // $ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); // $SideInfoOffset += 8; // if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { // $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); // $SideInfoOffset += 4; // } else { // $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); // $SideInfoOffset += 9; // } // $ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 1; // // if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') { // // $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); // $SideInfoOffset += 2; // $ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 1; // // for ($region = 0; $region < 2; $region++) { // $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); // $SideInfoOffset += 5; // } // $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0; // // for ($window = 0; $window < 3; $window++) { // $ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); // $SideInfoOffset += 3; // } // // } else { // // for ($region = 0; $region < 3; $region++) { // $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); // $SideInfoOffset += 5; // } // // $ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); // $SideInfoOffset += 4; // $ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); // $SideInfoOffset += 3; // $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0; // } // // if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { // $ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 1; // } // $ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 1; // $ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); // $SideInfoOffset += 1; // } // } //} return true; }
function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch = true, $ScanAsCBR = false, $FastMPEGheaderScan = false) { static $MPEGaudioVersionLookup; static $MPEGaudioLayerLookup; static $MPEGaudioBitrateLookup; static $MPEGaudioFrequencyLookup; static $MPEGaudioChannelModeLookup; static $MPEGaudioModeExtensionLookup; static $MPEGaudioEmphasisLookup; if (empty($MPEGaudioVersionLookup)) { $MPEGaudioVersionLookup = MPEGaudioVersionArray(); $MPEGaudioLayerLookup = MPEGaudioLayerArray(); $MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); $MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); $MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray(); $MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray(); $MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray(); } if ($offset >= $ThisFileInfo['avdataend']) { $ThisFileInfo['error'] .= "\n" . 'end of file encounter looking for MPEG synch'; return false; } fseek($fd, $offset, SEEK_SET); $headerstring = fread($fd, 192); // MP3 audio frame structure: // $aa $aa $aa $aa [$bb $bb] $cc... // where $aa..$aa is the four-byte mpeg-audio header (below) // $bb $bb is the optional 2-byte CRC // and $cc... is the audio data $head4 = substr($headerstring, 0, 4); static $MPEGaudioHeaderDecodeCache = array(); if (isset($MPEGaudioHeaderDecodeCache[$head4])) { $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; } else { $MPEGheaderRawArray = MPEGaudioHeaderDecode($head4); $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; } static $MPEGaudioHeaderValidCache = array(); // Not in cache if (!isset($MPEGaudioHeaderValidCache[$head4])) { $MPEGaudioHeaderValidCache[$head4] = MPEGaudioHeaderValid($MPEGheaderRawArray); } if ($MPEGaudioHeaderValidCache[$head4]) { $ThisFileInfo['mpeg']['audio']['raw'] = $MPEGheaderRawArray; } else { $ThisFileInfo['error'] .= "\n" . 'Invalid MPEG audio header at offset ' . $offset; return false; } if (!$FastMPEGheaderScan) { $ThisFileInfo['mpeg']['audio']['version'] = $MPEGaudioVersionLookup[$ThisFileInfo['mpeg']['audio']['raw']['version']]; $ThisFileInfo['mpeg']['audio']['layer'] = $MPEGaudioLayerLookup[$ThisFileInfo['mpeg']['audio']['raw']['layer']]; $ThisFileInfo['mpeg']['audio']['channelmode'] = $MPEGaudioChannelModeLookup[$ThisFileInfo['mpeg']['audio']['raw']['channelmode']]; $ThisFileInfo['mpeg']['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono' ? 1 : 2; $ThisFileInfo['mpeg']['audio']['sample_rate'] = $MPEGaudioFrequencyLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['raw']['sample_rate']]; $ThisFileInfo['mpeg']['audio']['protection'] = !$ThisFileInfo['mpeg']['audio']['raw']['protection']; $ThisFileInfo['mpeg']['audio']['private'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['private']; $ThisFileInfo['mpeg']['audio']['modeextension'] = $MPEGaudioModeExtensionLookup[$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['modeextension']]; $ThisFileInfo['mpeg']['audio']['copyright'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['copyright']; $ThisFileInfo['mpeg']['audio']['original'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['original']; $ThisFileInfo['mpeg']['audio']['emphasis'] = $MPEGaudioEmphasisLookup[$ThisFileInfo['mpeg']['audio']['raw']['emphasis']]; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; if ($ThisFileInfo['mpeg']['audio']['protection']) { $ThisFileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); } } $ThisFileInfo['mpeg']['audio']['padding'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['padding']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $MPEGaudioBitrateLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['bitrate']]; // For Layer II there are some combinations of bitrate and mode which are not allowed. if (!$FastMPEGheaderScan && $ThisFileInfo['mpeg']['audio']['layer'] == 'II') { //$ThisFileInfo['fileformat'] = 'mp2'; $ThisFileInfo['audio']['dataformat'] = 'mp2'; switch ($ThisFileInfo['mpeg']['audio']['channelmode']) { case 'mono': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] <= 192) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; case 'stereo': case 'joint stereo': case 'dual channel': if ($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free' || $ThisFileInfo['mpeg']['audio']['bitrate'] == 64 || $ThisFileInfo['mpeg']['audio']['bitrate'] >= 96) { // these are ok } else { $ThisFileInfo['error'] .= "\n" . $ThisFileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $ThisFileInfo['mpeg']['audio']['channelmode'] . '.'; return false; } break; } } if ($ThisFileInfo['mpeg']['audio']['bitrate'] != 'free') { if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 48; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 4 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } else { // Layer II / III $FrameLengthCoefficient = 144; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 1 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } } else { // MPEG-2 / MPEG-2.5 if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 24; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 4 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } else { // Layer II / III $FrameLengthCoefficient = 72; $FrameLengthPadding = $ThisFileInfo['mpeg']['audio']['padding'] ? 1 : 0; // For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. } } // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding // http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068 // -> [Finding the next frame synch] on www.r3mix.net forums if the above link goes dead if ($ThisFileInfo['audio']['sample_rate'] > 0) { $ThisFileInfo['mpeg']['audio']['framelength'] = (int) floor($FrameLengthCoefficient * 1000 * $ThisFileInfo['mpeg']['audio']['bitrate'] / $ThisFileInfo['audio']['sample_rate']) + $FrameLengthPadding; } } $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['bitrate']; if (isset($ThisFileInfo['mpeg']['audio']['framelength'])) { $nextframetestoffset = $offset + $ThisFileInfo['mpeg']['audio']['framelength']; } else { $ThisFileInfo['error'] .= "\n" . 'Frame at offset(' . $offset . ') is has an invalid frame length.'; return false; } //////////////////////////////////////////////////////////////////////////////////// // Variable-bitrate headers if (substr($headerstring, 4 + 32, 4) == 'VBRI') { // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer'; $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; $SideInfoData = substr($headerstring, 4 + 2, 32); $FraunhoferVBROffset = 4 + 32 + strlen('VBRI'); $Fraunhofer_EncoderVersion = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_encoder_version'] = BigEndian2Int($Fraunhofer_EncoderVersion); $Fraunhofer_EncoderDelay = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_encoder_delay'] = BigEndian2Int($Fraunhofer_EncoderDelay); $Fraunhofer_quality = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int($Fraunhofer_quality); $Fraunhofer_Bytes = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($Fraunhofer_Bytes); $Fraunhofer_Frames = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($Fraunhofer_Frames); $Fraunhofer_SeekOffsets = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets'] = BigEndian2Int($Fraunhofer_SeekOffsets); $FraunhoferVBROffset += 4; // hardcoded $00 $01 $00 $02 - purpose unknown $Fraunhofer_OffsetStride = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets_stride'] = BigEndian2Int($Fraunhofer_OffsetStride); $previousbyteoffset = $offset; for ($i = 0; $i < $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']; $i++) { $Fraunhofer_OffsetN = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, 2)); $FraunhoferVBROffset += 2; $ThisFileInfo['mpeg']['audio']['VBR_offsets_relative'][$i] = $Fraunhofer_OffsetN; $ThisFileInfo['mpeg']['audio']['VBR_offsets_absolute'][$i] = $Fraunhofer_OffsetN + $previousbyteoffset; $previousbyteoffset += $Fraunhofer_OffsetN; } } else { // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) // depending on MPEG layer and number of channels if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-1 (mono) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } else { // MPEG-1 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 32; // 0x24 $SideInfoData = substr($headerstring, 4 + 2, 32); } } else { // 2 or 2.5 if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-2, MPEG-2.5 (mono) $VBRidOffset = 4 + 9; // 0x0D $SideInfoData = substr($headerstring, 4 + 2, 9); } else { // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) $VBRidOffset = 4 + 17; // 0x15 $SideInfoData = substr($headerstring, 4 + 2, 17); } } if (substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing' || substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info') { // 'Xing' is traditional Xing VBR frame, 'Info' is LAME-encoded CBR // 'This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.' $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Xing'; $XingVBROffset = $VBRidOffset + strlen('Xing'); $ThisFileInfo['mpeg']['audio']['xing_flags_raw'] = substr($headerstring, $XingVBROffset, 4); $XingVBROffset += 4; $XingHeader_byte4 = BigEndian2Bin(substr($ThisFileInfo['mpeg']['audio']['xing_flags_raw'], 3, 1)); $ThisFileInfo['mpeg']['audio']['xing_flags']['frames'] = (bool) $XingHeader_byte4[4]; $ThisFileInfo['mpeg']['audio']['xing_flags']['bytes'] = (bool) $XingHeader_byte4[5]; $ThisFileInfo['mpeg']['audio']['xing_flags']['toc'] = (bool) $XingHeader_byte4[6]; $ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale'] = (bool) $XingHeader_byte4[7]; if ($ThisFileInfo['mpeg']['audio']['xing_flags']['frames']) { $ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']) { $ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['toc']) { $LAMEtocData = substr($headerstring, $XingVBROffset, 100); $XingVBROffset += 100; for ($i = 0; $i < 100; $i++) { $ThisFileInfo['mpeg']['audio']['toc'][$i] = ord($LAMEtocData[$i]); } } if ($ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale']) { $ThisFileInfo['mpeg']['audio']['VBR_scale'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; } if (substr($headerstring, $XingVBROffset, 4) == 'LAME') { $ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($headerstring, $XingVBROffset, 9); $XingVBROffset += 9; $LAMEtagRevisionVBRmethod = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xf0) >> 4; $ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] = $LAMEtagRevisionVBRmethod & 0xf; $ThisFileInfo['mpeg']['audio']['LAME']['vbr_method'] = LAMEvbrMethodLookup($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['lowpass_frequency'] = 100 * BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = BigEndian2Float(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; $RadioReplayGainRaw = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 4; $ReplayGainID = ($RadioReplayGainRaw & 0xe000) >> 13; $ReplayGainNameKey = ''; switch ($ReplayGainID) { case 1: $ReplayGainNameKey = 'radio'; break; case 2: $ReplayGainNameKey = 'audiophile'; break; case 0: // replay gain not set // replay gain not set default: // reserved break; } if ($ReplayGainNameKey) { require_once GETID3_INCLUDEPATH . 'getid3.rgad.php'; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['name'] = ($RadioReplayGainRaw & 0xe000) >> 13; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['originator'] = ($RadioReplayGainRaw & 0x1c00) >> 10; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['sign_bit'] = ($RadioReplayGainRaw & 0x200) >> 9; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['gain_adjust'] = $RadioReplayGainRaw & 0x1ff; $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['name']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['originator']); $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['raw']['sign_bit']); $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['originator']; $ThisFileInfo['replay_gain']["{$ReplayGainNameKey}"]['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']["{$ReplayGainNameKey}"]['gain_db']; } $EncodingFlagsATHtype = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); $ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); $ThisFileInfo['mpeg']['audio']['LAME']['ath_type'] = $EncodingFlagsATHtype & 0xf; $ABRbitrateMinBitrate = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; if ($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] == 2) { // Average BitRate (ABR) $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_abr'] = $ABRbitrateMinBitrate; } elseif ($ABRbitrateMinBitrate > 0) { // Variable BitRate (VBR) - minimum bitrate $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] = $ABRbitrateMinBitrate; } $EncoderDelays = BigEndian2Int(substr($headerstring, $XingVBROffset, 3)); $XingVBROffset += 3; $ThisFileInfo['mpeg']['audio']['LAME']['encoder_delay'] = ($EncoderDelays & 0xfff000) >> 12; $ThisFileInfo['mpeg']['audio']['LAME']['end_padding'] = $EncoderDelays & 0xfff; $MiscByte = BigEndian2Int(substr($headerstring, $XingVBROffset, 1)); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping_raw'] = $EncodingFlagsATHtype & 0x3; $ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode_raw'] = ($EncodingFlagsATHtype & 0x1c) >> 2; $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality_raw'] = ($EncodingFlagsATHtype & 0x20) >> 5; $ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq_raw'] = ($EncodingFlagsATHtype & 0xc0) >> 6; $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping'] = $ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode'] = LAMEmiscStereoModeLookup($ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality'] = (bool) $ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq'] = LAMEmiscSourceSampleFrequencyLookup($ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq_raw']); $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_raw'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 1), false, true); $XingVBROffset += 1; $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] = 1.5 * $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_raw']; $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_factor'] = pow(2, $ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] / 6); $PresetSurroundBytes = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); //$Reserved = ($PresetSurroundBytes & 0xC000); $ThisFileInfo['mpeg']['audio']['LAME']['surround_info_id'] = $PresetSurroundBytes & 0x3800; $ThisFileInfo['mpeg']['audio']['LAME']['surround_info'] = LAMEsurroundInfoLookup($ThisFileInfo['mpeg']['audio']['LAME']['surround_info_id']); $ThisFileInfo['mpeg']['audio']['LAME']['preset_used_id'] = $PresetSurroundBytes & 0x7ff; $XingVBROffset += 2; $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 4)); $XingVBROffset += 4; if ($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] > $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) { $ThisFileInfo['warning'] .= "\n" . 'Probable truncated file: expecting ' . $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] . ' bytes of audio data, only found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); } $ThisFileInfo['mpeg']['audio']['LAME']['music_crc'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 2; $ThisFileInfo['mpeg']['audio']['LAME']['lame_tag_crc'] = BigEndian2Int(substr($headerstring, $XingVBROffset, 2)); $XingVBROffset += 2; // LAME CBR if ($ThisFileInfo['mpeg']['audio']['LAME']['vbr_method_raw'] == 1) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if (empty($ThisFileInfo['mpeg']['audio']['bitrate']) || $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] != 255) { $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min']; } } } } else { // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; if ($recursivesearch) { $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; if (RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { $recursivesearch = false; $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') { $ThisFileInfo['warning'] .= "\n" . 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; } } } } if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr' && isset($ThisFileInfo['mpeg']['audio']['VBR_frames']) && $ThisFileInfo['mpeg']['audio']['VBR_frames'] > 1) { $ThisFileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame if ($ThisFileInfo['mpeg']['audio']['version'] == '1' && $ThisFileInfo['mpeg']['audio']['layer'] == 'I') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 384) / 1000; } elseif (($ThisFileInfo['mpeg']['audio']['version'] == '2' || $ThisFileInfo['mpeg']['audio']['version'] == '2.5') && $ThisFileInfo['mpeg']['audio']['layer'] == 'III') { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 576) / 1000; } else { $ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($ThisFileInfo['audio']['sample_rate'] / 1152) / 1000; } if ($ThisFileInfo['mpeg']['audio']['VBR_bitrate'] > 0) { $ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; $ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; // to avoid confusion } } // End variable-bitrate headers //////////////////////////////////////////////////////////////////////////////////// if ($recursivesearch) { if (!RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { return false; } } if (false) { // experimental side info parsing section - not returning anything useful yet $SideInfoBitstream = BigEndian2Bin($SideInfoData); $SideInfoOffset = 0; if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-1 (mono) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $SideInfoOffset += 5; } else { // MPEG-1 (stereo, joint-stereo, dual-channel) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $SideInfoOffset += 3; } } else { // 2 or 2.5 if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { // MPEG-2, MPEG-2.5 (mono) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; $SideInfoOffset += 1; } else { // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; $SideInfoOffset += 2; } } if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { $ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 2; } } } for ($granule = 0; $granule < ($ThisFileInfo['mpeg']['audio']['version'] == '1' ? 2 : 1); $granule++) { for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { $ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); $SideInfoOffset += 12; $ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; $ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); $SideInfoOffset += 8; if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); $SideInfoOffset += 4; } else { $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); $SideInfoOffset += 9; } $ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') { $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); $SideInfoOffset += 2; $ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; for ($region = 0; $region < 2; $region++) { $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); $SideInfoOffset += 5; } $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0; for ($window = 0; $window < 3; $window++) { $ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); $SideInfoOffset += 3; } } else { for ($region = 0; $region < 3; $region++) { $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); $SideInfoOffset += 5; } $ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); $SideInfoOffset += 4; $ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); $SideInfoOffset += 3; $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0; } if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { $ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; } $ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; $ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); $SideInfoOffset += 1; } } } return true; }