Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}