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

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1