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); |
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 ), |
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) |