File indexing completed on 2024-12-22 05:33:13

0001 <?php
0002 /////////////////////////////////////////////////////////////////
0003 /// getID3() by James Heinrich <info@getid3.org>               //
0004 //  available at http://getid3.sourceforge.net                 //
0005 //            or http://www.getid3.org                         //
0006 //          also https://github.com/JamesHeinrich/getID3       //
0007 /////////////////////////////////////////////////////////////////
0008 // See readme.txt for more details                             //
0009 /////////////////////////////////////////////////////////////////
0010 //                                                             //
0011 // module.audio.mp3.php                                        //
0012 // module for analyzing MP3 files                              //
0013 // dependencies: NONE                                          //
0014 //                                                            ///
0015 /////////////////////////////////////////////////////////////////
0016 
0017 
0018 // number of frames to scan to determine if MPEG-audio sequence is valid
0019 // Lower this number to 5-20 for faster scanning
0020 // Increase this number to 50+ for most accurate detection of valid VBR/CBR
0021 // mpeg-audio streams
0022 define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
0023 
0024 
0025 class getid3_mp3 extends getid3_handler
0026 {
0027 
0028   public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
0029 
0030   public function Analyze() {
0031     $info = &$this->getid3->info;
0032 
0033     $initialOffset = $info['avdataoffset'];
0034 
0035     if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
0036       if ($this->allow_bruteforce) {
0037         $info['error'][] = 'Rescanning file in BruteForce mode';
0038         $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
0039       }
0040     }
0041 
0042 
0043     if (isset($info['mpeg']['audio']['bitrate_mode'])) {
0044       $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
0045     }
0046 
0047     if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
0048 
0049       $synchoffsetwarning = 'Unknown data before synch ';
0050       if (isset($info['id3v2']['headerlength'])) {
0051         $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
0052       } elseif ($initialOffset > 0) {
0053         $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
0054       } else {
0055         $synchoffsetwarning .= '(should be at beginning of file, ';
0056       }
0057       $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
0058       if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
0059 
0060         if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
0061 
0062           $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
0063           $info['audio']['codec'] = 'LAME';
0064           $CurrentDataLAMEversionString = 'LAME3.';
0065 
0066         } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
0067 
0068           $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
0069           $info['audio']['codec'] = 'LAME';
0070           $CurrentDataLAMEversionString = 'LAME3.';
0071 
0072         }
0073 
0074       }
0075       $info['warning'][] = $synchoffsetwarning;
0076 
0077     }
0078 
0079     if (isset($info['mpeg']['audio']['LAME'])) {
0080       $info['audio']['codec'] = 'LAME';
0081       if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
0082         $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
0083       } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
0084         $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
0085       }
0086     }
0087 
0088     $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
0089     if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
0090       // a version number of LAME that does not end with a number like "LAME3.92"
0091       // or with a closing parenthesis like "LAME3.88 (alpha)"
0092       // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
0093 
0094       // not sure what the actual last frame length will be, but will be less than or equal to 1441
0095       $PossiblyLongerLAMEversion_FrameLength = 1441;
0096 
0097       // Not sure what version of LAME this is - look in padding of last frame for longer version string
0098       $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
0099       $this->fseek($PossibleLAMEversionStringOffset);
0100       $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
0101       switch (substr($CurrentDataLAMEversionString, -1)) {
0102         case 'a':
0103         case 'b':
0104           // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
0105           // need to trim off "a" to match longer string
0106           $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
0107           break;
0108       }
0109       if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
0110         if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
0111           $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
0112           if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
0113             $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
0114           }
0115         }
0116       }
0117     }
0118     if (!empty($info['audio']['encoder'])) {
0119       $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
0120     }
0121 
0122     switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
0123       case 1:
0124       case 2:
0125         $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
0126         break;
0127     }
0128     if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
0129       switch ($info['audio']['dataformat']) {
0130         case 'mp1':
0131         case 'mp2':
0132         case 'mp3':
0133           $info['fileformat'] = $info['audio']['dataformat'];
0134           break;
0135 
0136         default:
0137           $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
0138           break;
0139       }
0140     }
0141 
0142     if (empty($info['fileformat'])) {
0143       unset($info['fileformat']);
0144       unset($info['audio']['bitrate_mode']);
0145       unset($info['avdataoffset']);
0146       unset($info['avdataend']);
0147       return false;
0148     }
0149 
0150     $info['mime_type']         = 'audio/mpeg';
0151     $info['audio']['lossless'] = false;
0152 
0153     // Calculate playtime
0154     if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
0155       $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
0156     }
0157 
0158     $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
0159 
0160     return true;
0161   }
0162 
0163 
0164   public function GuessEncoderOptions() {
0165     // shortcuts
0166     $info = &$this->getid3->info;
0167     if (!empty($info['mpeg']['audio'])) {
0168       $thisfile_mpeg_audio = &$info['mpeg']['audio'];
0169       if (!empty($thisfile_mpeg_audio['LAME'])) {
0170         $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
0171       }
0172     }
0173 
0174     $encoder_options = '';
0175     static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
0176 
0177     if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
0178 
0179       $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
0180 
0181     } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
0182 
0183       $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
0184 
0185     } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
0186 
0187       static $KnownEncoderValues = array();
0188       if (empty($KnownEncoderValues)) {
0189 
0190         //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
0191         $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
0192         $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
0193         $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
0194         $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
0195         $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
0196         $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
0197         $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
0198         $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
0199         $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
0200         $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
0201         $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
0202         $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
0203         $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
0204         $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
0205         $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
0206         $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
0207         $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
0208 
0209         $KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
0210         $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
0211         $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
0212         $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
0213         $KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
0214         $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
0215         $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
0216         $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
0217         $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
0218         $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
0219         $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
0220         $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
0221         $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
0222         $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
0223         $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
0224         $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
0225         $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
0226         $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
0227         $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
0228         $KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
0229         $KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
0230         $KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
0231         $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
0232         $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
0233         $KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
0234         $KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
0235         $KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
0236         $KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
0237         $KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
0238         $KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
0239         $KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
0240         $KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
0241         $KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
0242       }
0243 
0244       if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
0245 
0246         $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
0247 
0248       } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
0249 
0250         $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
0251 
0252       } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
0253 
0254         // http://gabriel.mp3-tech.org/mp3infotag.html
0255         // int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
0256 
0257 
0258         $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
0259         $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
0260         $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
0261 
0262       } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
0263 
0264         $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
0265 
0266       } else {
0267 
0268         $encoder_options = strtoupper($info['audio']['bitrate_mode']);
0269 
0270       }
0271 
0272     } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
0273 
0274       $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
0275 
0276     } elseif (!empty($info['audio']['bitrate'])) {
0277 
0278       if ($info['audio']['bitrate_mode'] == 'cbr') {
0279         $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
0280       } else {
0281         $encoder_options = strtoupper($info['audio']['bitrate_mode']);
0282       }
0283 
0284     }
0285     if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
0286       $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
0287     }
0288 
0289     if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
0290       $encoder_options .= ' --nogap';
0291     }
0292 
0293     if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
0294       $ExplodedOptions = explode(' ', $encoder_options, 4);
0295       if ($ExplodedOptions[0] == '--r3mix') {
0296         $ExplodedOptions[1] = 'r3mix';
0297       }
0298       switch ($ExplodedOptions[0]) {
0299         case '--preset':
0300         case '--alt-preset':
0301         case '--r3mix':
0302           if ($ExplodedOptions[1] == 'fast') {
0303             $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
0304           }
0305           switch ($ExplodedOptions[1]) {
0306             case 'portable':
0307             case 'medium':
0308             case 'standard':
0309             case 'extreme':
0310             case 'insane':
0311             case 'fast portable':
0312             case 'fast medium':
0313             case 'fast standard':
0314             case 'fast extreme':
0315             case 'fast insane':
0316             case 'r3mix':
0317               static $ExpectedLowpass = array(
0318                   'insane|20500'        => 20500,
0319                   'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
0320                   'medium|18000'        => 18000,
0321                   'fast medium|18000'   => 18000,
0322                   'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
0323                   'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
0324                   'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
0325                   'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
0326                   'standard|19000'      => 19000,
0327                   'fast standard|19000' => 19000,
0328                   'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
0329                   'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
0330                   'r3mix|18000'         => 18000,  // 3.94,   3.95
0331                 );
0332               if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
0333                 $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
0334               }
0335               break;
0336 
0337             default:
0338               break;
0339           }
0340           break;
0341       }
0342     }
0343 
0344     if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
0345       if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
0346         $encoder_options .= ' --resample 44100';
0347       } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
0348         $encoder_options .= ' --resample 48000';
0349       } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
0350         switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
0351           case 0: // <= 32000
0352             // may or may not be same as source frequency - ignore
0353             break;
0354           case 1: // 44100
0355           case 2: // 48000
0356           case 3: // 48000+
0357             $ExplodedOptions = explode(' ', $encoder_options, 4);
0358             switch ($ExplodedOptions[0]) {
0359               case '--preset':
0360               case '--alt-preset':
0361                 switch ($ExplodedOptions[1]) {
0362                   case 'fast':
0363                   case 'portable':
0364                   case 'medium':
0365                   case 'standard':
0366                   case 'extreme':
0367                   case 'insane':
0368                     $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
0369                     break;
0370 
0371                   default:
0372                     static $ExpectedResampledRate = array(
0373                         'phon+/lw/mw-eu/sw|16000' => 16000,
0374                         'mw-us|24000'             => 24000, // 3.95
0375                         'mw-us|32000'             => 32000, // 3.93
0376                         'mw-us|16000'             => 16000, // 3.92
0377                         'phone|16000'             => 16000,
0378                         'phone|11025'             => 11025, // 3.94a15
0379                         'radio|32000'             => 32000, // 3.94a15
0380                         'fm/radio|32000'          => 32000, // 3.92
0381                         'fm|32000'                => 32000, // 3.90
0382                         'voice|32000'             => 32000);
0383                     if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
0384                       $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
0385                     }
0386                     break;
0387                 }
0388                 break;
0389 
0390               case '--r3mix':
0391               default:
0392                 $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
0393                 break;
0394             }
0395             break;
0396         }
0397       }
0398     }
0399     if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
0400       //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
0401       $encoder_options = strtoupper($info['audio']['bitrate_mode']);
0402     }
0403 
0404     return $encoder_options;
0405   }
0406 
0407 
0408   public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
0409     static $MPEGaudioVersionLookup;
0410     static $MPEGaudioLayerLookup;
0411     static $MPEGaudioBitrateLookup;
0412     static $MPEGaudioFrequencyLookup;
0413     static $MPEGaudioChannelModeLookup;
0414     static $MPEGaudioModeExtensionLookup;
0415     static $MPEGaudioEmphasisLookup;
0416     if (empty($MPEGaudioVersionLookup)) {
0417       $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
0418       $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
0419       $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
0420       $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
0421       $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
0422       $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
0423       $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
0424     }
0425 
0426     if ($this->fseek($offset) != 0) {
0427       $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
0428       return false;
0429     }
0430     //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
0431     $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
0432 
0433     // MP3 audio frame structure:
0434     // $aa $aa $aa $aa [$bb $bb] $cc...
0435     // where $aa..$aa is the four-byte mpeg-audio header (below)
0436     // $bb $bb is the optional 2-byte CRC
0437     // and $cc... is the audio data
0438 
0439     $head4 = substr($headerstring, 0, 4);
0440 
0441     static $MPEGaudioHeaderDecodeCache = array();
0442     if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
0443       $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
0444     } else {
0445       $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
0446       $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
0447     }
0448 
0449     static $MPEGaudioHeaderValidCache = array();
0450     if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
0451       //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
0452       $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
0453     }
0454 
0455     // shortcut
0456     if (!isset($info['mpeg']['audio'])) {
0457       $info['mpeg']['audio'] = array();
0458     }
0459     $thisfile_mpeg_audio = &$info['mpeg']['audio'];
0460 
0461 
0462     if ($MPEGaudioHeaderValidCache[$head4]) {
0463       $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
0464     } else {
0465       $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
0466       return false;
0467     }
0468 
0469     if (!$FastMPEGheaderScan) {
0470       $thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
0471       $thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
0472 
0473       $thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
0474       $thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
0475       $thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
0476       $thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
0477       $thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
0478       $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
0479       $thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
0480       $thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
0481       $thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
0482 
0483       $info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
0484       $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
0485 
0486       if ($thisfile_mpeg_audio['protection']) {
0487         $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
0488       }
0489     }
0490 
0491     if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
0492       // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
0493       $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
0494       $thisfile_mpeg_audio['raw']['bitrate'] = 0;
0495     }
0496     $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
0497     $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
0498 
0499     if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
0500       // only skip multiple frame check if free-format bitstream found at beginning of file
0501       // otherwise is quite possibly simply corrupted data
0502       $recursivesearch = false;
0503     }
0504 
0505     // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
0506     if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
0507 
0508       $info['audio']['dataformat'] = 'mp2';
0509       switch ($thisfile_mpeg_audio['channelmode']) {
0510 
0511         case 'mono':
0512           if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
0513             // these are ok
0514           } else {
0515             $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
0516             return false;
0517           }
0518           break;
0519 
0520         case 'stereo':
0521         case 'joint stereo':
0522         case 'dual channel':
0523           if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
0524             // these are ok
0525           } else {
0526             $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
0527             return false;
0528           }
0529           break;
0530 
0531       }
0532 
0533     }
0534 
0535 
0536     if ($info['audio']['sample_rate'] > 0) {
0537       $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
0538     }
0539 
0540     $nextframetestoffset = $offset + 1;
0541     if ($thisfile_mpeg_audio['bitrate'] != 'free') {
0542 
0543       $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
0544 
0545       if (isset($thisfile_mpeg_audio['framelength'])) {
0546         $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
0547       } else {
0548         $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
0549         return false;
0550       }
0551 
0552     }
0553 
0554     $ExpectedNumberOfAudioBytes = 0;
0555 
0556     ////////////////////////////////////////////////////////////////////////////////////
0557     // Variable-bitrate headers
0558 
0559     if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
0560       // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
0561       // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
0562 
0563       $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
0564       $thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
0565       $info['audio']['codec']                = 'Fraunhofer';
0566 
0567       $SideInfoData = substr($headerstring, 4 + 2, 32);
0568 
0569       $FraunhoferVBROffset = 36;
0570 
0571       $thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
0572       $thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
0573       $thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
0574       $thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
0575       $thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
0576       $thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
0577       $thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
0578       $thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
0579       $thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
0580 
0581       $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
0582 
0583       $previousbyteoffset = $offset;
0584       for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
0585         $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
0586         $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
0587         $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
0588         $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
0589         $previousbyteoffset += $Fraunhofer_OffsetN;
0590       }
0591 
0592 
0593     } else {
0594 
0595       // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
0596       // depending on MPEG layer and number of channels
0597 
0598       $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
0599       $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
0600 
0601       if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
0602         // 'Xing' is traditional Xing VBR frame
0603         // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
0604         // 'Info' *can* legally be used to specify a VBR file as well, however.
0605 
0606         // http://www.multiweb.cz/twoinches/MP3inside.htm
0607         //00..03 = "Xing" or "Info"
0608         //04..07 = Flags:
0609         //  0x01  Frames Flag     set if value for number of frames in file is stored
0610         //  0x02  Bytes Flag      set if value for filesize in bytes is stored
0611         //  0x04  TOC Flag        set if values for TOC are stored
0612         //  0x08  VBR Scale Flag  set if values for VBR scale is stored
0613         //08..11  Frames: Number of frames in file (including the first Xing/Info one)
0614         //12..15  Bytes:  File length in Bytes
0615         //16..115  TOC (Table of Contents):
0616         //  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
0617         //  Each Byte has a value according this formula:
0618         //  (TOC[i] / 256) * fileLenInBytes
0619         //  So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
0620         //  TOC[(60/240)*100] = TOC[25]
0621         //  and corresponding Byte in file is then approximately at:
0622         //  (TOC[25]/256) * 5000000
0623         //116..119  VBR Scale
0624 
0625 
0626         // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
0627 //        if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
0628           $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
0629           $thisfile_mpeg_audio['VBR_method']   = 'Xing';
0630 //        } else {
0631 //          $ScanAsCBR = true;
0632 //          $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
0633 //        }
0634 
0635         $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
0636 
0637         $thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
0638         $thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
0639         $thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
0640         $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
0641 
0642         if ($thisfile_mpeg_audio['xing_flags']['frames']) {
0643           $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
0644           //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
0645         }
0646         if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
0647           $thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
0648         }
0649 
0650         //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
0651         if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
0652 
0653           $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
0654 
0655           if ($thisfile_mpeg_audio['layer'] == '1') {
0656             // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
0657             //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
0658             $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
0659           } else {
0660             // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
0661             //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
0662             $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
0663           }
0664           $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
0665         }
0666 
0667         if ($thisfile_mpeg_audio['xing_flags']['toc']) {
0668           $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
0669           for ($i = 0; $i < 100; $i++) {
0670             $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
0671           }
0672         }
0673         if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
0674           $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
0675         }
0676 
0677 
0678         // http://gabriel.mp3-tech.org/mp3infotag.html
0679         if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
0680 
0681           // shortcut
0682           $thisfile_mpeg_audio['LAME'] = array();
0683           $thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
0684 
0685 
0686           $thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
0687           $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
0688 
0689           if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
0690 
0691             // extra 11 chars are not part of version string when LAMEtag present
0692             unset($thisfile_mpeg_audio_lame['long_version']);
0693 
0694             // It the LAME tag was only introduced in LAME v3.90
0695             // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
0696 
0697             // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
0698             // are assuming a 'Xing' identifier offset of 0x24, which is the case for
0699             // MPEG-1 non-mono, but not for other combinations
0700             $LAMEtagOffsetContant = $VBRidOffset - 0x24;
0701 
0702             // shortcuts
0703             $thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
0704             $thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
0705             $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
0706             $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
0707             $thisfile_mpeg_audio_lame['raw'] = array();
0708             $thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
0709 
0710             // byte $9B  VBR Quality
0711             // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
0712             // Actually overwrites original Xing bytes
0713             unset($thisfile_mpeg_audio['VBR_scale']);
0714             $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
0715 
0716             // bytes $9C-$A4  Encoder short VersionString
0717             $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
0718 
0719             // byte $A5  Info Tag revision + VBR method
0720             $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
0721 
0722             $thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
0723             $thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
0724             $thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
0725             $thisfile_mpeg_audio['bitrate_mode']        = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
0726 
0727             // byte $A6  Lowpass filter value
0728             $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
0729 
0730             // bytes $A7-$AE  Replay Gain
0731             // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
0732             // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
0733             if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
0734               // LAME 3.94a16 and later - 9.23 fixed point
0735               // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
0736               $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
0737             } else {
0738               // LAME 3.94a15 and earlier - 32-bit floating point
0739               // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
0740               $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
0741             }
0742             if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
0743               unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
0744             } else {
0745               $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
0746             }
0747 
0748             $thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
0749             $thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
0750 
0751 
0752             if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
0753 
0754               $thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
0755               $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
0756               $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
0757               $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
0758               $thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
0759               $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
0760               $thisfile_mpeg_audio_lame_RGAD_track['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
0761 
0762               if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
0763                 $info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
0764               }
0765               $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
0766               $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
0767             } else {
0768               unset($thisfile_mpeg_audio_lame_RGAD['track']);
0769             }
0770             if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
0771 
0772               $thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
0773               $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
0774               $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
0775               $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
0776               $thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
0777               $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
0778               $thisfile_mpeg_audio_lame_RGAD_album['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
0779 
0780               if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
0781                 $info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
0782               }
0783               $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
0784               $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
0785             } else {
0786               unset($thisfile_mpeg_audio_lame_RGAD['album']);
0787             }
0788             if (empty($thisfile_mpeg_audio_lame_RGAD)) {
0789               unset($thisfile_mpeg_audio_lame['RGAD']);
0790             }
0791 
0792 
0793             // byte $AF  Encoding flags + ATH Type
0794             $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
0795             $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
0796             $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
0797             $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
0798             $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
0799             $thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
0800 
0801             // byte $B0  if ABR {specified bitrate} else {minimal bitrate}
0802             $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
0803             if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
0804               $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
0805             } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
0806               // ignore
0807             } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
0808               $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
0809             }
0810 
0811             // bytes $B1-$B3  Encoder delays
0812             $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
0813             $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
0814             $thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
0815 
0816             // byte $B4  Misc
0817             $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
0818             $thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
0819             $thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
0820             $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
0821             $thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
0822             $thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
0823             $thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
0824             $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
0825             $thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
0826 
0827             // byte $B5  MP3 Gain
0828             $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
0829             $thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
0830             $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
0831 
0832             // bytes $B6-$B7  Preset and surround info
0833             $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
0834             // Reserved                                                    = ($PresetSurroundBytes & 0xC000);
0835             $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
0836             $thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
0837             $thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
0838             $thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
0839             if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
0840               $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
0841             }
0842             if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
0843               // this may change if 3.90.4 ever comes out
0844               $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
0845             }
0846 
0847             // bytes $B8-$BB  MusicLength
0848             $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
0849             $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
0850 
0851             // bytes $BC-$BD  MusicCRC
0852             $thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
0853 
0854             // bytes $BE-$BF  CRC-16 of Info Tag
0855             $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
0856 
0857 
0858             // LAME CBR
0859             if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
0860 
0861               $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
0862               $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
0863               $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
0864               //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
0865               //  $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
0866               //}
0867 
0868             }
0869 
0870           }
0871         }
0872 
0873       } else {
0874 
0875         // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
0876         $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
0877         if ($recursivesearch) {
0878           $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
0879           if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
0880             $recursivesearch = false;
0881             $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
0882           }
0883           if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
0884             $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
0885           }
0886         }
0887 
0888       }
0889 
0890     }
0891 
0892     if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
0893       if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
0894         if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
0895           // ignore, audio data is broken into chunks so will always be data "missing"
0896         }
0897         elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
0898           $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
0899         }
0900         else {
0901           $this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
0902         }
0903       } else {
0904         if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
0905         //  $prenullbytefileoffset = $this->ftell();
0906         //  $this->fseek($info['avdataend']);
0907         //  $PossibleNullByte = $this->fread(1);
0908         //  $this->fseek($prenullbytefileoffset);
0909         //  if ($PossibleNullByte === "\x00") {
0910             $info['avdataend']--;
0911         //    $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
0912         //  } else {
0913         //    $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
0914         //  }
0915         } else {
0916           $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
0917         }
0918       }
0919     }
0920 
0921     if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
0922       if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
0923         $framebytelength = $this->FreeFormatFrameLength($offset, true);
0924         if ($framebytelength > 0) {
0925           $thisfile_mpeg_audio['framelength'] = $framebytelength;
0926           if ($thisfile_mpeg_audio['layer'] == '1') {
0927             // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
0928             $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
0929           } else {
0930             // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
0931             $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
0932           }
0933         } else {
0934           $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
0935         }
0936       }
0937     }
0938 
0939     if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
0940       switch ($thisfile_mpeg_audio['bitrate_mode']) {
0941         case 'vbr':
0942         case 'abr':
0943           $bytes_per_frame = 1152;
0944           if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
0945             $bytes_per_frame = 384;
0946           } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
0947             $bytes_per_frame = 576;
0948           }
0949           $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
0950           if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
0951             $info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
0952             $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
0953           }
0954           break;
0955       }
0956     }
0957 
0958     // End variable-bitrate headers
0959     ////////////////////////////////////////////////////////////////////////////////////
0960 
0961     if ($recursivesearch) {
0962 
0963       if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
0964         return false;
0965       }
0966 
0967     }
0968 
0969 
0970     //if (false) {
0971     //    // experimental side info parsing section - not returning anything useful yet
0972     //
0973     //    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
0974     //    $SideInfoOffset = 0;
0975     //
0976     //    if ($thisfile_mpeg_audio['version'] == '1') {
0977     //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
0978     //            // MPEG-1 (mono)
0979     //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
0980     //            $SideInfoOffset += 9;
0981     //            $SideInfoOffset += 5;
0982     //        } else {
0983     //            // MPEG-1 (stereo, joint-stereo, dual-channel)
0984     //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
0985     //            $SideInfoOffset += 9;
0986     //            $SideInfoOffset += 3;
0987     //        }
0988     //    } else { // 2 or 2.5
0989     //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
0990     //            // MPEG-2, MPEG-2.5 (mono)
0991     //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
0992     //            $SideInfoOffset += 8;
0993     //            $SideInfoOffset += 1;
0994     //        } else {
0995     //            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
0996     //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
0997     //            $SideInfoOffset += 8;
0998     //            $SideInfoOffset += 2;
0999     //        }
1000     //    }
1001     //
1002     //    if ($thisfile_mpeg_audio['version'] == '1') {
1003     //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1004     //            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1005     //                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1006     //                $SideInfoOffset += 2;
1007     //            }
1008     //        }
1009     //    }
1010     //    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1011     //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1012     //            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1013     //            $SideInfoOffset += 12;
1014     //            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1015     //            $SideInfoOffset += 9;
1016     //            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1017     //            $SideInfoOffset += 8;
1018     //            if ($thisfile_mpeg_audio['version'] == '1') {
1019     //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1020     //                $SideInfoOffset += 4;
1021     //            } else {
1022     //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1023     //                $SideInfoOffset += 9;
1024     //            }
1025     //            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1026     //            $SideInfoOffset += 1;
1027     //
1028     //            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1029     //
1030     //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1031     //                $SideInfoOffset += 2;
1032     //                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1033     //                $SideInfoOffset += 1;
1034     //
1035     //                for ($region = 0; $region < 2; $region++) {
1036     //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1037     //                    $SideInfoOffset += 5;
1038     //                }
1039     //                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1040     //
1041     //                for ($window = 0; $window < 3; $window++) {
1042     //                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1043     //                    $SideInfoOffset += 3;
1044     //                }
1045     //
1046     //            } else {
1047     //
1048     //                for ($region = 0; $region < 3; $region++) {
1049     //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1050     //                    $SideInfoOffset += 5;
1051     //                }
1052     //
1053     //                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1054     //                $SideInfoOffset += 4;
1055     //                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1056     //                $SideInfoOffset += 3;
1057     //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1058     //            }
1059     //
1060     //            if ($thisfile_mpeg_audio['version'] == '1') {
1061     //                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1062     //                $SideInfoOffset += 1;
1063     //            }
1064     //            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1065     //            $SideInfoOffset += 1;
1066     //            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1067     //            $SideInfoOffset += 1;
1068     //        }
1069     //    }
1070     //}
1071 
1072     return true;
1073   }
1074 
1075   public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1076     $info = &$this->getid3->info;
1077     $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1078     $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1079 
1080     for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1081       // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1082       if (($nextframetestoffset + 4) >= $info['avdataend']) {
1083         // end of file
1084         return true;
1085       }
1086 
1087       $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1088       if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1089         if ($ScanAsCBR) {
1090           // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1091           if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1092             return false;
1093           }
1094         }
1095 
1096 
1097         // next frame is OK, get ready to check the one after that
1098         if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1099           $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1100         } else {
1101           $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1102           return false;
1103         }
1104 
1105       } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1106 
1107         // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
1108         return true;
1109 
1110       } else {
1111 
1112         // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1113         $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1114 
1115         return false;
1116       }
1117     }
1118     return true;
1119   }
1120 
1121   public function FreeFormatFrameLength($offset, $deepscan=false) {
1122     $info = &$this->getid3->info;
1123 
1124     $this->fseek($offset);
1125     $MPEGaudioData = $this->fread(32768);
1126 
1127     $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1128     // may be different pattern due to padding
1129     $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1130     if ($SyncPattern2 === $SyncPattern1) {
1131       $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1132     }
1133 
1134     $framelength = false;
1135     $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1136     $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1137     if ($framelength1 > 4) {
1138       $framelength = $framelength1;
1139     }
1140     if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1141       $framelength = $framelength2;
1142     }
1143     if (!$framelength) {
1144 
1145       // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1146       $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1147       $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1148 
1149       if ($framelength1 > 4) {
1150         $framelength = $framelength1;
1151       }
1152       if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1153         $framelength = $framelength2;
1154       }
1155       if (!$framelength) {
1156         $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1157         return false;
1158       } else {
1159         $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1160         $info['audio']['codec']   = 'LAME';
1161         $info['audio']['encoder'] = 'LAME3.88';
1162         $SyncPattern1 = substr($SyncPattern1, 0, 3);
1163         $SyncPattern2 = substr($SyncPattern2, 0, 3);
1164       }
1165     }
1166 
1167     if ($deepscan) {
1168 
1169       $ActualFrameLengthValues = array();
1170       $nextoffset = $offset + $framelength;
1171       while ($nextoffset < ($info['avdataend'] - 6)) {
1172         $this->fseek($nextoffset - 1);
1173         $NextSyncPattern = $this->fread(6);
1174         if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1175           // good - found where expected
1176           $ActualFrameLengthValues[] = $framelength;
1177         } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1178           // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1179           $ActualFrameLengthValues[] = ($framelength - 1);
1180           $nextoffset--;
1181         } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1182           // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1183           $ActualFrameLengthValues[] = ($framelength + 1);
1184           $nextoffset++;
1185         } else {
1186           $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1187           return false;
1188         }
1189         $nextoffset += $framelength;
1190       }
1191       if (count($ActualFrameLengthValues) > 0) {
1192         $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1193       }
1194     }
1195     return $framelength;
1196   }
1197 
1198   public function getOnlyMPEGaudioInfoBruteForce() {
1199     $MPEGaudioHeaderDecodeCache   = array();
1200     $MPEGaudioHeaderValidCache    = array();
1201     $MPEGaudioHeaderLengthCache   = array();
1202     $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1203     $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1204     $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1205     $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1206     $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1207     $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1208     $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1209     $LongMPEGversionLookup        = array();
1210     $LongMPEGlayerLookup          = array();
1211     $LongMPEGbitrateLookup        = array();
1212     $LongMPEGpaddingLookup        = array();
1213     $LongMPEGfrequencyLookup      = array();
1214     $Distribution['bitrate']      = array();
1215     $Distribution['frequency']    = array();
1216     $Distribution['layer']        = array();
1217     $Distribution['version']      = array();
1218     $Distribution['padding']      = array();
1219 
1220     $info = &$this->getid3->info;
1221     $this->fseek($info['avdataoffset']);
1222 
1223     $max_frames_scan = 5000;
1224     $frames_scanned  = 0;
1225 
1226     $previousvalidframe = $info['avdataoffset'];
1227     while ($this->ftell() < $info['avdataend']) {
1228       set_time_limit(30);
1229       $head4 = $this->fread(4);
1230       if (strlen($head4) < 4) {
1231         break;
1232       }
1233       if ($head4{0} != "\xFF") {
1234         for ($i = 1; $i < 4; $i++) {
1235           if ($head4{$i} == "\xFF") {
1236             $this->fseek($i - 4, SEEK_CUR);
1237             continue 2;
1238           }
1239         }
1240         continue;
1241       }
1242       if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1243         $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1244       }
1245       if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1246         $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1247       }
1248       if ($MPEGaudioHeaderValidCache[$head4]) {
1249 
1250         if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1251           $LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1252           $LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1253           $LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1254           $LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1255           $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1256           $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1257             $LongMPEGbitrateLookup[$head4],
1258             $LongMPEGversionLookup[$head4],
1259             $LongMPEGlayerLookup[$head4],
1260             $LongMPEGpaddingLookup[$head4],
1261             $LongMPEGfrequencyLookup[$head4]);
1262         }
1263         if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1264           $WhereWeWere = $this->ftell();
1265           $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1266           $next4 = $this->fread(4);
1267           if ($next4{0} == "\xFF") {
1268             if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1269               $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1270             }
1271             if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1272               $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1273             }
1274             if ($MPEGaudioHeaderValidCache[$next4]) {
1275               $this->fseek(-4, SEEK_CUR);
1276 
1277               getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1278               getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1279               getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1280               getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1281               getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1282               if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1283                 $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1284                 $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1285                 foreach ($Distribution as $key1 => $value1) {
1286                   foreach ($value1 as $key2 => $value2) {
1287                     $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1288                   }
1289                 }
1290                 break;
1291               }
1292               continue;
1293             }
1294           }
1295           unset($next4);
1296           $this->fseek($WhereWeWere - 3);
1297         }
1298 
1299       }
1300     }
1301     foreach ($Distribution as $key => $value) {
1302       ksort($Distribution[$key], SORT_NUMERIC);
1303     }
1304     ksort($Distribution['version'], SORT_STRING);
1305     $info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
1306     $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1307     $info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
1308     $info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
1309     $info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
1310     if (count($Distribution['version']) > 1) {
1311       $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1312     }
1313     if (count($Distribution['layer']) > 1) {
1314       $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1315     }
1316     if (count($Distribution['frequency']) > 1) {
1317       $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1318     }
1319 
1320 
1321     $bittotal = 0;
1322     foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1323       if ($bitratevalue != 'free') {
1324         $bittotal += ($bitratevalue * $bitratecount);
1325       }
1326     }
1327     $info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
1328     if ($info['mpeg']['audio']['frame_count'] == 0) {
1329       $info['error'][] = 'no MPEG audio frames found';
1330       return false;
1331     }
1332     $info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
1333     $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1334     $info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
1335 
1336     $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1337     $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1338     $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1339     $info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1340     $info['fileformat']            = $info['audio']['dataformat'];
1341 
1342     return true;
1343   }
1344 
1345 
1346   public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1347     // looks for synch, decodes MPEG audio header
1348 
1349     $info = &$this->getid3->info;
1350 
1351     static $MPEGaudioVersionLookup;
1352     static $MPEGaudioLayerLookup;
1353     static $MPEGaudioBitrateLookup;
1354     if (empty($MPEGaudioVersionLookup)) {
1355        $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1356        $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
1357        $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1358 
1359     }
1360 
1361     $this->fseek($avdataoffset);
1362     $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1363     if ($sync_seek_buffer_size <= 0) {
1364       $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1365       return false;
1366     }
1367     $header = $this->fread($sync_seek_buffer_size);
1368     $sync_seek_buffer_size = strlen($header);
1369     $SynchSeekOffset = 0;
1370     while ($SynchSeekOffset < $sync_seek_buffer_size) {
1371       if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
1372 
1373         if ($SynchSeekOffset > $sync_seek_buffer_size) {
1374           // if a synch's not found within the first 128k bytes, then give up
1375           $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1376           if (isset($info['audio']['bitrate'])) {
1377             unset($info['audio']['bitrate']);
1378           }
1379           if (isset($info['mpeg']['audio'])) {
1380             unset($info['mpeg']['audio']);
1381           }
1382           if (empty($info['mpeg'])) {
1383             unset($info['mpeg']);
1384           }
1385           return false;
1386 
1387         } elseif (feof($this->getid3->fp)) {
1388 
1389           $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1390           if (isset($info['audio']['bitrate'])) {
1391             unset($info['audio']['bitrate']);
1392           }
1393           if (isset($info['mpeg']['audio'])) {
1394             unset($info['mpeg']['audio']);
1395           }
1396           if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1397             unset($info['mpeg']);
1398           }
1399           return false;
1400         }
1401       }
1402 
1403       if (($SynchSeekOffset + 1) >= strlen($header)) {
1404         $info['error'][] = 'Could not find valid MPEG synch before end of file';
1405         return false;
1406       }
1407 
1408       if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1409         if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1410           $FirstFrameThisfileInfo = $info;
1411           $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1412           if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1413             // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1414             // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1415             unset($FirstFrameThisfileInfo);
1416           }
1417         }
1418 
1419         $dummy = $info; // only overwrite real data if valid header found
1420         if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1421           $info = $dummy;
1422           $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1423           switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1424             case '':
1425             case 'id3':
1426             case 'ape':
1427             case 'mp3':
1428               $info['fileformat']          = 'mp3';
1429               $info['audio']['dataformat'] = 'mp3';
1430               break;
1431           }
1432           if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1433             if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1434               // If there is garbage data between a valid VBR header frame and a sequence
1435               // of valid MPEG-audio frames the VBR data is no longer discarded.
1436               $info = $FirstFrameThisfileInfo;
1437               $info['avdataoffset']        = $FirstFrameAVDataOffset;
1438               $info['fileformat']          = 'mp3';
1439               $info['audio']['dataformat'] = 'mp3';
1440               $dummy                       = $info;
1441               unset($dummy['mpeg']['audio']);
1442               $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1443               $GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
1444               if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1445                 $info = $dummy;
1446                 $info['avdataoffset'] = $GarbageOffsetEnd;
1447                 $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1448               } else {
1449                 $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1450               }
1451             }
1452           }
1453           if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1454             // VBR file with no VBR header
1455             $BitrateHistogram = true;
1456           }
1457 
1458           if ($BitrateHistogram) {
1459 
1460             $info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1461             $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1462 
1463             if ($info['mpeg']['audio']['version'] == '1') {
1464               if ($info['mpeg']['audio']['layer'] == 3) {
1465                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1466               } elseif ($info['mpeg']['audio']['layer'] == 2) {
1467                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1468               } elseif ($info['mpeg']['audio']['layer'] == 1) {
1469                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1470               }
1471             } elseif ($info['mpeg']['audio']['layer'] == 1) {
1472               $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1473             } else {
1474               $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1475             }
1476 
1477             $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1478             $synchstartoffset = $info['avdataoffset'];
1479             $this->fseek($info['avdataoffset']);
1480 
1481             // you can play with these numbers:
1482             $max_frames_scan  = 50000;
1483             $max_scan_segments = 10;
1484 
1485             // don't play with these numbers:
1486             $FastMode = false;
1487             $SynchErrorsFound = 0;
1488             $frames_scanned   = 0;
1489             $this_scan_segment = 0;
1490             $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1491             $pct_data_scanned = 0;
1492             for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1493               $frames_scanned_this_segment = 0;
1494               if ($this->ftell() >= $info['avdataend']) {
1495                 break;
1496               }
1497               $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1498               if ($current_segment > 0) {
1499                 $this->fseek($scan_start_offset[$current_segment]);
1500                 $buffer_4k = $this->fread(4096);
1501                 for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1502                   if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1503                     if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1504                       $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1505                       if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1506                         $scan_start_offset[$current_segment] += $j;
1507                         break;
1508                       }
1509                     }
1510                   }
1511                 }
1512               }
1513               $synchstartoffset = $scan_start_offset[$current_segment];
1514               while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1515                 $FastMode = true;
1516                 $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1517 
1518                 if (empty($dummy['mpeg']['audio']['framelength'])) {
1519                   $SynchErrorsFound++;
1520                   $synchstartoffset++;
1521                 } else {
1522                   getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1523                   getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1524                   getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1525                   $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1526                 }
1527                 $frames_scanned++;
1528                 if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1529                   $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1530                   if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1531                     // file likely contains < $max_frames_scan, just scan as one segment
1532                     $max_scan_segments = 1;
1533                     $frames_scan_per_segment = $max_frames_scan;
1534                   } else {
1535                     $pct_data_scanned += $this_pct_scanned;
1536                     break;
1537                   }
1538                 }
1539               }
1540             }
1541             if ($pct_data_scanned > 0) {
1542               $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1543               foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1544                 if (!preg_match('#_distribution$#i', $key1)) {
1545                   continue;
1546                 }
1547                 foreach ($value1 as $key2 => $value2) {
1548                   $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1549                 }
1550               }
1551             }
1552 
1553             if ($SynchErrorsFound > 0) {
1554               $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1555               //return false;
1556             }
1557 
1558             $bittotal     = 0;
1559             $framecounter = 0;
1560             foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1561               $framecounter += $bitratecount;
1562               if ($bitratevalue != 'free') {
1563                 $bittotal += ($bitratevalue * $bitratecount);
1564               }
1565             }
1566             if ($framecounter == 0) {
1567               $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1568               return false;
1569             }
1570             $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1571             $info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
1572 
1573             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1574 
1575 
1576             // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1577             $distinct_bitrates = 0;
1578             foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1579               if ($bitrate_count > 0) {
1580                 $distinct_bitrates++;
1581               }
1582             }
1583             if ($distinct_bitrates > 1) {
1584               $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1585             } else {
1586               $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1587             }
1588             $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1589 
1590           }
1591 
1592           break; // exit while()
1593         }
1594       }
1595 
1596       $SynchSeekOffset++;
1597       if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1598         // end of file/data
1599 
1600         if (empty($info['mpeg']['audio'])) {
1601 
1602           $info['error'][] = 'could not find valid MPEG synch before end of file';
1603           if (isset($info['audio']['bitrate'])) {
1604             unset($info['audio']['bitrate']);
1605           }
1606           if (isset($info['mpeg']['audio'])) {
1607             unset($info['mpeg']['audio']);
1608           }
1609           if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1610             unset($info['mpeg']);
1611           }
1612           return false;
1613 
1614         }
1615         break;
1616       }
1617 
1618     }
1619     $info['audio']['channels']        = $info['mpeg']['audio']['channels'];
1620     $info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
1621     $info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
1622     return true;
1623   }
1624 
1625 
1626   public static function MPEGaudioVersionArray() {
1627     static $MPEGaudioVersion = array('2.5', false, '2', '1');
1628     return $MPEGaudioVersion;
1629   }
1630 
1631   public static function MPEGaudioLayerArray() {
1632     static $MPEGaudioLayer = array(false, 3, 2, 1);
1633     return $MPEGaudioLayer;
1634   }
1635 
1636   public static function MPEGaudioBitrateArray() {
1637     static $MPEGaudioBitrate;
1638     if (empty($MPEGaudioBitrate)) {
1639       $MPEGaudioBitrate = array (
1640         '1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1641                 2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1642                 3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1643                  ),
1644 
1645         '2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1646                 2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
1647                  )
1648       );
1649       $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1650       $MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
1651     }
1652     return $MPEGaudioBitrate;
1653   }
1654 
1655   public static function MPEGaudioFrequencyArray() {
1656     static $MPEGaudioFrequency;
1657     if (empty($MPEGaudioFrequency)) {
1658       $MPEGaudioFrequency = array (
1659         '1'   => array(44100, 48000, 32000),
1660         '2'   => array(22050, 24000, 16000),
1661         '2.5' => array(11025, 12000,  8000)
1662       );
1663     }
1664     return $MPEGaudioFrequency;
1665   }
1666 
1667   public static function MPEGaudioChannelModeArray() {
1668     static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1669     return $MPEGaudioChannelMode;
1670   }
1671 
1672   public static function MPEGaudioModeExtensionArray() {
1673     static $MPEGaudioModeExtension;
1674     if (empty($MPEGaudioModeExtension)) {
1675       $MPEGaudioModeExtension = array (
1676         1 => array('4-31', '8-31', '12-31', '16-31'),
1677         2 => array('4-31', '8-31', '12-31', '16-31'),
1678         3 => array('', 'IS', 'MS', 'IS+MS')
1679       );
1680     }
1681     return $MPEGaudioModeExtension;
1682   }
1683 
1684   public static function MPEGaudioEmphasisArray() {
1685     static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1686     return $MPEGaudioEmphasis;
1687   }
1688 
1689   public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1690     return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1691   }
1692 
1693   public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1694     if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1695       return false;
1696     }
1697 
1698     static $MPEGaudioVersionLookup;
1699     static $MPEGaudioLayerLookup;
1700     static $MPEGaudioBitrateLookup;
1701     static $MPEGaudioFrequencyLookup;
1702     static $MPEGaudioChannelModeLookup;
1703     static $MPEGaudioModeExtensionLookup;
1704     static $MPEGaudioEmphasisLookup;
1705     if (empty($MPEGaudioVersionLookup)) {
1706       $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1707       $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1708       $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1709       $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1710       $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1711       $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1712       $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1713     }
1714 
1715     if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1716       $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1717     } else {
1718       echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1719       return false;
1720     }
1721     if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1722       $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1723     } else {
1724       echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1725       return false;
1726     }
1727     if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1728       echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1729       if ($rawarray['bitrate'] == 15) {
1730         // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1731         // let it go through here otherwise file will not be identified
1732         if (!$allowBitrate15) {
1733           return false;
1734         }
1735       } else {
1736         return false;
1737       }
1738     }
1739     if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1740       echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1741       return false;
1742     }
1743     if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1744       echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1745       return false;
1746     }
1747     if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1748       echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1749       return false;
1750     }
1751     if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1752       echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1753       return false;
1754     }
1755     // These are just either set or not set, you can't mess that up :)
1756     // $rawarray['protection'];
1757     // $rawarray['padding'];
1758     // $rawarray['private'];
1759     // $rawarray['copyright'];
1760     // $rawarray['original'];
1761 
1762     return true;
1763   }
1764 
1765   public static function MPEGaudioHeaderDecode($Header4Bytes) {
1766     // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
1767     // A - Frame sync (all bits set)
1768     // B - MPEG Audio version ID
1769     // C - Layer description
1770     // D - Protection bit
1771     // E - Bitrate index
1772     // F - Sampling rate frequency index
1773     // G - Padding bit
1774     // H - Private bit
1775     // I - Channel Mode
1776     // J - Mode extension (Only if Joint stereo)
1777     // K - Copyright
1778     // L - Original
1779     // M - Emphasis
1780 
1781     if (strlen($Header4Bytes) != 4) {
1782       return false;
1783     }
1784 
1785     $MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
1786     $MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
1787     $MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
1788     $MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
1789     $MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1790     $MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
1791     $MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
1792     $MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
1793     $MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1794     $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
1795     $MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
1796     $MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
1797     $MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
1798 
1799     return $MPEGrawHeader;
1800   }
1801 
1802   public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
1803     static $AudioFrameLengthCache = array();
1804 
1805     if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1806       $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1807       if ($bitrate != 'free') {
1808 
1809         if ($version == '1') {
1810 
1811           if ($layer == '1') {
1812 
1813             // For Layer I slot is 32 bits long
1814             $FrameLengthCoefficient = 48;
1815             $SlotLength = 4;
1816 
1817           } else { // Layer 2 / 3
1818 
1819             // for Layer 2 and Layer 3 slot is 8 bits long.
1820             $FrameLengthCoefficient = 144;
1821             $SlotLength = 1;
1822 
1823           }
1824 
1825         } else { // MPEG-2 / MPEG-2.5
1826 
1827           if ($layer == '1') {
1828 
1829             // For Layer I slot is 32 bits long
1830             $FrameLengthCoefficient = 24;
1831             $SlotLength = 4;
1832 
1833           } elseif ($layer == '2') {
1834 
1835             // for Layer 2 and Layer 3 slot is 8 bits long.
1836             $FrameLengthCoefficient = 144;
1837             $SlotLength = 1;
1838 
1839           } else { // layer 3
1840 
1841             // for Layer 2 and Layer 3 slot is 8 bits long.
1842             $FrameLengthCoefficient = 72;
1843             $SlotLength = 1;
1844 
1845           }
1846 
1847         }
1848 
1849         // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1850         if ($samplerate > 0) {
1851           $NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1852           $NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1853           if ($padding) {
1854             $NewFramelength += $SlotLength;
1855           }
1856           $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1857         }
1858       }
1859     }
1860     return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1861   }
1862 
1863   public static function ClosestStandardMP3Bitrate($bit_rate) {
1864     static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1865     static $bit_rate_table = array (0=>'-');
1866     $round_bit_rate = intval(round($bit_rate, -3));
1867     if (!isset($bit_rate_table[$round_bit_rate])) {
1868       if ($round_bit_rate > max($standard_bit_rates)) {
1869         $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
1870       } else {
1871         $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
1872         foreach ($standard_bit_rates as $standard_bit_rate) {
1873           if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
1874             break;
1875           }
1876           $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1877         }
1878       }
1879     }
1880     return $bit_rate_table[$round_bit_rate];
1881   }
1882 
1883   public static function XingVBRidOffset($version, $channelmode) {
1884     static $XingVBRidOffsetCache = array();
1885     if (empty($XingVBRidOffset)) {
1886       $XingVBRidOffset = array (
1887         '1'   => array ('mono'          => 0x15, // 4 + 17 = 21
1888                 'stereo'        => 0x24, // 4 + 32 = 36
1889                 'joint stereo'  => 0x24,
1890                 'dual channel'  => 0x24
1891                  ),
1892 
1893         '2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
1894                 'stereo'        => 0x15, // 4 + 17 = 21
1895                 'joint stereo'  => 0x15,
1896                 'dual channel'  => 0x15
1897                  ),
1898 
1899         '2.5' => array ('mono'          => 0x15,
1900                 'stereo'        => 0x15,
1901                 'joint stereo'  => 0x15,
1902                 'dual channel'  => 0x15
1903                  )
1904       );
1905     }
1906     return $XingVBRidOffset[$version][$channelmode];
1907   }
1908 
1909   public static function LAMEvbrMethodLookup($VBRmethodID) {
1910     static $LAMEvbrMethodLookup = array(
1911       0x00 => 'unknown',
1912       0x01 => 'cbr',
1913       0x02 => 'abr',
1914       0x03 => 'vbr-old / vbr-rh',
1915       0x04 => 'vbr-new / vbr-mtrh',
1916       0x05 => 'vbr-mt',
1917       0x06 => 'vbr (full vbr method 4)',
1918       0x08 => 'cbr (constant bitrate 2 pass)',
1919       0x09 => 'abr (2 pass)',
1920       0x0F => 'reserved'
1921     );
1922     return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1923   }
1924 
1925   public static function LAMEmiscStereoModeLookup($StereoModeID) {
1926     static $LAMEmiscStereoModeLookup = array(
1927       0 => 'mono',
1928       1 => 'stereo',
1929       2 => 'dual mono',
1930       3 => 'joint stereo',
1931       4 => 'forced stereo',
1932       5 => 'auto',
1933       6 => 'intensity stereo',
1934       7 => 'other'
1935     );
1936     return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1937   }
1938 
1939   public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
1940     static $LAMEmiscSourceSampleFrequencyLookup = array(
1941       0 => '<= 32 kHz',
1942       1 => '44.1 kHz',
1943       2 => '48 kHz',
1944       3 => '> 48kHz'
1945     );
1946     return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1947   }
1948 
1949   public static function LAMEsurroundInfoLookup($SurroundInfoID) {
1950     static $LAMEsurroundInfoLookup = array(
1951       0 => 'no surround info',
1952       1 => 'DPL encoding',
1953       2 => 'DPL2 encoding',
1954       3 => 'Ambisonic encoding'
1955     );
1956     return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1957   }
1958 
1959   public static function LAMEpresetUsedLookup($LAMEtag) {
1960 
1961     if ($LAMEtag['preset_used_id'] == 0) {
1962       // no preset used (LAME >=3.93)
1963       // no preset recorded (LAME <3.93)
1964       return '';
1965     }
1966     $LAMEpresetUsedLookup = array();
1967 
1968     /////  THIS PART CANNOT BE STATIC .
1969     for ($i = 8; $i <= 320; $i++) {
1970       switch ($LAMEtag['vbr_method']) {
1971         case 'cbr':
1972           $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1973           break;
1974         case 'abr':
1975         default: // other VBR modes shouldn't be here(?)
1976           $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1977           break;
1978       }
1979     }
1980 
1981     // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1982 
1983     // named alt-presets
1984     $LAMEpresetUsedLookup[1000] = '--r3mix';
1985     $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1986     $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1987     $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1988     $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1989     $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
1990     $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
1991     $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
1992 
1993     // LAME 3.94 additions/changes
1994     $LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
1995     $LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
1996 
1997     $LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
1998     $LAMEpresetUsedLookup[410]  = '-V9';
1999     $LAMEpresetUsedLookup[420]  = '-V8';
2000     $LAMEpresetUsedLookup[440]  = '-V6';
2001     $LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
2002     $LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
2003     $LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
2004     $LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
2005     $LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
2006     $LAMEpresetUsedLookup[490]  = '-V1';
2007     $LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
2008 
2009     return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
2010   }
2011 
2012 }