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