wp/wp-includes/ID3/module.audio.mp3.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     1 <?php
       
     2 /////////////////////////////////////////////////////////////////
       
     3 /// getID3() by James Heinrich <info@getid3.org>               //
       
     4 //  available at http://getid3.sourceforge.net                 //
       
     5 //            or http://www.getid3.org                         //
       
     6 /////////////////////////////////////////////////////////////////
       
     7 // See readme.txt for more details                             //
       
     8 /////////////////////////////////////////////////////////////////
       
     9 //                                                             //
       
    10 // module.audio.mp3.php                                        //
       
    11 // module for analyzing MP3 files                              //
       
    12 // dependencies: NONE                                          //
       
    13 //                                                            ///
       
    14 /////////////////////////////////////////////////////////////////
       
    15 
       
    16 
       
    17 // number of frames to scan to determine if MPEG-audio sequence is valid
       
    18 // Lower this number to 5-20 for faster scanning
       
    19 // Increase this number to 50+ for most accurate detection of valid VBR/CBR
       
    20 // mpeg-audio streams
       
    21 define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
       
    22 
       
    23 
       
    24 class getid3_mp3 extends getid3_handler
       
    25 {
       
    26 
       
    27 	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
       
    28 
       
    29 	public function Analyze() {
       
    30 		$info = &$this->getid3->info;
       
    31 
       
    32 		$initialOffset = $info['avdataoffset'];
       
    33 
       
    34 		if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
       
    35 			if ($this->allow_bruteforce) {
       
    36 				$info['error'][] = 'Rescanning file in BruteForce mode';
       
    37 				$this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
       
    38 			}
       
    39 		}
       
    40 
       
    41 
       
    42 		if (isset($info['mpeg']['audio']['bitrate_mode'])) {
       
    43 			$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
       
    44 		}
       
    45 
       
    46 		if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
       
    47 
       
    48 			$synchoffsetwarning = 'Unknown data before synch ';
       
    49 			if (isset($info['id3v2']['headerlength'])) {
       
    50 				$synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
       
    51 			} elseif ($initialOffset > 0) {
       
    52 				$synchoffsetwarning .= '(should be at '.$initialOffset.', ';
       
    53 			} else {
       
    54 				$synchoffsetwarning .= '(should be at beginning of file, ';
       
    55 			}
       
    56 			$synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
       
    57 			if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
       
    58 
       
    59 				if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
       
    60 
       
    61 					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
       
    62 					$info['audio']['codec'] = 'LAME';
       
    63 					$CurrentDataLAMEversionString = 'LAME3.';
       
    64 
       
    65 				} elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
       
    66 
       
    67 					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
       
    68 					$info['audio']['codec'] = 'LAME';
       
    69 					$CurrentDataLAMEversionString = 'LAME3.';
       
    70 
       
    71 				}
       
    72 
       
    73 			}
       
    74 			$info['warning'][] = $synchoffsetwarning;
       
    75 
       
    76 		}
       
    77 
       
    78 		if (isset($info['mpeg']['audio']['LAME'])) {
       
    79 			$info['audio']['codec'] = 'LAME';
       
    80 			if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
       
    81 				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
       
    82 			} elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
       
    83 				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
       
    84 			}
       
    85 		}
       
    86 
       
    87 		$CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
       
    88 		if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
       
    89 			// a version number of LAME that does not end with a number like "LAME3.92"
       
    90 			// or with a closing parenthesis like "LAME3.88 (alpha)"
       
    91 			// or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
       
    92 
       
    93 			// not sure what the actual last frame length will be, but will be less than or equal to 1441
       
    94 			$PossiblyLongerLAMEversion_FrameLength = 1441;
       
    95 
       
    96 			// Not sure what version of LAME this is - look in padding of last frame for longer version string
       
    97 			$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
       
    98 			fseek($this->getid3->fp, $PossibleLAMEversionStringOffset);
       
    99 			$PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength);
       
   100 			switch (substr($CurrentDataLAMEversionString, -1)) {
       
   101 				case 'a':
       
   102 				case 'b':
       
   103 					// "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
       
   104 					// need to trim off "a" to match longer string
       
   105 					$CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
       
   106 					break;
       
   107 			}
       
   108 			if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
       
   109 				if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
       
   110 					$PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
       
   111 					if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
       
   112 						$info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
       
   113 					}
       
   114 				}
       
   115 			}
       
   116 		}
       
   117 		if (!empty($info['audio']['encoder'])) {
       
   118 			$info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
       
   119 		}
       
   120 
       
   121 		switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
       
   122 			case 1:
       
   123 			case 2:
       
   124 				$info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
       
   125 				break;
       
   126 		}
       
   127 		if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
       
   128 			switch ($info['audio']['dataformat']) {
       
   129 				case 'mp1':
       
   130 				case 'mp2':
       
   131 				case 'mp3':
       
   132 					$info['fileformat'] = $info['audio']['dataformat'];
       
   133 					break;
       
   134 
       
   135 				default:
       
   136 					$info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
       
   137 					break;
       
   138 			}
       
   139 		}
       
   140 
       
   141 		if (empty($info['fileformat'])) {
       
   142 			unset($info['fileformat']);
       
   143 			unset($info['audio']['bitrate_mode']);
       
   144 			unset($info['avdataoffset']);
       
   145 			unset($info['avdataend']);
       
   146 			return false;
       
   147 		}
       
   148 
       
   149 		$info['mime_type']         = 'audio/mpeg';
       
   150 		$info['audio']['lossless'] = false;
       
   151 
       
   152 		// Calculate playtime
       
   153 		if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
       
   154 			$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
       
   155 		}
       
   156 
       
   157 		$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
       
   158 
       
   159 		return true;
       
   160 	}
       
   161 
       
   162 
       
   163 	public function GuessEncoderOptions() {
       
   164 		// shortcuts
       
   165 		$info = &$this->getid3->info;
       
   166 		if (!empty($info['mpeg']['audio'])) {
       
   167 			$thisfile_mpeg_audio = &$info['mpeg']['audio'];
       
   168 			if (!empty($thisfile_mpeg_audio['LAME'])) {
       
   169 				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
       
   170 			}
       
   171 		}
       
   172 
       
   173 		$encoder_options = '';
       
   174 		static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
       
   175 
       
   176 		if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
       
   177 
       
   178 			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
       
   179 
       
   180 		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
       
   181 
       
   182 			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
       
   183 
       
   184 		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
       
   185 
       
   186 			static $KnownEncoderValues = array();
       
   187 			if (empty($KnownEncoderValues)) {
       
   188 
       
   189 				//$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
       
   190 				$KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
       
   191 				$KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
       
   192 				$KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
       
   193 				$KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
       
   194 				$KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
       
   195 				$KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
       
   196 				$KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
       
   197 				$KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
       
   198 				$KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
       
   199 				$KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
       
   200 				$KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
       
   201 				$KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
       
   202 				$KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
       
   203 				$KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
       
   204 				$KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
       
   205 				$KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
       
   206 				$KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
       
   207 
       
   208 				$KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
       
   209 				$KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
       
   210 				$KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
       
   211 				$KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
       
   212 				$KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
       
   213 				$KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
       
   214 				$KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
       
   215 				$KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
       
   216 				$KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
       
   217 				$KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
       
   218 				$KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
       
   219 				$KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
       
   220 				$KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
       
   221 				$KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
       
   222 				$KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
       
   223 				$KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
       
   224 				$KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
       
   225 				$KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
       
   226 				$KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
       
   227 				$KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
       
   228 				$KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
       
   229 				$KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
       
   230 				$KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
       
   231 				$KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
       
   232 				$KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
       
   233 				$KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
       
   234 				$KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
       
   235 				$KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
       
   236 				$KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
       
   237 				$KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
       
   238 				$KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
       
   239 				$KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
       
   240 				$KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
       
   241 			}
       
   242 
       
   243 			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']])) {
       
   244 
       
   245 				$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']];
       
   246 
       
   247 			} 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']])) {
       
   248 
       
   249 				$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']];
       
   250 
       
   251 			} elseif ($info['audio']['bitrate_mode'] == 'vbr') {
       
   252 
       
   253 				// http://gabriel.mp3-tech.org/mp3infotag.html
       
   254 				// int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
       
   255 
       
   256 
       
   257 				$LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
       
   258 				$LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
       
   259 				$encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
       
   260 
       
   261 			} elseif ($info['audio']['bitrate_mode'] == 'cbr') {
       
   262 
       
   263 				$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
       
   264 
       
   265 			} else {
       
   266 
       
   267 				$encoder_options = strtoupper($info['audio']['bitrate_mode']);
       
   268 
       
   269 			}
       
   270 
       
   271 		} elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
       
   272 
       
   273 			$encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
       
   274 
       
   275 		} elseif (!empty($info['audio']['bitrate'])) {
       
   276 
       
   277 			if ($info['audio']['bitrate_mode'] == 'cbr') {
       
   278 				$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
       
   279 			} else {
       
   280 				$encoder_options = strtoupper($info['audio']['bitrate_mode']);
       
   281 			}
       
   282 
       
   283 		}
       
   284 		if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
       
   285 			$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
       
   286 		}
       
   287 
       
   288 		if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
       
   289 			$encoder_options .= ' --nogap';
       
   290 		}
       
   291 
       
   292 		if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
       
   293 			$ExplodedOptions = explode(' ', $encoder_options, 4);
       
   294 			if ($ExplodedOptions[0] == '--r3mix') {
       
   295 				$ExplodedOptions[1] = 'r3mix';
       
   296 			}
       
   297 			switch ($ExplodedOptions[0]) {
       
   298 				case '--preset':
       
   299 				case '--alt-preset':
       
   300 				case '--r3mix':
       
   301 					if ($ExplodedOptions[1] == 'fast') {
       
   302 						$ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
       
   303 					}
       
   304 					switch ($ExplodedOptions[1]) {
       
   305 						case 'portable':
       
   306 						case 'medium':
       
   307 						case 'standard':
       
   308 						case 'extreme':
       
   309 						case 'insane':
       
   310 						case 'fast portable':
       
   311 						case 'fast medium':
       
   312 						case 'fast standard':
       
   313 						case 'fast extreme':
       
   314 						case 'fast insane':
       
   315 						case 'r3mix':
       
   316 							static $ExpectedLowpass = array(
       
   317 									'insane|20500'        => 20500,
       
   318 									'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
       
   319 									'medium|18000'        => 18000,
       
   320 									'fast medium|18000'   => 18000,
       
   321 									'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
       
   322 									'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
       
   323 									'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
       
   324 									'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
       
   325 									'standard|19000'      => 19000,
       
   326 									'fast standard|19000' => 19000,
       
   327 									'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
       
   328 									'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
       
   329 									'r3mix|18000'         => 18000,  // 3.94,   3.95
       
   330 								);
       
   331 							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))) {
       
   332 								$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
       
   333 							}
       
   334 							break;
       
   335 
       
   336 						default:
       
   337 							break;
       
   338 					}
       
   339 					break;
       
   340 			}
       
   341 		}
       
   342 
       
   343 		if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
       
   344 			if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
       
   345 				$encoder_options .= ' --resample 44100';
       
   346 			} elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
       
   347 				$encoder_options .= ' --resample 48000';
       
   348 			} elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
       
   349 				switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
       
   350 					case 0: // <= 32000
       
   351 						// may or may not be same as source frequency - ignore
       
   352 						break;
       
   353 					case 1: // 44100
       
   354 					case 2: // 48000
       
   355 					case 3: // 48000+
       
   356 						$ExplodedOptions = explode(' ', $encoder_options, 4);
       
   357 						switch ($ExplodedOptions[0]) {
       
   358 							case '--preset':
       
   359 							case '--alt-preset':
       
   360 								switch ($ExplodedOptions[1]) {
       
   361 									case 'fast':
       
   362 									case 'portable':
       
   363 									case 'medium':
       
   364 									case 'standard':
       
   365 									case 'extreme':
       
   366 									case 'insane':
       
   367 										$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
       
   368 										break;
       
   369 
       
   370 									default:
       
   371 										static $ExpectedResampledRate = array(
       
   372 												'phon+/lw/mw-eu/sw|16000' => 16000,
       
   373 												'mw-us|24000'             => 24000, // 3.95
       
   374 												'mw-us|32000'             => 32000, // 3.93
       
   375 												'mw-us|16000'             => 16000, // 3.92
       
   376 												'phone|16000'             => 16000,
       
   377 												'phone|11025'             => 11025, // 3.94a15
       
   378 												'radio|32000'             => 32000, // 3.94a15
       
   379 												'fm/radio|32000'          => 32000, // 3.92
       
   380 												'fm|32000'                => 32000, // 3.90
       
   381 												'voice|32000'             => 32000);
       
   382 										if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
       
   383 											$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
       
   384 										}
       
   385 										break;
       
   386 								}
       
   387 								break;
       
   388 
       
   389 							case '--r3mix':
       
   390 							default:
       
   391 								$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
       
   392 								break;
       
   393 						}
       
   394 						break;
       
   395 				}
       
   396 			}
       
   397 		}
       
   398 		if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
       
   399 			//$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
       
   400 			$encoder_options = strtoupper($info['audio']['bitrate_mode']);
       
   401 		}
       
   402 
       
   403 		return $encoder_options;
       
   404 	}
       
   405 
       
   406 
       
   407 	public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
       
   408 		static $MPEGaudioVersionLookup;
       
   409 		static $MPEGaudioLayerLookup;
       
   410 		static $MPEGaudioBitrateLookup;
       
   411 		static $MPEGaudioFrequencyLookup;
       
   412 		static $MPEGaudioChannelModeLookup;
       
   413 		static $MPEGaudioModeExtensionLookup;
       
   414 		static $MPEGaudioEmphasisLookup;
       
   415 		if (empty($MPEGaudioVersionLookup)) {
       
   416 			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
       
   417 			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
       
   418 			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
       
   419 			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
       
   420 			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
       
   421 			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
       
   422 			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
       
   423 		}
       
   424 
       
   425 		if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) {
       
   426 			$info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
       
   427 			return false;
       
   428 		}
       
   429 		//$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
       
   430 		$headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
       
   431 
       
   432 		// MP3 audio frame structure:
       
   433 		// $aa $aa $aa $aa [$bb $bb] $cc...
       
   434 		// where $aa..$aa is the four-byte mpeg-audio header (below)
       
   435 		// $bb $bb is the optional 2-byte CRC
       
   436 		// and $cc... is the audio data
       
   437 
       
   438 		$head4 = substr($headerstring, 0, 4);
       
   439 
       
   440 		static $MPEGaudioHeaderDecodeCache = array();
       
   441 		if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
       
   442 			$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
       
   443 		} else {
       
   444 			$MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
       
   445 			$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
       
   446 		}
       
   447 
       
   448 		static $MPEGaudioHeaderValidCache = array();
       
   449 		if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
       
   450 			//$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
       
   451 			$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
       
   452 		}
       
   453 
       
   454 		// shortcut
       
   455 		if (!isset($info['mpeg']['audio'])) {
       
   456 			$info['mpeg']['audio'] = array();
       
   457 		}
       
   458 		$thisfile_mpeg_audio = &$info['mpeg']['audio'];
       
   459 
       
   460 
       
   461 		if ($MPEGaudioHeaderValidCache[$head4]) {
       
   462 			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
       
   463 		} else {
       
   464 			$info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
       
   465 			return false;
       
   466 		}
       
   467 
       
   468 		if (!$FastMPEGheaderScan) {
       
   469 			$thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
       
   470 			$thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
       
   471 
       
   472 			$thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
       
   473 			$thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
       
   474 			$thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
       
   475 			$thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
       
   476 			$thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
       
   477 			$thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
       
   478 			$thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
       
   479 			$thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
       
   480 			$thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
       
   481 
       
   482 			$info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
       
   483 			$info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
       
   484 
       
   485 			if ($thisfile_mpeg_audio['protection']) {
       
   486 				$thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
       
   487 			}
       
   488 		}
       
   489 
       
   490 		if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
       
   491 			// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
       
   492 			$info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
       
   493 			$thisfile_mpeg_audio['raw']['bitrate'] = 0;
       
   494 		}
       
   495 		$thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
       
   496 		$thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
       
   497 
       
   498 		if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
       
   499 			// only skip multiple frame check if free-format bitstream found at beginning of file
       
   500 			// otherwise is quite possibly simply corrupted data
       
   501 			$recursivesearch = false;
       
   502 		}
       
   503 
       
   504 		// For Layer 2 there are some combinations of bitrate and mode which are not allowed.
       
   505 		if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
       
   506 
       
   507 			$info['audio']['dataformat'] = 'mp2';
       
   508 			switch ($thisfile_mpeg_audio['channelmode']) {
       
   509 
       
   510 				case 'mono':
       
   511 					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
       
   512 						// these are ok
       
   513 					} else {
       
   514 						$info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
       
   515 						return false;
       
   516 					}
       
   517 					break;
       
   518 
       
   519 				case 'stereo':
       
   520 				case 'joint stereo':
       
   521 				case 'dual channel':
       
   522 					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
       
   523 						// these are ok
       
   524 					} else {
       
   525 						$info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
       
   526 						return false;
       
   527 					}
       
   528 					break;
       
   529 
       
   530 			}
       
   531 
       
   532 		}
       
   533 
       
   534 
       
   535 		if ($info['audio']['sample_rate'] > 0) {
       
   536 			$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']);
       
   537 		}
       
   538 
       
   539 		$nextframetestoffset = $offset + 1;
       
   540 		if ($thisfile_mpeg_audio['bitrate'] != 'free') {
       
   541 
       
   542 			$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
       
   543 
       
   544 			if (isset($thisfile_mpeg_audio['framelength'])) {
       
   545 				$nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
       
   546 			} else {
       
   547 				$info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
       
   548 				return false;
       
   549 			}
       
   550 
       
   551 		}
       
   552 
       
   553 		$ExpectedNumberOfAudioBytes = 0;
       
   554 
       
   555 		////////////////////////////////////////////////////////////////////////////////////
       
   556 		// Variable-bitrate headers
       
   557 
       
   558 		if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
       
   559 			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
       
   560 			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
       
   561 
       
   562 			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
       
   563 			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
       
   564 			$info['audio']['codec']                = 'Fraunhofer';
       
   565 
       
   566 			$SideInfoData = substr($headerstring, 4 + 2, 32);
       
   567 
       
   568 			$FraunhoferVBROffset = 36;
       
   569 
       
   570 			$thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
       
   571 			$thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
       
   572 			$thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
       
   573 			$thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
       
   574 			$thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
       
   575 			$thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
       
   576 			$thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
       
   577 			$thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
       
   578 			$thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
       
   579 
       
   580 			$ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
       
   581 
       
   582 			$previousbyteoffset = $offset;
       
   583 			for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
       
   584 				$Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
       
   585 				$FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
       
   586 				$thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
       
   587 				$thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
       
   588 				$previousbyteoffset += $Fraunhofer_OffsetN;
       
   589 			}
       
   590 
       
   591 
       
   592 		} else {
       
   593 
       
   594 			// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
       
   595 			// depending on MPEG layer and number of channels
       
   596 
       
   597 			$VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
       
   598 			$SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
       
   599 
       
   600 			if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
       
   601 				// 'Xing' is traditional Xing VBR frame
       
   602 				// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
       
   603 				// 'Info' *can* legally be used to specify a VBR file as well, however.
       
   604 
       
   605 				// http://www.multiweb.cz/twoinches/MP3inside.htm
       
   606 				//00..03 = "Xing" or "Info"
       
   607 				//04..07 = Flags:
       
   608 				//  0x01  Frames Flag     set if value for number of frames in file is stored
       
   609 				//  0x02  Bytes Flag      set if value for filesize in bytes is stored
       
   610 				//  0x04  TOC Flag        set if values for TOC are stored
       
   611 				//  0x08  VBR Scale Flag  set if values for VBR scale is stored
       
   612 				//08..11  Frames: Number of frames in file (including the first Xing/Info one)
       
   613 				//12..15  Bytes:  File length in Bytes
       
   614 				//16..115  TOC (Table of Contents):
       
   615 				//  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
       
   616 				//  Each Byte has a value according this formula:
       
   617 				//  (TOC[i] / 256) * fileLenInBytes
       
   618 				//  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:
       
   619 				//  TOC[(60/240)*100] = TOC[25]
       
   620 				//  and corresponding Byte in file is then approximately at:
       
   621 				//  (TOC[25]/256) * 5000000
       
   622 				//116..119  VBR Scale
       
   623 
       
   624 
       
   625 				// should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
       
   626 //				if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
       
   627 					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
       
   628 					$thisfile_mpeg_audio['VBR_method']   = 'Xing';
       
   629 //				} else {
       
   630 //					$ScanAsCBR = true;
       
   631 //					$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
       
   632 //				}
       
   633 
       
   634 				$thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
       
   635 
       
   636 				$thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
       
   637 				$thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
       
   638 				$thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
       
   639 				$thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
       
   640 
       
   641 				if ($thisfile_mpeg_audio['xing_flags']['frames']) {
       
   642 					$thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
       
   643 					//$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
       
   644 				}
       
   645 				if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
       
   646 					$thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
       
   647 				}
       
   648 
       
   649 				//if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
       
   650 				if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
       
   651 
       
   652 					$framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
       
   653 
       
   654 					if ($thisfile_mpeg_audio['layer'] == '1') {
       
   655 						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
       
   656 						//$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
       
   657 						$info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
       
   658 					} else {
       
   659 						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
       
   660 						//$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
       
   661 						$info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
       
   662 					}
       
   663 					$thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
       
   664 				}
       
   665 
       
   666 				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
       
   667 					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
       
   668 					for ($i = 0; $i < 100; $i++) {
       
   669 						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
       
   670 					}
       
   671 				}
       
   672 				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
       
   673 					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
       
   674 				}
       
   675 
       
   676 
       
   677 				// http://gabriel.mp3-tech.org/mp3infotag.html
       
   678 				if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
       
   679 
       
   680 					// shortcut
       
   681 					$thisfile_mpeg_audio['LAME'] = array();
       
   682 					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
       
   683 
       
   684 
       
   685 					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
       
   686 					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
       
   687 
       
   688 					if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
       
   689 
       
   690 						// extra 11 chars are not part of version string when LAMEtag present
       
   691 						unset($thisfile_mpeg_audio_lame['long_version']);
       
   692 
       
   693 						// It the LAME tag was only introduced in LAME v3.90
       
   694 						// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
       
   695 
       
   696 						// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
       
   697 						// are assuming a 'Xing' identifier offset of 0x24, which is the case for
       
   698 						// MPEG-1 non-mono, but not for other combinations
       
   699 						$LAMEtagOffsetContant = $VBRidOffset - 0x24;
       
   700 
       
   701 						// shortcuts
       
   702 						$thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
       
   703 						$thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
       
   704 						$thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
       
   705 						$thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
       
   706 						$thisfile_mpeg_audio_lame['raw'] = array();
       
   707 						$thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
       
   708 
       
   709 						// byte $9B  VBR Quality
       
   710 						// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
       
   711 						// Actually overwrites original Xing bytes
       
   712 						unset($thisfile_mpeg_audio['VBR_scale']);
       
   713 						$thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
       
   714 
       
   715 						// bytes $9C-$A4  Encoder short VersionString
       
   716 						$thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
       
   717 
       
   718 						// byte $A5  Info Tag revision + VBR method
       
   719 						$LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
       
   720 
       
   721 						$thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
       
   722 						$thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
       
   723 						$thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
       
   724 						$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'
       
   725 
       
   726 						// byte $A6  Lowpass filter value
       
   727 						$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
       
   728 
       
   729 						// bytes $A7-$AE  Replay Gain
       
   730 						// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
       
   731 						// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
       
   732 						if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
       
   733 							// LAME 3.94a16 and later - 9.23 fixed point
       
   734 							// ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
       
   735 							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
       
   736 						} else {
       
   737 							// LAME 3.94a15 and earlier - 32-bit floating point
       
   738 							// Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
       
   739 							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
       
   740 						}
       
   741 						if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
       
   742 							unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
       
   743 						} else {
       
   744 							$thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
       
   745 						}
       
   746 
       
   747 						$thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
       
   748 						$thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
       
   749 
       
   750 
       
   751 						if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
       
   752 
       
   753 							$thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
       
   754 							$thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
       
   755 							$thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
       
   756 							$thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
       
   757 							$thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
       
   758 							$thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
       
   759 							$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']);
       
   760 
       
   761 							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
       
   762 								$info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
       
   763 							}
       
   764 							$info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
       
   765 							$info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
       
   766 						} else {
       
   767 							unset($thisfile_mpeg_audio_lame_RGAD['track']);
       
   768 						}
       
   769 						if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
       
   770 
       
   771 							$thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
       
   772 							$thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
       
   773 							$thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
       
   774 							$thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
       
   775 							$thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
       
   776 							$thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
       
   777 							$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']);
       
   778 
       
   779 							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
       
   780 								$info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
       
   781 							}
       
   782 							$info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
       
   783 							$info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
       
   784 						} else {
       
   785 							unset($thisfile_mpeg_audio_lame_RGAD['album']);
       
   786 						}
       
   787 						if (empty($thisfile_mpeg_audio_lame_RGAD)) {
       
   788 							unset($thisfile_mpeg_audio_lame['RGAD']);
       
   789 						}
       
   790 
       
   791 
       
   792 						// byte $AF  Encoding flags + ATH Type
       
   793 						$EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
       
   794 						$thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
       
   795 						$thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
       
   796 						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
       
   797 						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
       
   798 						$thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
       
   799 
       
   800 						// byte $B0  if ABR {specified bitrate} else {minimal bitrate}
       
   801 						$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
       
   802 						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
       
   803 							$thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
       
   804 						} elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
       
   805 							// ignore
       
   806 						} elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
       
   807 							$thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
       
   808 						}
       
   809 
       
   810 						// bytes $B1-$B3  Encoder delays
       
   811 						$EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
       
   812 						$thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
       
   813 						$thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
       
   814 
       
   815 						// byte $B4  Misc
       
   816 						$MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
       
   817 						$thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
       
   818 						$thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
       
   819 						$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
       
   820 						$thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
       
   821 						$thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
       
   822 						$thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
       
   823 						$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
       
   824 						$thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
       
   825 
       
   826 						// byte $B5  MP3 Gain
       
   827 						$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
       
   828 						$thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
       
   829 						$thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
       
   830 
       
   831 						// bytes $B6-$B7  Preset and surround info
       
   832 						$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
       
   833 						// Reserved                                                    = ($PresetSurroundBytes & 0xC000);
       
   834 						$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
       
   835 						$thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
       
   836 						$thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
       
   837 						$thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
       
   838 						if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
       
   839 							$info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
       
   840 						}
       
   841 						if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
       
   842 							// this may change if 3.90.4 ever comes out
       
   843 							$thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
       
   844 						}
       
   845 
       
   846 						// bytes $B8-$BB  MusicLength
       
   847 						$thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
       
   848 						$ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
       
   849 
       
   850 						// bytes $BC-$BD  MusicCRC
       
   851 						$thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
       
   852 
       
   853 						// bytes $BE-$BF  CRC-16 of Info Tag
       
   854 						$thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
       
   855 
       
   856 
       
   857 						// LAME CBR
       
   858 						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
       
   859 
       
   860 							$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
       
   861 							$thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
       
   862 							$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
       
   863 							//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
       
   864 							//	$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
       
   865 							//}
       
   866 
       
   867 						}
       
   868 
       
   869 					}
       
   870 				}
       
   871 
       
   872 			} else {
       
   873 
       
   874 				// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
       
   875 				$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
       
   876 				if ($recursivesearch) {
       
   877 					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
       
   878 					if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
       
   879 						$recursivesearch = false;
       
   880 						$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
       
   881 					}
       
   882 					if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
       
   883 						$info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
       
   884 					}
       
   885 				}
       
   886 
       
   887 			}
       
   888 
       
   889 		}
       
   890 
       
   891 		if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
       
   892 			if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
       
   893 				if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) {
       
   894 					// ignore, audio data is broken into chunks so will always be data "missing"
       
   895 				} elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
       
   896 					$info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
       
   897 				} else {
       
   898 					$info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)';
       
   899 				}
       
   900 			} else {
       
   901 				if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
       
   902 				//	$prenullbytefileoffset = ftell($this->getid3->fp);
       
   903 				//	fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
       
   904 				//	$PossibleNullByte = fread($this->getid3->fp, 1);
       
   905 				//	fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET);
       
   906 				//	if ($PossibleNullByte === "\x00") {
       
   907 						$info['avdataend']--;
       
   908 				//		$info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
       
   909 				//	} else {
       
   910 				//		$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)';
       
   911 				//	}
       
   912 				} else {
       
   913 					$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)';
       
   914 				}
       
   915 			}
       
   916 		}
       
   917 
       
   918 		if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
       
   919 			if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
       
   920 				$framebytelength = $this->FreeFormatFrameLength($offset, true);
       
   921 				if ($framebytelength > 0) {
       
   922 					$thisfile_mpeg_audio['framelength'] = $framebytelength;
       
   923 					if ($thisfile_mpeg_audio['layer'] == '1') {
       
   924 						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
       
   925 						$info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
       
   926 					} else {
       
   927 						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
       
   928 						$info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
       
   929 					}
       
   930 				} else {
       
   931 					$info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
       
   932 				}
       
   933 			}
       
   934 		}
       
   935 
       
   936 		if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
       
   937 			switch ($thisfile_mpeg_audio['bitrate_mode']) {
       
   938 				case 'vbr':
       
   939 				case 'abr':
       
   940 					$bytes_per_frame = 1152;
       
   941 					if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
       
   942 						$bytes_per_frame = 384;
       
   943 					} elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
       
   944 						$bytes_per_frame = 576;
       
   945 					}
       
   946 					$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);
       
   947 					if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
       
   948 						$info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
       
   949 						$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
       
   950 					}
       
   951 					break;
       
   952 			}
       
   953 		}
       
   954 
       
   955 		// End variable-bitrate headers
       
   956 		////////////////////////////////////////////////////////////////////////////////////
       
   957 
       
   958 		if ($recursivesearch) {
       
   959 
       
   960 			if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
       
   961 				return false;
       
   962 			}
       
   963 
       
   964 		}
       
   965 
       
   966 
       
   967 		//if (false) {
       
   968 		//    // experimental side info parsing section - not returning anything useful yet
       
   969 		//
       
   970 		//    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
       
   971 		//    $SideInfoOffset = 0;
       
   972 		//
       
   973 		//    if ($thisfile_mpeg_audio['version'] == '1') {
       
   974 		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
       
   975 		//            // MPEG-1 (mono)
       
   976 		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
       
   977 		//            $SideInfoOffset += 9;
       
   978 		//            $SideInfoOffset += 5;
       
   979 		//        } else {
       
   980 		//            // MPEG-1 (stereo, joint-stereo, dual-channel)
       
   981 		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
       
   982 		//            $SideInfoOffset += 9;
       
   983 		//            $SideInfoOffset += 3;
       
   984 		//        }
       
   985 		//    } else { // 2 or 2.5
       
   986 		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
       
   987 		//            // MPEG-2, MPEG-2.5 (mono)
       
   988 		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
       
   989 		//            $SideInfoOffset += 8;
       
   990 		//            $SideInfoOffset += 1;
       
   991 		//        } else {
       
   992 		//            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
       
   993 		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
       
   994 		//            $SideInfoOffset += 8;
       
   995 		//            $SideInfoOffset += 2;
       
   996 		//        }
       
   997 		//    }
       
   998 		//
       
   999 		//    if ($thisfile_mpeg_audio['version'] == '1') {
       
  1000 		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
       
  1001 		//            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
       
  1002 		//                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1003 		//                $SideInfoOffset += 2;
       
  1004 		//            }
       
  1005 		//        }
       
  1006 		//    }
       
  1007 		//    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
       
  1008 		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
       
  1009 		//            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
       
  1010 		//            $SideInfoOffset += 12;
       
  1011 		//            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
       
  1012 		//            $SideInfoOffset += 9;
       
  1013 		//            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
       
  1014 		//            $SideInfoOffset += 8;
       
  1015 		//            if ($thisfile_mpeg_audio['version'] == '1') {
       
  1016 		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
       
  1017 		//                $SideInfoOffset += 4;
       
  1018 		//            } else {
       
  1019 		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
       
  1020 		//                $SideInfoOffset += 9;
       
  1021 		//            }
       
  1022 		//            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1023 		//            $SideInfoOffset += 1;
       
  1024 		//
       
  1025 		//            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
       
  1026 		//
       
  1027 		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
       
  1028 		//                $SideInfoOffset += 2;
       
  1029 		//                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1030 		//                $SideInfoOffset += 1;
       
  1031 		//
       
  1032 		//                for ($region = 0; $region < 2; $region++) {
       
  1033 		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
       
  1034 		//                    $SideInfoOffset += 5;
       
  1035 		//                }
       
  1036 		//                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
       
  1037 		//
       
  1038 		//                for ($window = 0; $window < 3; $window++) {
       
  1039 		//                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
       
  1040 		//                    $SideInfoOffset += 3;
       
  1041 		//                }
       
  1042 		//
       
  1043 		//            } else {
       
  1044 		//
       
  1045 		//                for ($region = 0; $region < 3; $region++) {
       
  1046 		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
       
  1047 		//                    $SideInfoOffset += 5;
       
  1048 		//                }
       
  1049 		//
       
  1050 		//                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
       
  1051 		//                $SideInfoOffset += 4;
       
  1052 		//                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
       
  1053 		//                $SideInfoOffset += 3;
       
  1054 		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
       
  1055 		//            }
       
  1056 		//
       
  1057 		//            if ($thisfile_mpeg_audio['version'] == '1') {
       
  1058 		//                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1059 		//                $SideInfoOffset += 1;
       
  1060 		//            }
       
  1061 		//            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1062 		//            $SideInfoOffset += 1;
       
  1063 		//            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
       
  1064 		//            $SideInfoOffset += 1;
       
  1065 		//        }
       
  1066 		//    }
       
  1067 		//}
       
  1068 
       
  1069 		return true;
       
  1070 	}
       
  1071 
       
  1072 	public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
       
  1073 		$info = &$this->getid3->info;
       
  1074 		$firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
       
  1075 		$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
       
  1076 
       
  1077 		for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
       
  1078 			// check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
       
  1079 			if (($nextframetestoffset + 4) >= $info['avdataend']) {
       
  1080 				// end of file
       
  1081 				return true;
       
  1082 			}
       
  1083 
       
  1084 			$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
       
  1085 			if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
       
  1086 				if ($ScanAsCBR) {
       
  1087 					// force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
       
  1088 					if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
       
  1089 						return false;
       
  1090 					}
       
  1091 				}
       
  1092 
       
  1093 
       
  1094 				// next frame is OK, get ready to check the one after that
       
  1095 				if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
       
  1096 					$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
       
  1097 				} else {
       
  1098 					$info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
       
  1099 					return false;
       
  1100 				}
       
  1101 
       
  1102 			} elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
       
  1103 
       
  1104 				// 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
       
  1105 				return true;
       
  1106 
       
  1107 			} else {
       
  1108 
       
  1109 				// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
       
  1110 				$info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
       
  1111 
       
  1112 				return false;
       
  1113 			}
       
  1114 		}
       
  1115 		return true;
       
  1116 	}
       
  1117 
       
  1118 	public function FreeFormatFrameLength($offset, $deepscan=false) {
       
  1119 		$info = &$this->getid3->info;
       
  1120 
       
  1121 		fseek($this->getid3->fp, $offset, SEEK_SET);
       
  1122 		$MPEGaudioData = fread($this->getid3->fp, 32768);
       
  1123 
       
  1124 		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
       
  1125 		// may be different pattern due to padding
       
  1126 		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
       
  1127 		if ($SyncPattern2 === $SyncPattern1) {
       
  1128 			$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
       
  1129 		}
       
  1130 
       
  1131 		$framelength = false;
       
  1132 		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
       
  1133 		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
       
  1134 		if ($framelength1 > 4) {
       
  1135 			$framelength = $framelength1;
       
  1136 		}
       
  1137 		if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
       
  1138 			$framelength = $framelength2;
       
  1139 		}
       
  1140 		if (!$framelength) {
       
  1141 
       
  1142 			// LAME 3.88 has a different value for modeextension on the first frame vs the rest
       
  1143 			$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
       
  1144 			$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
       
  1145 
       
  1146 			if ($framelength1 > 4) {
       
  1147 				$framelength = $framelength1;
       
  1148 			}
       
  1149 			if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
       
  1150 				$framelength = $framelength2;
       
  1151 			}
       
  1152 			if (!$framelength) {
       
  1153 				$info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
       
  1154 				return false;
       
  1155 			} else {
       
  1156 				$info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
       
  1157 				$info['audio']['codec']   = 'LAME';
       
  1158 				$info['audio']['encoder'] = 'LAME3.88';
       
  1159 				$SyncPattern1 = substr($SyncPattern1, 0, 3);
       
  1160 				$SyncPattern2 = substr($SyncPattern2, 0, 3);
       
  1161 			}
       
  1162 		}
       
  1163 
       
  1164 		if ($deepscan) {
       
  1165 
       
  1166 			$ActualFrameLengthValues = array();
       
  1167 			$nextoffset = $offset + $framelength;
       
  1168 			while ($nextoffset < ($info['avdataend'] - 6)) {
       
  1169 				fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET);
       
  1170 				$NextSyncPattern = fread($this->getid3->fp, 6);
       
  1171 				if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
       
  1172 					// good - found where expected
       
  1173 					$ActualFrameLengthValues[] = $framelength;
       
  1174 				} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
       
  1175 					// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
       
  1176 					$ActualFrameLengthValues[] = ($framelength - 1);
       
  1177 					$nextoffset--;
       
  1178 				} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
       
  1179 					// ok - found one byte later than expected (last frame was padded, first frame wasn't)
       
  1180 					$ActualFrameLengthValues[] = ($framelength + 1);
       
  1181 					$nextoffset++;
       
  1182 				} else {
       
  1183 					$info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
       
  1184 					return false;
       
  1185 				}
       
  1186 				$nextoffset += $framelength;
       
  1187 			}
       
  1188 			if (count($ActualFrameLengthValues) > 0) {
       
  1189 				$framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
       
  1190 			}
       
  1191 		}
       
  1192 		return $framelength;
       
  1193 	}
       
  1194 
       
  1195 	public function getOnlyMPEGaudioInfoBruteForce() {
       
  1196 		$MPEGaudioHeaderDecodeCache   = array();
       
  1197 		$MPEGaudioHeaderValidCache    = array();
       
  1198 		$MPEGaudioHeaderLengthCache   = array();
       
  1199 		$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
       
  1200 		$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
       
  1201 		$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
       
  1202 		$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
       
  1203 		$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
       
  1204 		$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
       
  1205 		$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
       
  1206 		$LongMPEGversionLookup        = array();
       
  1207 		$LongMPEGlayerLookup          = array();
       
  1208 		$LongMPEGbitrateLookup        = array();
       
  1209 		$LongMPEGpaddingLookup        = array();
       
  1210 		$LongMPEGfrequencyLookup      = array();
       
  1211 		$Distribution['bitrate']      = array();
       
  1212 		$Distribution['frequency']    = array();
       
  1213 		$Distribution['layer']        = array();
       
  1214 		$Distribution['version']      = array();
       
  1215 		$Distribution['padding']      = array();
       
  1216 
       
  1217 		$info = &$this->getid3->info;
       
  1218 		fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
       
  1219 
       
  1220 		$max_frames_scan = 5000;
       
  1221 		$frames_scanned  = 0;
       
  1222 
       
  1223 		$previousvalidframe = $info['avdataoffset'];
       
  1224 		while (ftell($this->getid3->fp) < $info['avdataend']) {
       
  1225 			set_time_limit(30);
       
  1226 			$head4 = fread($this->getid3->fp, 4);
       
  1227 			if (strlen($head4) < 4) {
       
  1228 				break;
       
  1229 			}
       
  1230 			if ($head4{0} != "\xFF") {
       
  1231 				for ($i = 1; $i < 4; $i++) {
       
  1232 					if ($head4{$i} == "\xFF") {
       
  1233 						fseek($this->getid3->fp, $i - 4, SEEK_CUR);
       
  1234 						continue 2;
       
  1235 					}
       
  1236 				}
       
  1237 				continue;
       
  1238 			}
       
  1239 			if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
       
  1240 				$MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
       
  1241 			}
       
  1242 			if (!isset($MPEGaudioHeaderValidCache[$head4])) {
       
  1243 				$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
       
  1244 			}
       
  1245 			if ($MPEGaudioHeaderValidCache[$head4]) {
       
  1246 
       
  1247 				if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
       
  1248 					$LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
       
  1249 					$LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
       
  1250 					$LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
       
  1251 					$LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
       
  1252 					$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
       
  1253 					$MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
       
  1254 						$LongMPEGbitrateLookup[$head4],
       
  1255 						$LongMPEGversionLookup[$head4],
       
  1256 						$LongMPEGlayerLookup[$head4],
       
  1257 						$LongMPEGpaddingLookup[$head4],
       
  1258 						$LongMPEGfrequencyLookup[$head4]);
       
  1259 				}
       
  1260 				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
       
  1261 					$WhereWeWere = ftell($this->getid3->fp);
       
  1262 					fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
       
  1263 					$next4 = fread($this->getid3->fp, 4);
       
  1264 					if ($next4{0} == "\xFF") {
       
  1265 						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
       
  1266 							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
       
  1267 						}
       
  1268 						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
       
  1269 							$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
       
  1270 						}
       
  1271 						if ($MPEGaudioHeaderValidCache[$next4]) {
       
  1272 							fseek($this->getid3->fp, -4, SEEK_CUR);
       
  1273 
       
  1274 							getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
       
  1275 							getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
       
  1276 							getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
       
  1277 							getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
       
  1278 							getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
       
  1279 							if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
       
  1280 								$pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
       
  1281 								$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.';
       
  1282 								foreach ($Distribution as $key1 => $value1) {
       
  1283 									foreach ($value1 as $key2 => $value2) {
       
  1284 										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
       
  1285 									}
       
  1286 								}
       
  1287 								break;
       
  1288 							}
       
  1289 							continue;
       
  1290 						}
       
  1291 					}
       
  1292 					unset($next4);
       
  1293 					fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET);
       
  1294 				}
       
  1295 
       
  1296 			}
       
  1297 		}
       
  1298 		foreach ($Distribution as $key => $value) {
       
  1299 			ksort($Distribution[$key], SORT_NUMERIC);
       
  1300 		}
       
  1301 		ksort($Distribution['version'], SORT_STRING);
       
  1302 		$info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
       
  1303 		$info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
       
  1304 		$info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
       
  1305 		$info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
       
  1306 		$info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
       
  1307 		if (count($Distribution['version']) > 1) {
       
  1308 			$info['error'][] = 'Corrupt file - more than one MPEG version detected';
       
  1309 		}
       
  1310 		if (count($Distribution['layer']) > 1) {
       
  1311 			$info['error'][] = 'Corrupt file - more than one MPEG layer detected';
       
  1312 		}
       
  1313 		if (count($Distribution['frequency']) > 1) {
       
  1314 			$info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
       
  1315 		}
       
  1316 
       
  1317 
       
  1318 		$bittotal = 0;
       
  1319 		foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
       
  1320 			if ($bitratevalue != 'free') {
       
  1321 				$bittotal += ($bitratevalue * $bitratecount);
       
  1322 			}
       
  1323 		}
       
  1324 		$info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
       
  1325 		if ($info['mpeg']['audio']['frame_count'] == 0) {
       
  1326 			$info['error'][] = 'no MPEG audio frames found';
       
  1327 			return false;
       
  1328 		}
       
  1329 		$info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
       
  1330 		$info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
       
  1331 		$info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
       
  1332 
       
  1333 		$info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
       
  1334 		$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
       
  1335 		$info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
       
  1336 		$info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
       
  1337 		$info['fileformat']            = $info['audio']['dataformat'];
       
  1338 
       
  1339 		return true;
       
  1340 	}
       
  1341 
       
  1342 
       
  1343 	public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
       
  1344 		// looks for synch, decodes MPEG audio header
       
  1345 
       
  1346 		$info = &$this->getid3->info;
       
  1347 
       
  1348 		static $MPEGaudioVersionLookup;
       
  1349 		static $MPEGaudioLayerLookup;
       
  1350 		static $MPEGaudioBitrateLookup;
       
  1351 		if (empty($MPEGaudioVersionLookup)) {
       
  1352 		   $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
       
  1353 		   $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
       
  1354 		   $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
       
  1355 
       
  1356 		}
       
  1357 
       
  1358 		fseek($this->getid3->fp, $avdataoffset, SEEK_SET);
       
  1359 		$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
       
  1360 		if ($sync_seek_buffer_size <= 0) {
       
  1361 			$info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
       
  1362 			return false;
       
  1363 		}
       
  1364 		$header = fread($this->getid3->fp, $sync_seek_buffer_size);
       
  1365 		$sync_seek_buffer_size = strlen($header);
       
  1366 		$SynchSeekOffset = 0;
       
  1367 		while ($SynchSeekOffset < $sync_seek_buffer_size) {
       
  1368 			if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
       
  1369 
       
  1370 				if ($SynchSeekOffset > $sync_seek_buffer_size) {
       
  1371 					// if a synch's not found within the first 128k bytes, then give up
       
  1372 					$info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
       
  1373 					if (isset($info['audio']['bitrate'])) {
       
  1374 						unset($info['audio']['bitrate']);
       
  1375 					}
       
  1376 					if (isset($info['mpeg']['audio'])) {
       
  1377 						unset($info['mpeg']['audio']);
       
  1378 					}
       
  1379 					if (empty($info['mpeg'])) {
       
  1380 						unset($info['mpeg']);
       
  1381 					}
       
  1382 					return false;
       
  1383 
       
  1384 				} elseif (feof($this->getid3->fp)) {
       
  1385 
       
  1386 					$info['error'][] = 'Could not find valid MPEG audio synch before end of file';
       
  1387 					if (isset($info['audio']['bitrate'])) {
       
  1388 						unset($info['audio']['bitrate']);
       
  1389 					}
       
  1390 					if (isset($info['mpeg']['audio'])) {
       
  1391 						unset($info['mpeg']['audio']);
       
  1392 					}
       
  1393 					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
       
  1394 						unset($info['mpeg']);
       
  1395 					}
       
  1396 					return false;
       
  1397 				}
       
  1398 			}
       
  1399 
       
  1400 			if (($SynchSeekOffset + 1) >= strlen($header)) {
       
  1401 				$info['error'][] = 'Could not find valid MPEG synch before end of file';
       
  1402 				return false;
       
  1403 			}
       
  1404 
       
  1405 			if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
       
  1406 				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
       
  1407 					$FirstFrameThisfileInfo = $info;
       
  1408 					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
       
  1409 					if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
       
  1410 						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
       
  1411 						// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
       
  1412 						unset($FirstFrameThisfileInfo);
       
  1413 					}
       
  1414 				}
       
  1415 
       
  1416 				$dummy = $info; // only overwrite real data if valid header found
       
  1417 				if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
       
  1418 					$info = $dummy;
       
  1419 					$info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
       
  1420 					switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
       
  1421 						case '':
       
  1422 						case 'id3':
       
  1423 						case 'ape':
       
  1424 						case 'mp3':
       
  1425 							$info['fileformat']          = 'mp3';
       
  1426 							$info['audio']['dataformat'] = 'mp3';
       
  1427 							break;
       
  1428 					}
       
  1429 					if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
       
  1430 						if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
       
  1431 							// If there is garbage data between a valid VBR header frame and a sequence
       
  1432 							// of valid MPEG-audio frames the VBR data is no longer discarded.
       
  1433 							$info = $FirstFrameThisfileInfo;
       
  1434 							$info['avdataoffset']        = $FirstFrameAVDataOffset;
       
  1435 							$info['fileformat']          = 'mp3';
       
  1436 							$info['audio']['dataformat'] = 'mp3';
       
  1437 							$dummy                       = $info;
       
  1438 							unset($dummy['mpeg']['audio']);
       
  1439 							$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
       
  1440 							$GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
       
  1441 							if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
       
  1442 								$info = $dummy;
       
  1443 								$info['avdataoffset'] = $GarbageOffsetEnd;
       
  1444 								$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;
       
  1445 							} else {
       
  1446 								$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.')';
       
  1447 							}
       
  1448 						}
       
  1449 					}
       
  1450 					if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
       
  1451 						// VBR file with no VBR header
       
  1452 						$BitrateHistogram = true;
       
  1453 					}
       
  1454 
       
  1455 					if ($BitrateHistogram) {
       
  1456 
       
  1457 						$info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
       
  1458 						$info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
       
  1459 
       
  1460 						if ($info['mpeg']['audio']['version'] == '1') {
       
  1461 							if ($info['mpeg']['audio']['layer'] == 3) {
       
  1462 								$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);
       
  1463 							} elseif ($info['mpeg']['audio']['layer'] == 2) {
       
  1464 								$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);
       
  1465 							} elseif ($info['mpeg']['audio']['layer'] == 1) {
       
  1466 								$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);
       
  1467 							}
       
  1468 						} elseif ($info['mpeg']['audio']['layer'] == 1) {
       
  1469 							$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);
       
  1470 						} else {
       
  1471 							$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);
       
  1472 						}
       
  1473 
       
  1474 						$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
       
  1475 						$synchstartoffset = $info['avdataoffset'];
       
  1476 						fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
       
  1477 
       
  1478 						// you can play with these numbers:
       
  1479 						$max_frames_scan  = 50000;
       
  1480 						$max_scan_segments = 10;
       
  1481 
       
  1482 						// don't play with these numbers:
       
  1483 						$FastMode = false;
       
  1484 						$SynchErrorsFound = 0;
       
  1485 						$frames_scanned   = 0;
       
  1486 						$this_scan_segment = 0;
       
  1487 						$frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
       
  1488 						$pct_data_scanned = 0;
       
  1489 						for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
       
  1490 							$frames_scanned_this_segment = 0;
       
  1491 							if (ftell($this->getid3->fp) >= $info['avdataend']) {
       
  1492 								break;
       
  1493 							}
       
  1494 							$scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
       
  1495 							if ($current_segment > 0) {
       
  1496 								fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET);
       
  1497 								$buffer_4k = fread($this->getid3->fp, 4096);
       
  1498 								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
       
  1499 									if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
       
  1500 										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
       
  1501 											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
       
  1502 											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
       
  1503 												$scan_start_offset[$current_segment] += $j;
       
  1504 												break;
       
  1505 											}
       
  1506 										}
       
  1507 									}
       
  1508 								}
       
  1509 							}
       
  1510 							$synchstartoffset = $scan_start_offset[$current_segment];
       
  1511 							while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
       
  1512 								$FastMode = true;
       
  1513 								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
       
  1514 
       
  1515 								if (empty($dummy['mpeg']['audio']['framelength'])) {
       
  1516 									$SynchErrorsFound++;
       
  1517 									$synchstartoffset++;
       
  1518 								} else {
       
  1519 									getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
       
  1520 									getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
       
  1521 									getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
       
  1522 									$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
       
  1523 								}
       
  1524 								$frames_scanned++;
       
  1525 								if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
       
  1526 									$this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
       
  1527 									if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
       
  1528 										// file likely contains < $max_frames_scan, just scan as one segment
       
  1529 										$max_scan_segments = 1;
       
  1530 										$frames_scan_per_segment = $max_frames_scan;
       
  1531 									} else {
       
  1532 										$pct_data_scanned += $this_pct_scanned;
       
  1533 										break;
       
  1534 									}
       
  1535 								}
       
  1536 							}
       
  1537 						}
       
  1538 						if ($pct_data_scanned > 0) {
       
  1539 							$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.';
       
  1540 							foreach ($info['mpeg']['audio'] as $key1 => $value1) {
       
  1541 								if (!preg_match('#_distribution$#i', $key1)) {
       
  1542 									continue;
       
  1543 								}
       
  1544 								foreach ($value1 as $key2 => $value2) {
       
  1545 									$info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
       
  1546 								}
       
  1547 							}
       
  1548 						}
       
  1549 
       
  1550 						if ($SynchErrorsFound > 0) {
       
  1551 							$info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
       
  1552 							//return false;
       
  1553 						}
       
  1554 
       
  1555 						$bittotal     = 0;
       
  1556 						$framecounter = 0;
       
  1557 						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
       
  1558 							$framecounter += $bitratecount;
       
  1559 							if ($bitratevalue != 'free') {
       
  1560 								$bittotal += ($bitratevalue * $bitratecount);
       
  1561 							}
       
  1562 						}
       
  1563 						if ($framecounter == 0) {
       
  1564 							$info['error'][] = 'Corrupt MP3 file: framecounter == zero';
       
  1565 							return false;
       
  1566 						}
       
  1567 						$info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
       
  1568 						$info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
       
  1569 
       
  1570 						$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
       
  1571 
       
  1572 
       
  1573 						// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
       
  1574 						$distinct_bitrates = 0;
       
  1575 						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
       
  1576 							if ($bitrate_count > 0) {
       
  1577 								$distinct_bitrates++;
       
  1578 							}
       
  1579 						}
       
  1580 						if ($distinct_bitrates > 1) {
       
  1581 							$info['mpeg']['audio']['bitrate_mode'] = 'vbr';
       
  1582 						} else {
       
  1583 							$info['mpeg']['audio']['bitrate_mode'] = 'cbr';
       
  1584 						}
       
  1585 						$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
       
  1586 
       
  1587 					}
       
  1588 
       
  1589 					break; // exit while()
       
  1590 				}
       
  1591 			}
       
  1592 
       
  1593 			$SynchSeekOffset++;
       
  1594 			if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
       
  1595 				// end of file/data
       
  1596 
       
  1597 				if (empty($info['mpeg']['audio'])) {
       
  1598 
       
  1599 					$info['error'][] = 'could not find valid MPEG synch before end of file';
       
  1600 					if (isset($info['audio']['bitrate'])) {
       
  1601 						unset($info['audio']['bitrate']);
       
  1602 					}
       
  1603 					if (isset($info['mpeg']['audio'])) {
       
  1604 						unset($info['mpeg']['audio']);
       
  1605 					}
       
  1606 					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
       
  1607 						unset($info['mpeg']);
       
  1608 					}
       
  1609 					return false;
       
  1610 
       
  1611 				}
       
  1612 				break;
       
  1613 			}
       
  1614 
       
  1615 		}
       
  1616 		$info['audio']['channels']        = $info['mpeg']['audio']['channels'];
       
  1617 		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
       
  1618 		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
       
  1619 		return true;
       
  1620 	}
       
  1621 
       
  1622 
       
  1623 	public static function MPEGaudioVersionArray() {
       
  1624 		static $MPEGaudioVersion = array('2.5', false, '2', '1');
       
  1625 		return $MPEGaudioVersion;
       
  1626 	}
       
  1627 
       
  1628 	public static function MPEGaudioLayerArray() {
       
  1629 		static $MPEGaudioLayer = array(false, 3, 2, 1);
       
  1630 		return $MPEGaudioLayer;
       
  1631 	}
       
  1632 
       
  1633 	public static function MPEGaudioBitrateArray() {
       
  1634 		static $MPEGaudioBitrate;
       
  1635 		if (empty($MPEGaudioBitrate)) {
       
  1636 			$MPEGaudioBitrate = array (
       
  1637 				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
       
  1638 								2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
       
  1639 								3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
       
  1640 							   ),
       
  1641 
       
  1642 				'2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
       
  1643 								2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
       
  1644 							   )
       
  1645 			);
       
  1646 			$MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
       
  1647 			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
       
  1648 		}
       
  1649 		return $MPEGaudioBitrate;
       
  1650 	}
       
  1651 
       
  1652 	public static function MPEGaudioFrequencyArray() {
       
  1653 		static $MPEGaudioFrequency;
       
  1654 		if (empty($MPEGaudioFrequency)) {
       
  1655 			$MPEGaudioFrequency = array (
       
  1656 				'1'   => array(44100, 48000, 32000),
       
  1657 				'2'   => array(22050, 24000, 16000),
       
  1658 				'2.5' => array(11025, 12000,  8000)
       
  1659 			);
       
  1660 		}
       
  1661 		return $MPEGaudioFrequency;
       
  1662 	}
       
  1663 
       
  1664 	public static function MPEGaudioChannelModeArray() {
       
  1665 		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
       
  1666 		return $MPEGaudioChannelMode;
       
  1667 	}
       
  1668 
       
  1669 	public static function MPEGaudioModeExtensionArray() {
       
  1670 		static $MPEGaudioModeExtension;
       
  1671 		if (empty($MPEGaudioModeExtension)) {
       
  1672 			$MPEGaudioModeExtension = array (
       
  1673 				1 => array('4-31', '8-31', '12-31', '16-31'),
       
  1674 				2 => array('4-31', '8-31', '12-31', '16-31'),
       
  1675 				3 => array('', 'IS', 'MS', 'IS+MS')
       
  1676 			);
       
  1677 		}
       
  1678 		return $MPEGaudioModeExtension;
       
  1679 	}
       
  1680 
       
  1681 	public static function MPEGaudioEmphasisArray() {
       
  1682 		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
       
  1683 		return $MPEGaudioEmphasis;
       
  1684 	}
       
  1685 
       
  1686 	public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
       
  1687 		return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
       
  1688 	}
       
  1689 
       
  1690 	public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
       
  1691 		if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
       
  1692 			return false;
       
  1693 		}
       
  1694 
       
  1695 		static $MPEGaudioVersionLookup;
       
  1696 		static $MPEGaudioLayerLookup;
       
  1697 		static $MPEGaudioBitrateLookup;
       
  1698 		static $MPEGaudioFrequencyLookup;
       
  1699 		static $MPEGaudioChannelModeLookup;
       
  1700 		static $MPEGaudioModeExtensionLookup;
       
  1701 		static $MPEGaudioEmphasisLookup;
       
  1702 		if (empty($MPEGaudioVersionLookup)) {
       
  1703 			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
       
  1704 			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
       
  1705 			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
       
  1706 			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
       
  1707 			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
       
  1708 			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
       
  1709 			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
       
  1710 		}
       
  1711 
       
  1712 		if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
       
  1713 			$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
       
  1714 		} else {
       
  1715 			echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
       
  1716 			return false;
       
  1717 		}
       
  1718 		if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
       
  1719 			$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
       
  1720 		} else {
       
  1721 			echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
       
  1722 			return false;
       
  1723 		}
       
  1724 		if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
       
  1725 			echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
       
  1726 			if ($rawarray['bitrate'] == 15) {
       
  1727 				// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
       
  1728 				// let it go through here otherwise file will not be identified
       
  1729 				if (!$allowBitrate15) {
       
  1730 					return false;
       
  1731 				}
       
  1732 			} else {
       
  1733 				return false;
       
  1734 			}
       
  1735 		}
       
  1736 		if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
       
  1737 			echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
       
  1738 			return false;
       
  1739 		}
       
  1740 		if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
       
  1741 			echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
       
  1742 			return false;
       
  1743 		}
       
  1744 		if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
       
  1745 			echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
       
  1746 			return false;
       
  1747 		}
       
  1748 		if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
       
  1749 			echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
       
  1750 			return false;
       
  1751 		}
       
  1752 		// These are just either set or not set, you can't mess that up :)
       
  1753 		// $rawarray['protection'];
       
  1754 		// $rawarray['padding'];
       
  1755 		// $rawarray['private'];
       
  1756 		// $rawarray['copyright'];
       
  1757 		// $rawarray['original'];
       
  1758 
       
  1759 		return true;
       
  1760 	}
       
  1761 
       
  1762 	public static function MPEGaudioHeaderDecode($Header4Bytes) {
       
  1763 		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
       
  1764 		// A - Frame sync (all bits set)
       
  1765 		// B - MPEG Audio version ID
       
  1766 		// C - Layer description
       
  1767 		// D - Protection bit
       
  1768 		// E - Bitrate index
       
  1769 		// F - Sampling rate frequency index
       
  1770 		// G - Padding bit
       
  1771 		// H - Private bit
       
  1772 		// I - Channel Mode
       
  1773 		// J - Mode extension (Only if Joint stereo)
       
  1774 		// K - Copyright
       
  1775 		// L - Original
       
  1776 		// M - Emphasis
       
  1777 
       
  1778 		if (strlen($Header4Bytes) != 4) {
       
  1779 			return false;
       
  1780 		}
       
  1781 
       
  1782 		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
       
  1783 		$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
       
  1784 		$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
       
  1785 		$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
       
  1786 		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
       
  1787 		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
       
  1788 		$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
       
  1789 		$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
       
  1790 		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
       
  1791 		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
       
  1792 		$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
       
  1793 		$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
       
  1794 		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
       
  1795 
       
  1796 		return $MPEGrawHeader;
       
  1797 	}
       
  1798 
       
  1799 	public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
       
  1800 		static $AudioFrameLengthCache = array();
       
  1801 
       
  1802 		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
       
  1803 			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
       
  1804 			if ($bitrate != 'free') {
       
  1805 
       
  1806 				if ($version == '1') {
       
  1807 
       
  1808 					if ($layer == '1') {
       
  1809 
       
  1810 						// For Layer I slot is 32 bits long
       
  1811 						$FrameLengthCoefficient = 48;
       
  1812 						$SlotLength = 4;
       
  1813 
       
  1814 					} else { // Layer 2 / 3
       
  1815 
       
  1816 						// for Layer 2 and Layer 3 slot is 8 bits long.
       
  1817 						$FrameLengthCoefficient = 144;
       
  1818 						$SlotLength = 1;
       
  1819 
       
  1820 					}
       
  1821 
       
  1822 				} else { // MPEG-2 / MPEG-2.5
       
  1823 
       
  1824 					if ($layer == '1') {
       
  1825 
       
  1826 						// For Layer I slot is 32 bits long
       
  1827 						$FrameLengthCoefficient = 24;
       
  1828 						$SlotLength = 4;
       
  1829 
       
  1830 					} elseif ($layer == '2') {
       
  1831 
       
  1832 						// for Layer 2 and Layer 3 slot is 8 bits long.
       
  1833 						$FrameLengthCoefficient = 144;
       
  1834 						$SlotLength = 1;
       
  1835 
       
  1836 					} else { // layer 3
       
  1837 
       
  1838 						// for Layer 2 and Layer 3 slot is 8 bits long.
       
  1839 						$FrameLengthCoefficient = 72;
       
  1840 						$SlotLength = 1;
       
  1841 
       
  1842 					}
       
  1843 
       
  1844 				}
       
  1845 
       
  1846 				// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
       
  1847 				if ($samplerate > 0) {
       
  1848 					$NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
       
  1849 					$NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
       
  1850 					if ($padding) {
       
  1851 						$NewFramelength += $SlotLength;
       
  1852 					}
       
  1853 					$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
       
  1854 				}
       
  1855 			}
       
  1856 		}
       
  1857 		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
       
  1858 	}
       
  1859 
       
  1860 	public static function ClosestStandardMP3Bitrate($bit_rate) {
       
  1861 		static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
       
  1862 		static $bit_rate_table = array (0=>'-');
       
  1863 		$round_bit_rate = intval(round($bit_rate, -3));
       
  1864 		if (!isset($bit_rate_table[$round_bit_rate])) {
       
  1865 			if ($round_bit_rate > max($standard_bit_rates)) {
       
  1866 				$bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
       
  1867 			} else {
       
  1868 				$bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
       
  1869 				foreach ($standard_bit_rates as $standard_bit_rate) {
       
  1870 					if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
       
  1871 						break;
       
  1872 					}
       
  1873 					$bit_rate_table[$round_bit_rate] = $standard_bit_rate;
       
  1874 				}
       
  1875 			}
       
  1876 		}
       
  1877 		return $bit_rate_table[$round_bit_rate];
       
  1878 	}
       
  1879 
       
  1880 	public static function XingVBRidOffset($version, $channelmode) {
       
  1881 		static $XingVBRidOffsetCache = array();
       
  1882 		if (empty($XingVBRidOffset)) {
       
  1883 			$XingVBRidOffset = array (
       
  1884 				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
       
  1885 								'stereo'        => 0x24, // 4 + 32 = 36
       
  1886 								'joint stereo'  => 0x24,
       
  1887 								'dual channel'  => 0x24
       
  1888 							   ),
       
  1889 
       
  1890 				'2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
       
  1891 								'stereo'        => 0x15, // 4 + 17 = 21
       
  1892 								'joint stereo'  => 0x15,
       
  1893 								'dual channel'  => 0x15
       
  1894 							   ),
       
  1895 
       
  1896 				'2.5' => array ('mono'          => 0x15,
       
  1897 								'stereo'        => 0x15,
       
  1898 								'joint stereo'  => 0x15,
       
  1899 								'dual channel'  => 0x15
       
  1900 							   )
       
  1901 			);
       
  1902 		}
       
  1903 		return $XingVBRidOffset[$version][$channelmode];
       
  1904 	}
       
  1905 
       
  1906 	public static function LAMEvbrMethodLookup($VBRmethodID) {
       
  1907 		static $LAMEvbrMethodLookup = array(
       
  1908 			0x00 => 'unknown',
       
  1909 			0x01 => 'cbr',
       
  1910 			0x02 => 'abr',
       
  1911 			0x03 => 'vbr-old / vbr-rh',
       
  1912 			0x04 => 'vbr-new / vbr-mtrh',
       
  1913 			0x05 => 'vbr-mt',
       
  1914 			0x06 => 'vbr (full vbr method 4)',
       
  1915 			0x08 => 'cbr (constant bitrate 2 pass)',
       
  1916 			0x09 => 'abr (2 pass)',
       
  1917 			0x0F => 'reserved'
       
  1918 		);
       
  1919 		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
       
  1920 	}
       
  1921 
       
  1922 	public static function LAMEmiscStereoModeLookup($StereoModeID) {
       
  1923 		static $LAMEmiscStereoModeLookup = array(
       
  1924 			0 => 'mono',
       
  1925 			1 => 'stereo',
       
  1926 			2 => 'dual mono',
       
  1927 			3 => 'joint stereo',
       
  1928 			4 => 'forced stereo',
       
  1929 			5 => 'auto',
       
  1930 			6 => 'intensity stereo',
       
  1931 			7 => 'other'
       
  1932 		);
       
  1933 		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
       
  1934 	}
       
  1935 
       
  1936 	public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
       
  1937 		static $LAMEmiscSourceSampleFrequencyLookup = array(
       
  1938 			0 => '<= 32 kHz',
       
  1939 			1 => '44.1 kHz',
       
  1940 			2 => '48 kHz',
       
  1941 			3 => '> 48kHz'
       
  1942 		);
       
  1943 		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
       
  1944 	}
       
  1945 
       
  1946 	public static function LAMEsurroundInfoLookup($SurroundInfoID) {
       
  1947 		static $LAMEsurroundInfoLookup = array(
       
  1948 			0 => 'no surround info',
       
  1949 			1 => 'DPL encoding',
       
  1950 			2 => 'DPL2 encoding',
       
  1951 			3 => 'Ambisonic encoding'
       
  1952 		);
       
  1953 		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
       
  1954 	}
       
  1955 
       
  1956 	public static function LAMEpresetUsedLookup($LAMEtag) {
       
  1957 
       
  1958 		if ($LAMEtag['preset_used_id'] == 0) {
       
  1959 			// no preset used (LAME >=3.93)
       
  1960 			// no preset recorded (LAME <3.93)
       
  1961 			return '';
       
  1962 		}
       
  1963 		$LAMEpresetUsedLookup = array();
       
  1964 
       
  1965 		/////  THIS PART CANNOT BE STATIC .
       
  1966 		for ($i = 8; $i <= 320; $i++) {
       
  1967 			switch ($LAMEtag['vbr_method']) {
       
  1968 				case 'cbr':
       
  1969 					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
       
  1970 					break;
       
  1971 				case 'abr':
       
  1972 				default: // other VBR modes shouldn't be here(?)
       
  1973 					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
       
  1974 					break;
       
  1975 			}
       
  1976 		}
       
  1977 
       
  1978 		// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
       
  1979 
       
  1980 		// named alt-presets
       
  1981 		$LAMEpresetUsedLookup[1000] = '--r3mix';
       
  1982 		$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
       
  1983 		$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
       
  1984 		$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
       
  1985 		$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
       
  1986 		$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
       
  1987 		$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
       
  1988 		$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
       
  1989 
       
  1990 		// LAME 3.94 additions/changes
       
  1991 		$LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
       
  1992 		$LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
       
  1993 
       
  1994 		$LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
       
  1995 		$LAMEpresetUsedLookup[410]  = '-V9';
       
  1996 		$LAMEpresetUsedLookup[420]  = '-V8';
       
  1997 		$LAMEpresetUsedLookup[440]  = '-V6';
       
  1998 		$LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
       
  1999 		$LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
       
  2000 		$LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
       
  2001 		$LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
       
  2002 		$LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
       
  2003 		$LAMEpresetUsedLookup[490]  = '-V1';
       
  2004 		$LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
       
  2005 
       
  2006 		return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
       
  2007 	}
       
  2008 
       
  2009 }