wp/wp-includes/ID3/module.audio.mp3.php
changeset 16 a86126ab1dd4
parent 7 cf61fcea0001
child 19 3d72ae0968f4
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     1 <?php
     1 <?php
       
     2 
     2 /////////////////////////////////////////////////////////////////
     3 /////////////////////////////////////////////////////////////////
     3 /// getID3() by James Heinrich <info@getid3.org>               //
     4 /// getID3() by James Heinrich <info@getid3.org>               //
     4 //  available at http://getid3.sourceforge.net                 //
     5 //  available at https://github.com/JamesHeinrich/getID3       //
     5 //            or http://www.getid3.org                         //
     6 //            or https://www.getid3.org                        //
     6 //          also https://github.com/JamesHeinrich/getID3       //
     7 //            or http://getid3.sourceforge.net                 //
     7 /////////////////////////////////////////////////////////////////
     8 //  see readme.txt for more details                            //
     8 // See readme.txt for more details                             //
       
     9 /////////////////////////////////////////////////////////////////
     9 /////////////////////////////////////////////////////////////////
    10 //                                                             //
    10 //                                                             //
    11 // module.audio.mp3.php                                        //
    11 // module.audio.mp3.php                                        //
    12 // module for analyzing MP3 files                              //
    12 // module for analyzing MP3 files                              //
    13 // dependencies: NONE                                          //
    13 // dependencies: NONE                                          //
    14 //                                                            ///
    14 //                                                            ///
    15 /////////////////////////////////////////////////////////////////
    15 /////////////////////////////////////////////////////////////////
    16 
    16 
       
    17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
       
    18 	exit;
       
    19 }
    17 
    20 
    18 // number of frames to scan to determine if MPEG-audio sequence is valid
    21 // number of frames to scan to determine if MPEG-audio sequence is valid
    19 // Lower this number to 5-20 for faster scanning
    22 // Lower this number to 5-20 for faster scanning
    20 // Increase this number to 50+ for most accurate detection of valid VBR/CBR
    23 // Increase this number to 50+ for most accurate detection of valid VBR/CBR
    21 // mpeg-audio streams
    24 // mpeg-audio streams
    22 define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
    25 define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
    23 
    26 
    24 
    27 
    25 class getid3_mp3 extends getid3_handler
    28 class getid3_mp3 extends getid3_handler
    26 {
    29 {
    27 
    30 	/**
    28 	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
    31 	 * Forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow,
    29 
    32 	 * unrecommended, but may provide data from otherwise-unusable files.
       
    33 	 *
       
    34 	 * @var bool
       
    35 	 */
       
    36 	public $allow_bruteforce = false;
       
    37 
       
    38 	/**
       
    39 	 * @return bool
       
    40 	 */
    30 	public function Analyze() {
    41 	public function Analyze() {
    31 		$info = &$this->getid3->info;
    42 		$info = &$this->getid3->info;
    32 
    43 
    33 		$initialOffset = $info['avdataoffset'];
    44 		$initialOffset = $info['avdataoffset'];
    34 
    45 
    35 		if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
    46 		if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
    36 			if ($this->allow_bruteforce) {
    47 			if ($this->allow_bruteforce) {
    37 				$this->error('Rescanning file in BruteForce mode');
    48 				$this->error('Rescanning file in BruteForce mode');
    38 				$this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
    49 				$this->getOnlyMPEGaudioInfoBruteForce();
    39 			}
    50 			}
    40 		}
    51 		}
    41 
    52 
    42 
    53 
    43 		if (isset($info['mpeg']['audio']['bitrate_mode'])) {
    54 		if (isset($info['mpeg']['audio']['bitrate_mode'])) {
   150 		$info['mime_type']         = 'audio/mpeg';
   161 		$info['mime_type']         = 'audio/mpeg';
   151 		$info['audio']['lossless'] = false;
   162 		$info['audio']['lossless'] = false;
   152 
   163 
   153 		// Calculate playtime
   164 		// Calculate playtime
   154 		if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
   165 		if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
   155 			$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
   166 			// https://github.com/JamesHeinrich/getID3/issues/161
       
   167 			// VBR header frame contains ~0.026s of silent audio data, but is not actually part of the original encoding and should be ignored
       
   168 			$xingVBRheaderFrameLength = ((isset($info['mpeg']['audio']['VBR_frames']) && isset($info['mpeg']['audio']['framelength'])) ? $info['mpeg']['audio']['framelength'] : 0);
       
   169 
       
   170 			$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset'] - $xingVBRheaderFrameLength) * 8 / $info['audio']['bitrate'];
   156 		}
   171 		}
   157 
   172 
   158 		$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
   173 		$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
   159 
   174 
   160 		return true;
   175 		return true;
   161 	}
   176 	}
   162 
   177 
   163 
   178 	/**
       
   179 	 * @return string
       
   180 	 */
   164 	public function GuessEncoderOptions() {
   181 	public function GuessEncoderOptions() {
   165 		// shortcuts
   182 		// shortcuts
   166 		$info = &$this->getid3->info;
   183 		$info = &$this->getid3->info;
       
   184 		$thisfile_mpeg_audio = array();
       
   185 		$thisfile_mpeg_audio_lame = array();
   167 		if (!empty($info['mpeg']['audio'])) {
   186 		if (!empty($info['mpeg']['audio'])) {
   168 			$thisfile_mpeg_audio = &$info['mpeg']['audio'];
   187 			$thisfile_mpeg_audio = &$info['mpeg']['audio'];
   169 			if (!empty($thisfile_mpeg_audio['LAME'])) {
   188 			if (!empty($thisfile_mpeg_audio['LAME'])) {
   170 				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
   189 				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
   171 			}
   190 			}
   176 
   195 
   177 		if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
   196 		if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
   178 
   197 
   179 			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
   198 			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
   180 
   199 
   181 		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
   200 		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && isset($thisfile_mpeg_audio_lame['preset_used_id']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
   182 
   201 
   183 			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
   202 			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
   184 
   203 
   185 		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
   204 		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
   186 
   205 
   402 		}
   421 		}
   403 
   422 
   404 		return $encoder_options;
   423 		return $encoder_options;
   405 	}
   424 	}
   406 
   425 
   407 
   426 	/**
       
   427 	 * @param int   $offset
       
   428 	 * @param array $info
       
   429 	 * @param bool  $recursivesearch
       
   430 	 * @param bool  $ScanAsCBR
       
   431 	 * @param bool  $FastMPEGheaderScan
       
   432 	 *
       
   433 	 * @return bool
       
   434 	 */
   408 	public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
   435 	public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
   409 		static $MPEGaudioVersionLookup;
   436 		static $MPEGaudioVersionLookup;
   410 		static $MPEGaudioLayerLookup;
   437 		static $MPEGaudioLayerLookup;
   411 		static $MPEGaudioBitrateLookup;
   438 		static $MPEGaudioBitrateLookup;
   412 		static $MPEGaudioFrequencyLookup;
   439 		static $MPEGaudioFrequencyLookup;
   456 		if (!isset($info['mpeg']['audio'])) {
   483 		if (!isset($info['mpeg']['audio'])) {
   457 			$info['mpeg']['audio'] = array();
   484 			$info['mpeg']['audio'] = array();
   458 		}
   485 		}
   459 		$thisfile_mpeg_audio = &$info['mpeg']['audio'];
   486 		$thisfile_mpeg_audio = &$info['mpeg']['audio'];
   460 
   487 
   461 
       
   462 		if ($MPEGaudioHeaderValidCache[$head4_key]) {
   488 		if ($MPEGaudioHeaderValidCache[$head4_key]) {
   463 			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
   489 			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
   464 		} else {
   490 		} else {
   465 			$this->error('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset);
   491 			$this->error('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset);
   466 			return false;
   492 			return false;
   560 			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
   586 			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
   561 			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
   587 			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
   562 
   588 
   563 			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
   589 			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
   564 			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
   590 			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
   565 			$info['audio']['codec']                = 'Fraunhofer';
   591 			$info['audio']['codec']              = 'Fraunhofer';
   566 
   592 
   567 			$SideInfoData = substr($headerstring, 4 + 2, 32);
   593 			$SideInfoData = substr($headerstring, 4 + 2, 32);
   568 
   594 
   569 			$FraunhoferVBROffset = 36;
   595 			$FraunhoferVBROffset = 36;
   570 
   596 
   653 					$used_filesize  = 0;
   679 					$used_filesize  = 0;
   654 					if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
   680 					if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
   655 						$used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
   681 						$used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
   656 					} elseif (!empty($info['filesize'])) {
   682 					} elseif (!empty($info['filesize'])) {
   657 						$used_filesize  = $info['filesize'];
   683 						$used_filesize  = $info['filesize'];
   658 						$used_filesize -= intval(@$info['id3v2']['headerlength']);
   684 						$used_filesize -= (isset($info['id3v2']['headerlength']) ? intval($info['id3v2']['headerlength']) : 0);
   659 						$used_filesize -= (isset($info['id3v1']) ? 128 : 0);
   685 						$used_filesize -= (isset($info['id3v1']) ? 128 : 0);
   660 						$used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
   686 						$used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
   661 						$this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes');
   687 						$this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes');
   662 					}
   688 					}
   663 
   689 
   676 				}
   702 				}
   677 
   703 
   678 				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
   704 				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
   679 					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
   705 					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
   680 					for ($i = 0; $i < 100; $i++) {
   706 					for ($i = 0; $i < 100; $i++) {
   681 						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
   707 						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
   682 					}
   708 					}
   683 				}
   709 				}
   684 				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
   710 				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
   685 					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
   711 					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
   686 				}
   712 				}
   694 					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
   720 					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
   695 
   721 
   696 
   722 
   697 					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
   723 					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
   698 					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
   724 					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
   699 
   725 					$thisfile_mpeg_audio_lame['numeric_version'] = str_replace('LAME', '', $thisfile_mpeg_audio_lame['short_version']);
   700 					if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
   726 					if (preg_match('#^LAME([0-9\\.a-z]+)#', $thisfile_mpeg_audio_lame['long_version'], $matches)) {
       
   727 						$thisfile_mpeg_audio_lame['short_version']   = $matches[0];
       
   728 						$thisfile_mpeg_audio_lame['numeric_version'] = $matches[1];
       
   729 					}
       
   730 					foreach (explode('.', $thisfile_mpeg_audio_lame['numeric_version']) as $key => $number) {
       
   731 						$thisfile_mpeg_audio_lame['integer_version'][$key] = intval($number);
       
   732 					}
       
   733 
       
   734 					//if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
       
   735 					if ((($thisfile_mpeg_audio_lame['integer_version'][0] * 1000) + $thisfile_mpeg_audio_lame['integer_version'][1]) >= 3090) { // cannot use string version compare, may have "LAME3.90" or "LAME3.100" -- see https://github.com/JamesHeinrich/getID3/issues/207
   701 
   736 
   702 						// extra 11 chars are not part of version string when LAMEtag present
   737 						// extra 11 chars are not part of version string when LAMEtag present
   703 						unset($thisfile_mpeg_audio_lame['long_version']);
   738 						unset($thisfile_mpeg_audio_lame['long_version']);
   704 
   739 
   705 						// It the LAME tag was only introduced in LAME v3.90
   740 						// It the LAME tag was only introduced in LAME v3.90
  1081 		//}
  1116 		//}
  1082 
  1117 
  1083 		return true;
  1118 		return true;
  1084 	}
  1119 	}
  1085 
  1120 
       
  1121 	/**
       
  1122 	 * @param int $offset
       
  1123 	 * @param int $nextframetestoffset
       
  1124 	 * @param bool $ScanAsCBR
       
  1125 	 *
       
  1126 	 * @return bool
       
  1127 	 */
  1086 	public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
  1128 	public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
  1087 		$info = &$this->getid3->info;
  1129 		$info = &$this->getid3->info;
  1088 		$firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
  1130 		$firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
  1089 		$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
  1131 		$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
  1090 
  1132 
  1127 			}
  1169 			}
  1128 		}
  1170 		}
  1129 		return true;
  1171 		return true;
  1130 	}
  1172 	}
  1131 
  1173 
       
  1174 	/**
       
  1175 	 * @param int  $offset
       
  1176 	 * @param bool $deepscan
       
  1177 	 *
       
  1178 	 * @return int|false
       
  1179 	 */
  1132 	public function FreeFormatFrameLength($offset, $deepscan=false) {
  1180 	public function FreeFormatFrameLength($offset, $deepscan=false) {
  1133 		$info = &$this->getid3->info;
  1181 		$info = &$this->getid3->info;
  1134 
  1182 
  1135 		$this->fseek($offset);
  1183 		$this->fseek($offset);
  1136 		$MPEGaudioData = $this->fread(32768);
  1184 		$MPEGaudioData = $this->fread(32768);
  1137 
  1185 
  1138 		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
  1186 		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
  1139 		// may be different pattern due to padding
  1187 		// may be different pattern due to padding
  1140 		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
  1188 		$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
  1141 		if ($SyncPattern2 === $SyncPattern1) {
  1189 		if ($SyncPattern2 === $SyncPattern1) {
  1142 			$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
  1190 			$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
  1143 		}
  1191 		}
  1144 
  1192 
  1145 		$framelength = false;
  1193 		$framelength = false;
  1146 		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
  1194 		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
  1147 		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
  1195 		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
  1204 			}
  1252 			}
  1205 		}
  1253 		}
  1206 		return $framelength;
  1254 		return $framelength;
  1207 	}
  1255 	}
  1208 
  1256 
       
  1257 	/**
       
  1258 	 * @return bool
       
  1259 	 */
  1209 	public function getOnlyMPEGaudioInfoBruteForce() {
  1260 	public function getOnlyMPEGaudioInfoBruteForce() {
  1210 		$MPEGaudioHeaderDecodeCache   = array();
  1261 		$MPEGaudioHeaderDecodeCache   = array();
  1211 		$MPEGaudioHeaderValidCache    = array();
  1262 		$MPEGaudioHeaderValidCache    = array();
  1212 		$MPEGaudioHeaderLengthCache   = array();
  1263 		$MPEGaudioHeaderLengthCache   = array();
  1213 		$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
  1264 		$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
  1239 			set_time_limit(30);
  1290 			set_time_limit(30);
  1240 			$head4 = $this->fread(4);
  1291 			$head4 = $this->fread(4);
  1241 			if (strlen($head4) < 4) {
  1292 			if (strlen($head4) < 4) {
  1242 				break;
  1293 				break;
  1243 			}
  1294 			}
  1244 			if ($head4{0} != "\xFF") {
  1295 			if ($head4[0] != "\xFF") {
  1245 				for ($i = 1; $i < 4; $i++) {
  1296 				for ($i = 1; $i < 4; $i++) {
  1246 					if ($head4{$i} == "\xFF") {
  1297 					if ($head4[$i] == "\xFF") {
  1247 						$this->fseek($i - 4, SEEK_CUR);
  1298 						$this->fseek($i - 4, SEEK_CUR);
  1248 						continue 2;
  1299 						continue 2;
  1249 					}
  1300 					}
  1250 				}
  1301 				}
  1251 				continue;
  1302 				continue;
  1273 				}
  1324 				}
  1274 				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
  1325 				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
  1275 					$WhereWeWere = $this->ftell();
  1326 					$WhereWeWere = $this->ftell();
  1276 					$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
  1327 					$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
  1277 					$next4 = $this->fread(4);
  1328 					$next4 = $this->fread(4);
  1278 					if ($next4{0} == "\xFF") {
  1329 					if ($next4[0] == "\xFF") {
  1279 						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
  1330 						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
  1280 							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
  1331 							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
  1281 						}
  1332 						}
  1282 						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
  1333 						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
  1283 							$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
  1334 							$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
  1284 						}
  1335 						}
  1285 						if ($MPEGaudioHeaderValidCache[$next4]) {
  1336 						if ($MPEGaudioHeaderValidCache[$next4]) {
  1286 							$this->fseek(-4, SEEK_CUR);
  1337 							$this->fseek(-4, SEEK_CUR);
  1287 
  1338 
  1288 							getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
  1339 							$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] = isset($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]) ? ++$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] : 1;
  1289 							getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
  1340 							$Distribution['layer'][$LongMPEGlayerLookup[$head4]] = isset($Distribution['layer'][$LongMPEGlayerLookup[$head4]]) ? ++$Distribution['layer'][$LongMPEGlayerLookup[$head4]] : 1;
  1290 							getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
  1341 							$Distribution['version'][$LongMPEGversionLookup[$head4]] = isset($Distribution['version'][$LongMPEGversionLookup[$head4]]) ? ++$Distribution['version'][$LongMPEGversionLookup[$head4]] : 1;
  1291 							getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
  1342 							$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] = isset($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]) ? ++$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] : 1;
  1292 							getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
  1343 							$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] = isset($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]) ? ++$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] : 1;
  1293 							if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
  1344 							if (++$frames_scanned >= $max_frames_scan) {
  1294 								$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
  1345 								$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
  1295 								$this->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.');
  1346 								$this->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.');
  1296 								foreach ($Distribution as $key1 => $value1) {
  1347 								foreach ($Distribution as $key1 => $value1) {
  1297 									foreach ($value1 as $key2 => $value2) {
  1348 									foreach ($value1 as $key2 => $value2) {
  1298 										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
  1349 										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
  1351 		$info['fileformat']            = $info['audio']['dataformat'];
  1402 		$info['fileformat']            = $info['audio']['dataformat'];
  1352 
  1403 
  1353 		return true;
  1404 		return true;
  1354 	}
  1405 	}
  1355 
  1406 
  1356 
  1407 	/**
       
  1408 	 * @param int  $avdataoffset
       
  1409 	 * @param bool $BitrateHistogram
       
  1410 	 *
       
  1411 	 * @return bool
       
  1412 	 */
  1357 	public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
  1413 	public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
  1358 		// looks for synch, decodes MPEG audio header
  1414 		// looks for synch, decodes MPEG audio header
  1359 
  1415 
  1360 		$info = &$this->getid3->info;
  1416 		$info = &$this->getid3->info;
  1361 
  1417 
  1362 		static $MPEGaudioVersionLookup;
  1418 		static $MPEGaudioVersionLookup;
  1363 		static $MPEGaudioLayerLookup;
  1419 		static $MPEGaudioLayerLookup;
  1364 		static $MPEGaudioBitrateLookup;
  1420 		static $MPEGaudioBitrateLookup;
  1365 		if (empty($MPEGaudioVersionLookup)) {
  1421 		if (empty($MPEGaudioVersionLookup)) {
  1366 		   $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
  1422 			$MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
  1367 		   $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
  1423 			$MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
  1368 		   $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
  1424 			$MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
  1369 
       
  1370 		}
  1425 		}
  1371 
  1426 
  1372 		$this->fseek($avdataoffset);
  1427 		$this->fseek($avdataoffset);
  1373 		$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
  1428 		$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
  1374 		if ($sync_seek_buffer_size <= 0) {
  1429 		if ($sync_seek_buffer_size <= 0) {
  1414 			if (($SynchSeekOffset + 1) >= strlen($header)) {
  1469 			if (($SynchSeekOffset + 1) >= strlen($header)) {
  1415 				$this->error('Could not find valid MPEG synch before end of file');
  1470 				$this->error('Could not find valid MPEG synch before end of file');
  1416 				return false;
  1471 				return false;
  1417 			}
  1472 			}
  1418 
  1473 
  1419 			if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
  1474 			if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected
       
  1475 				$FirstFrameAVDataOffset = null;
  1420 				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
  1476 				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
  1421 					$FirstFrameThisfileInfo = $info;
  1477 					$FirstFrameThisfileInfo = $info;
  1422 					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
  1478 					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
  1423 					if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
  1479 					if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
  1424 						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
  1480 						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
  1438 						case 'mp3':
  1494 						case 'mp3':
  1439 							$info['fileformat']          = 'mp3';
  1495 							$info['fileformat']          = 'mp3';
  1440 							$info['audio']['dataformat'] = 'mp3';
  1496 							$info['audio']['dataformat'] = 'mp3';
  1441 							break;
  1497 							break;
  1442 					}
  1498 					}
  1443 					if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
  1499 					if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
  1444 						if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
  1500 						if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
  1445 							// If there is garbage data between a valid VBR header frame and a sequence
  1501 							// If there is garbage data between a valid VBR header frame and a sequence
  1446 							// of valid MPEG-audio frames the VBR data is no longer discarded.
  1502 							// of valid MPEG-audio frames the VBR data is no longer discarded.
  1447 							$info = $FirstFrameThisfileInfo;
  1503 							$info = $FirstFrameThisfileInfo;
  1448 							$info['avdataoffset']        = $FirstFrameAVDataOffset;
  1504 							$info['avdataoffset']        = $FirstFrameAVDataOffset;
  1508 							$scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
  1564 							$scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
  1509 							if ($current_segment > 0) {
  1565 							if ($current_segment > 0) {
  1510 								$this->fseek($scan_start_offset[$current_segment]);
  1566 								$this->fseek($scan_start_offset[$current_segment]);
  1511 								$buffer_4k = $this->fread(4096);
  1567 								$buffer_4k = $this->fread(4096);
  1512 								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
  1568 								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
  1513 									if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
  1569 									if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected
  1514 										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
  1570 										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
  1515 											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
  1571 											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
  1516 											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
  1572 											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
  1517 												$scan_start_offset[$current_segment] += $j;
  1573 												$scan_start_offset[$current_segment] += $j;
  1518 												break;
  1574 												break;
  1520 										}
  1576 										}
  1521 									}
  1577 									}
  1522 								}
  1578 								}
  1523 							}
  1579 							}
  1524 							$synchstartoffset = $scan_start_offset[$current_segment];
  1580 							$synchstartoffset = $scan_start_offset[$current_segment];
  1525 							while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
  1581 							while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
  1526 								$FastMode = true;
  1582 								$FastMode = true;
  1527 								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
  1583 								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
  1528 
  1584 
  1529 								if (empty($dummy['mpeg']['audio']['framelength'])) {
  1585 								if (empty($dummy['mpeg']['audio']['framelength'])) {
  1530 									$SynchErrorsFound++;
  1586 									$SynchErrorsFound++;
  1631 		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
  1687 		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
  1632 		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
  1688 		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
  1633 		return true;
  1689 		return true;
  1634 	}
  1690 	}
  1635 
  1691 
  1636 
  1692 	/**
       
  1693 	 * @return array
       
  1694 	 */
  1637 	public static function MPEGaudioVersionArray() {
  1695 	public static function MPEGaudioVersionArray() {
  1638 		static $MPEGaudioVersion = array('2.5', false, '2', '1');
  1696 		static $MPEGaudioVersion = array('2.5', false, '2', '1');
  1639 		return $MPEGaudioVersion;
  1697 		return $MPEGaudioVersion;
  1640 	}
  1698 	}
  1641 
  1699 
       
  1700 	/**
       
  1701 	 * @return array
       
  1702 	 */
  1642 	public static function MPEGaudioLayerArray() {
  1703 	public static function MPEGaudioLayerArray() {
  1643 		static $MPEGaudioLayer = array(false, 3, 2, 1);
  1704 		static $MPEGaudioLayer = array(false, 3, 2, 1);
  1644 		return $MPEGaudioLayer;
  1705 		return $MPEGaudioLayer;
  1645 	}
  1706 	}
  1646 
  1707 
       
  1708 	/**
       
  1709 	 * @return array
       
  1710 	 */
  1647 	public static function MPEGaudioBitrateArray() {
  1711 	public static function MPEGaudioBitrateArray() {
  1648 		static $MPEGaudioBitrate;
  1712 		static $MPEGaudioBitrate;
  1649 		if (empty($MPEGaudioBitrate)) {
  1713 		if (empty($MPEGaudioBitrate)) {
  1650 			$MPEGaudioBitrate = array (
  1714 			$MPEGaudioBitrate = array (
  1651 				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
  1715 				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
  1661 			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
  1725 			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
  1662 		}
  1726 		}
  1663 		return $MPEGaudioBitrate;
  1727 		return $MPEGaudioBitrate;
  1664 	}
  1728 	}
  1665 
  1729 
       
  1730 	/**
       
  1731 	 * @return array
       
  1732 	 */
  1666 	public static function MPEGaudioFrequencyArray() {
  1733 	public static function MPEGaudioFrequencyArray() {
  1667 		static $MPEGaudioFrequency;
  1734 		static $MPEGaudioFrequency;
  1668 		if (empty($MPEGaudioFrequency)) {
  1735 		if (empty($MPEGaudioFrequency)) {
  1669 			$MPEGaudioFrequency = array (
  1736 			$MPEGaudioFrequency = array (
  1670 				'1'   => array(44100, 48000, 32000),
  1737 				'1'   => array(44100, 48000, 32000),
  1673 			);
  1740 			);
  1674 		}
  1741 		}
  1675 		return $MPEGaudioFrequency;
  1742 		return $MPEGaudioFrequency;
  1676 	}
  1743 	}
  1677 
  1744 
       
  1745 	/**
       
  1746 	 * @return array
       
  1747 	 */
  1678 	public static function MPEGaudioChannelModeArray() {
  1748 	public static function MPEGaudioChannelModeArray() {
  1679 		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
  1749 		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
  1680 		return $MPEGaudioChannelMode;
  1750 		return $MPEGaudioChannelMode;
  1681 	}
  1751 	}
  1682 
  1752 
       
  1753 	/**
       
  1754 	 * @return array
       
  1755 	 */
  1683 	public static function MPEGaudioModeExtensionArray() {
  1756 	public static function MPEGaudioModeExtensionArray() {
  1684 		static $MPEGaudioModeExtension;
  1757 		static $MPEGaudioModeExtension;
  1685 		if (empty($MPEGaudioModeExtension)) {
  1758 		if (empty($MPEGaudioModeExtension)) {
  1686 			$MPEGaudioModeExtension = array (
  1759 			$MPEGaudioModeExtension = array (
  1687 				1 => array('4-31', '8-31', '12-31', '16-31'),
  1760 				1 => array('4-31', '8-31', '12-31', '16-31'),
  1690 			);
  1763 			);
  1691 		}
  1764 		}
  1692 		return $MPEGaudioModeExtension;
  1765 		return $MPEGaudioModeExtension;
  1693 	}
  1766 	}
  1694 
  1767 
       
  1768 	/**
       
  1769 	 * @return array
       
  1770 	 */
  1695 	public static function MPEGaudioEmphasisArray() {
  1771 	public static function MPEGaudioEmphasisArray() {
  1696 		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
  1772 		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
  1697 		return $MPEGaudioEmphasis;
  1773 		return $MPEGaudioEmphasis;
  1698 	}
  1774 	}
  1699 
  1775 
       
  1776 	/**
       
  1777 	 * @param string $head4
       
  1778 	 * @param bool   $allowBitrate15
       
  1779 	 *
       
  1780 	 * @return bool
       
  1781 	 */
  1700 	public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
  1782 	public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
  1701 		return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
  1783 		return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
  1702 	}
  1784 	}
  1703 
  1785 
       
  1786 	/**
       
  1787 	 * @param array $rawarray
       
  1788 	 * @param bool  $echoerrors
       
  1789 	 * @param bool  $allowBitrate15
       
  1790 	 *
       
  1791 	 * @return bool
       
  1792 	 */
  1704 	public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
  1793 	public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
  1705 		if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
  1794 		if (!isset($rawarray['synch']) || ($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
  1706 			return false;
  1795 			return false;
  1707 		}
  1796 		}
  1708 
  1797 
  1709 		static $MPEGaudioVersionLookup;
  1798 		static $MPEGaudioVersionLookup;
  1710 		static $MPEGaudioLayerLookup;
  1799 		static $MPEGaudioLayerLookup;
  1771 		// $rawarray['original'];
  1860 		// $rawarray['original'];
  1772 
  1861 
  1773 		return true;
  1862 		return true;
  1774 	}
  1863 	}
  1775 
  1864 
       
  1865 	/**
       
  1866 	 * @param string $Header4Bytes
       
  1867 	 *
       
  1868 	 * @return array|false
       
  1869 	 */
  1776 	public static function MPEGaudioHeaderDecode($Header4Bytes) {
  1870 	public static function MPEGaudioHeaderDecode($Header4Bytes) {
  1777 		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
  1871 		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
  1778 		// A - Frame sync (all bits set)
  1872 		// A - Frame sync (all bits set)
  1779 		// B - MPEG Audio version ID
  1873 		// B - MPEG Audio version ID
  1780 		// C - Layer description
  1874 		// C - Layer description
  1792 		if (strlen($Header4Bytes) != 4) {
  1886 		if (strlen($Header4Bytes) != 4) {
  1793 			return false;
  1887 			return false;
  1794 		}
  1888 		}
  1795 
  1889 
  1796 		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
  1890 		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
  1797 		$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
  1891 		$MPEGrawHeader['version']       = (ord($Header4Bytes[1]) & 0x18) >> 3; //    BB
  1798 		$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
  1892 		$MPEGrawHeader['layer']         = (ord($Header4Bytes[1]) & 0x06) >> 1; //      CC
  1799 		$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
  1893 		$MPEGrawHeader['protection']    = (ord($Header4Bytes[1]) & 0x01);      //        D
  1800 		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
  1894 		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
  1801 		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
  1895 		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes[2]) & 0x0C) >> 2; //     FF
  1802 		$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
  1896 		$MPEGrawHeader['padding']       = (ord($Header4Bytes[2]) & 0x02) >> 1; //       G
  1803 		$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
  1897 		$MPEGrawHeader['private']       = (ord($Header4Bytes[2]) & 0x01);      //        H
  1804 		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
  1898 		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
  1805 		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
  1899 		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; //   JJ
  1806 		$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
  1900 		$MPEGrawHeader['copyright']     = (ord($Header4Bytes[3]) & 0x08) >> 3; //     K
  1807 		$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
  1901 		$MPEGrawHeader['original']      = (ord($Header4Bytes[3]) & 0x04) >> 2; //      L
  1808 		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
  1902 		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes[3]) & 0x03);      //       MM
  1809 
  1903 
  1810 		return $MPEGrawHeader;
  1904 		return $MPEGrawHeader;
  1811 	}
  1905 	}
  1812 
  1906 
       
  1907 	/**
       
  1908 	 * @param int|string $bitrate
       
  1909 	 * @param string     $version
       
  1910 	 * @param string     $layer
       
  1911 	 * @param bool       $padding
       
  1912 	 * @param int        $samplerate
       
  1913 	 *
       
  1914 	 * @return int|false
       
  1915 	 */
  1813 	public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
  1916 	public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
  1814 		static $AudioFrameLengthCache = array();
  1917 		static $AudioFrameLengthCache = array();
  1815 
  1918 
  1816 		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
  1919 		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
  1817 			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
  1920 			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
  1869 			}
  1972 			}
  1870 		}
  1973 		}
  1871 		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
  1974 		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
  1872 	}
  1975 	}
  1873 
  1976 
       
  1977 	/**
       
  1978 	 * @param float|int $bit_rate
       
  1979 	 *
       
  1980 	 * @return int|float|string
       
  1981 	 */
  1874 	public static function ClosestStandardMP3Bitrate($bit_rate) {
  1982 	public static function ClosestStandardMP3Bitrate($bit_rate) {
  1875 		static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
  1983 		static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
  1876 		static $bit_rate_table = array (0=>'-');
  1984 		static $bit_rate_table = array (0=>'-');
  1877 		$round_bit_rate = intval(round($bit_rate, -3));
  1985 		$round_bit_rate = intval(round($bit_rate, -3));
  1878 		if (!isset($bit_rate_table[$round_bit_rate])) {
  1986 		if (!isset($bit_rate_table[$round_bit_rate])) {
  1889 			}
  1997 			}
  1890 		}
  1998 		}
  1891 		return $bit_rate_table[$round_bit_rate];
  1999 		return $bit_rate_table[$round_bit_rate];
  1892 	}
  2000 	}
  1893 
  2001 
       
  2002 	/**
       
  2003 	 * @param string $version
       
  2004 	 * @param string $channelmode
       
  2005 	 *
       
  2006 	 * @return int
       
  2007 	 */
  1894 	public static function XingVBRidOffset($version, $channelmode) {
  2008 	public static function XingVBRidOffset($version, $channelmode) {
  1895 		static $XingVBRidOffsetCache = array();
  2009 		static $XingVBRidOffsetCache = array();
  1896 		if (empty($XingVBRidOffset)) {
  2010 		if (empty($XingVBRidOffsetCache)) {
  1897 			$XingVBRidOffset = array (
  2011 			$XingVBRidOffsetCache = array (
  1898 				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
  2012 				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
  1899 								'stereo'        => 0x24, // 4 + 32 = 36
  2013 								'stereo'        => 0x24, // 4 + 32 = 36
  1900 								'joint stereo'  => 0x24,
  2014 								'joint stereo'  => 0x24,
  1901 								'dual channel'  => 0x24
  2015 								'dual channel'  => 0x24
  1902 							   ),
  2016 							   ),
  1912 								'joint stereo'  => 0x15,
  2026 								'joint stereo'  => 0x15,
  1913 								'dual channel'  => 0x15
  2027 								'dual channel'  => 0x15
  1914 							   )
  2028 							   )
  1915 			);
  2029 			);
  1916 		}
  2030 		}
  1917 		return $XingVBRidOffset[$version][$channelmode];
  2031 		return $XingVBRidOffsetCache[$version][$channelmode];
  1918 	}
  2032 	}
  1919 
  2033 
       
  2034 	/**
       
  2035 	 * @param int $VBRmethodID
       
  2036 	 *
       
  2037 	 * @return string
       
  2038 	 */
  1920 	public static function LAMEvbrMethodLookup($VBRmethodID) {
  2039 	public static function LAMEvbrMethodLookup($VBRmethodID) {
  1921 		static $LAMEvbrMethodLookup = array(
  2040 		static $LAMEvbrMethodLookup = array(
  1922 			0x00 => 'unknown',
  2041 			0x00 => 'unknown',
  1923 			0x01 => 'cbr',
  2042 			0x01 => 'cbr',
  1924 			0x02 => 'abr',
  2043 			0x02 => 'abr',
  1931 			0x0F => 'reserved'
  2050 			0x0F => 'reserved'
  1932 		);
  2051 		);
  1933 		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
  2052 		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
  1934 	}
  2053 	}
  1935 
  2054 
       
  2055 	/**
       
  2056 	 * @param int $StereoModeID
       
  2057 	 *
       
  2058 	 * @return string
       
  2059 	 */
  1936 	public static function LAMEmiscStereoModeLookup($StereoModeID) {
  2060 	public static function LAMEmiscStereoModeLookup($StereoModeID) {
  1937 		static $LAMEmiscStereoModeLookup = array(
  2061 		static $LAMEmiscStereoModeLookup = array(
  1938 			0 => 'mono',
  2062 			0 => 'mono',
  1939 			1 => 'stereo',
  2063 			1 => 'stereo',
  1940 			2 => 'dual mono',
  2064 			2 => 'dual mono',
  1945 			7 => 'other'
  2069 			7 => 'other'
  1946 		);
  2070 		);
  1947 		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
  2071 		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
  1948 	}
  2072 	}
  1949 
  2073 
       
  2074 	/**
       
  2075 	 * @param int $SourceSampleFrequencyID
       
  2076 	 *
       
  2077 	 * @return string
       
  2078 	 */
  1950 	public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
  2079 	public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
  1951 		static $LAMEmiscSourceSampleFrequencyLookup = array(
  2080 		static $LAMEmiscSourceSampleFrequencyLookup = array(
  1952 			0 => '<= 32 kHz',
  2081 			0 => '<= 32 kHz',
  1953 			1 => '44.1 kHz',
  2082 			1 => '44.1 kHz',
  1954 			2 => '48 kHz',
  2083 			2 => '48 kHz',
  1955 			3 => '> 48kHz'
  2084 			3 => '> 48kHz'
  1956 		);
  2085 		);
  1957 		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
  2086 		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
  1958 	}
  2087 	}
  1959 
  2088 
       
  2089 	/**
       
  2090 	 * @param int $SurroundInfoID
       
  2091 	 *
       
  2092 	 * @return string
       
  2093 	 */
  1960 	public static function LAMEsurroundInfoLookup($SurroundInfoID) {
  2094 	public static function LAMEsurroundInfoLookup($SurroundInfoID) {
  1961 		static $LAMEsurroundInfoLookup = array(
  2095 		static $LAMEsurroundInfoLookup = array(
  1962 			0 => 'no surround info',
  2096 			0 => 'no surround info',
  1963 			1 => 'DPL encoding',
  2097 			1 => 'DPL encoding',
  1964 			2 => 'DPL2 encoding',
  2098 			2 => 'DPL2 encoding',
  1965 			3 => 'Ambisonic encoding'
  2099 			3 => 'Ambisonic encoding'
  1966 		);
  2100 		);
  1967 		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
  2101 		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
  1968 	}
  2102 	}
  1969 
  2103 
       
  2104 	/**
       
  2105 	 * @param array $LAMEtag
       
  2106 	 *
       
  2107 	 * @return string
       
  2108 	 */
  1970 	public static function LAMEpresetUsedLookup($LAMEtag) {
  2109 	public static function LAMEpresetUsedLookup($LAMEtag) {
  1971 
  2110 
  1972 		if ($LAMEtag['preset_used_id'] == 0) {
  2111 		if ($LAMEtag['preset_used_id'] == 0) {
  1973 			// no preset used (LAME >=3.93)
  2112 			// no preset used (LAME >=3.93)
  1974 			// no preset recorded (LAME <3.93)
  2113 			// no preset recorded (LAME <3.93)