Passed
Branch ticket-41057 (c4f931)
by Stephen
24:38
created

getid3_mp3::getOnlyMPEGaudioInfoBruteForce()   F

Complexity

Conditions 27
Paths 11808

Size

Total Lines 146
Code Lines 111

Duplication

Lines 6
Ratio 4.11 %

Importance

Changes 0
Metric Value
cc 27
eloc 111
nc 11808
nop 0
dl 6
loc 146
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/////////////////////////////////////////////////////////////////
3
/// getID3() by James Heinrich <[email protected]>               //
4
//  available at http://getid3.sourceforge.net                 //
5
//            or http://www.getid3.org                         //
6
//          also https://github.com/JamesHeinrich/getID3       //
7
/////////////////////////////////////////////////////////////////
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
17
18
// number of frames to scan to determine if MPEG-audio sequence is valid
19
// Lower this number to 5-20 for faster scanning
20
// Increase this number to 50+ for most accurate detection of valid VBR/CBR
21
// mpeg-audio streams
22
define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
23
24
25
class getid3_mp3 extends getid3_handler
26
{
27
28
	public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
29
30
	public function Analyze() {
31
		$info = &$this->getid3->info;
32
33
		$initialOffset = $info['avdataoffset'];
34
35
		if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
36
			if ($this->allow_bruteforce) {
37
				$info['error'][] = 'Rescanning file in BruteForce mode';
38
				$this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
39
			}
40
		}
41
42
43
		if (isset($info['mpeg']['audio']['bitrate_mode'])) {
44
			$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
45
		}
46
47
		if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
48
49
			$synchoffsetwarning = 'Unknown data before synch ';
50
			if (isset($info['id3v2']['headerlength'])) {
51
				$synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
52
			} elseif ($initialOffset > 0) {
53
				$synchoffsetwarning .= '(should be at '.$initialOffset.', ';
54
			} else {
55
				$synchoffsetwarning .= '(should be at beginning of file, ';
56
			}
57
			$synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
58
			if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
59
60
				if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
61
62
					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
63
					$info['audio']['codec'] = 'LAME';
64
					$CurrentDataLAMEversionString = 'LAME3.';
65
66
				} elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
67
68
					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
69
					$info['audio']['codec'] = 'LAME';
70
					$CurrentDataLAMEversionString = 'LAME3.';
71
72
				}
73
74
			}
75
			$info['warning'][] = $synchoffsetwarning;
76
77
		}
78
79
		if (isset($info['mpeg']['audio']['LAME'])) {
80
			$info['audio']['codec'] = 'LAME';
81
			if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
82
				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
83 View Code Duplication
			} elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
84
				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
85
			}
86
		}
87
88
		$CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
89
		if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
90
			// a version number of LAME that does not end with a number like "LAME3.92"
91
			// or with a closing parenthesis like "LAME3.88 (alpha)"
92
			// or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
93
94
			// not sure what the actual last frame length will be, but will be less than or equal to 1441
95
			$PossiblyLongerLAMEversion_FrameLength = 1441;
96
97
			// Not sure what version of LAME this is - look in padding of last frame for longer version string
98
			$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
99
			$this->fseek($PossibleLAMEversionStringOffset);
100
			$PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
101
			switch (substr($CurrentDataLAMEversionString, -1)) {
102
				case 'a':
103
				case 'b':
104
					// "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
105
					// need to trim off "a" to match longer string
106
					$CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
107
					break;
108
			}
109
			if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
110
				if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
111
					$PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
112
					if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
113
						$info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
114
					}
115
				}
116
			}
117
		}
118
		if (!empty($info['audio']['encoder'])) {
119
			$info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
120
		}
121
122
		switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
123
			case 1:
124
			case 2:
125
				$info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
126
				break;
127
		}
128
		if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
129
			switch ($info['audio']['dataformat']) {
130
				case 'mp1':
131
				case 'mp2':
132
				case 'mp3':
133
					$info['fileformat'] = $info['audio']['dataformat'];
134
					break;
135
136
				default:
137
					$info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
138
					break;
139
			}
140
		}
141
142
		if (empty($info['fileformat'])) {
143
			unset($info['fileformat']);
144
			unset($info['audio']['bitrate_mode']);
145
			unset($info['avdataoffset']);
146
			unset($info['avdataend']);
147
			return false;
148
		}
149
150
		$info['mime_type']         = 'audio/mpeg';
151
		$info['audio']['lossless'] = false;
152
153
		// Calculate playtime
154
		if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
155
			$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
156
		}
157
158
		$info['audio']['encoder_options'] = $this->GuessEncoderOptions();
159
160
		return true;
161
	}
162
163
164
	public function GuessEncoderOptions() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
165
		// shortcuts
166
		$info = &$this->getid3->info;
167
		if (!empty($info['mpeg']['audio'])) {
168
			$thisfile_mpeg_audio = &$info['mpeg']['audio'];
169
			if (!empty($thisfile_mpeg_audio['LAME'])) {
170
				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
171
			}
172
		}
173
174
		$encoder_options = '';
175
		static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
176
177
		if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
178
179
			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
180
181
		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
182
183
			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
184
185
		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
186
187
			static $KnownEncoderValues = array();
188
			if (empty($KnownEncoderValues)) {
189
190
				//$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
191
				$KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
192
				$KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
193
				$KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
194
				$KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
195
				$KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
196
				$KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
197
				$KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
198
				$KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
199
				$KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
200
				$KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
201
				$KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
202
				$KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
203
				$KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
204
				$KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
205
				$KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
206
				$KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
207
				$KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
208
209
				$KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
210
				$KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
211
				$KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
212
				$KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
213
				$KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
214
				$KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
215
				$KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
216
				$KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
217
				$KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
218
				$KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
219
				$KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
220
				$KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
221
				$KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
222
				$KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
223
				$KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
224
				$KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
225
				$KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
226
				$KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
227
				$KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
228
				$KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
229
				$KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
230
				$KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
231
				$KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
232
				$KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
233
				$KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
234
				$KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
235
				$KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
236
				$KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
237
				$KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
238
				$KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
239
				$KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
240
				$KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
241
				$KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
242
			}
243
244
			if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
245
246
				$encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
247
248
			} elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
249
250
				$encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
251
252
			} elseif ($info['audio']['bitrate_mode'] == 'vbr') {
253
254
				// http://gabriel.mp3-tech.org/mp3infotag.html
255
				// int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
256
257
258
				$LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
259
				$LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
260
				$encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
261
262
			} elseif ($info['audio']['bitrate_mode'] == 'cbr') {
263
264
				$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
265
266
			} else {
267
268
				$encoder_options = strtoupper($info['audio']['bitrate_mode']);
269
270
			}
271
272
		} elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
273
274
			$encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
275
276
		} elseif (!empty($info['audio']['bitrate'])) {
277
278
			if ($info['audio']['bitrate_mode'] == 'cbr') {
279
				$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
280
			} else {
281
				$encoder_options = strtoupper($info['audio']['bitrate_mode']);
282
			}
283
284
		}
285
		if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
286
			$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
287
		}
288
289
		if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
290
			$encoder_options .= ' --nogap';
291
		}
292
293
		if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
294
			$ExplodedOptions = explode(' ', $encoder_options, 4);
295
			if ($ExplodedOptions[0] == '--r3mix') {
296
				$ExplodedOptions[1] = 'r3mix';
297
			}
298
			switch ($ExplodedOptions[0]) {
299
				case '--preset':
300
				case '--alt-preset':
301
				case '--r3mix':
302
					if ($ExplodedOptions[1] == 'fast') {
303
						$ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
304
					}
305
					switch ($ExplodedOptions[1]) {
306
						case 'portable':
307
						case 'medium':
308
						case 'standard':
309
						case 'extreme':
310
						case 'insane':
311
						case 'fast portable':
312
						case 'fast medium':
313
						case 'fast standard':
314
						case 'fast extreme':
315
						case 'fast insane':
316
						case 'r3mix':
317
							static $ExpectedLowpass = array(
318
									'insane|20500'        => 20500,
319
									'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
320
									'medium|18000'        => 18000,
321
									'fast medium|18000'   => 18000,
322
									'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
323
									'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
324
									'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
325
									'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
326
									'standard|19000'      => 19000,
327
									'fast standard|19000' => 19000,
328
									'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
329
									'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
330
									'r3mix|18000'         => 18000,  // 3.94,   3.95
331
								);
332
							if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
0 ignored issues
show
Bug introduced by
The variable $thisfile_mpeg_audio does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
333
								$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
334
							}
335
							break;
336
337
						default:
338
							break;
339
					}
340
					break;
341
			}
342
		}
343
344
		if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
345
			if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
346
				$encoder_options .= ' --resample 44100';
347
			} elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
348
				$encoder_options .= ' --resample 48000';
349
			} elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
350
				switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
351
					case 0: // <= 32000
352
						// may or may not be same as source frequency - ignore
353
						break;
354
					case 1: // 44100
355
					case 2: // 48000
356
					case 3: // 48000+
357
						$ExplodedOptions = explode(' ', $encoder_options, 4);
358
						switch ($ExplodedOptions[0]) {
359
							case '--preset':
360
							case '--alt-preset':
361
								switch ($ExplodedOptions[1]) {
362
									case 'fast':
363
									case 'portable':
364
									case 'medium':
365
									case 'standard':
366
									case 'extreme':
367
									case 'insane':
368
										$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
369
										break;
370
371
									default:
372
										static $ExpectedResampledRate = array(
373
												'phon+/lw/mw-eu/sw|16000' => 16000,
374
												'mw-us|24000'             => 24000, // 3.95
375
												'mw-us|32000'             => 32000, // 3.93
376
												'mw-us|16000'             => 16000, // 3.92
377
												'phone|16000'             => 16000,
378
												'phone|11025'             => 11025, // 3.94a15
379
												'radio|32000'             => 32000, // 3.94a15
380
												'fm/radio|32000'          => 32000, // 3.92
381
												'fm|32000'                => 32000, // 3.90
382
												'voice|32000'             => 32000);
383
										if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
384
											$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
385
										}
386
										break;
387
								}
388
								break;
389
390
							case '--r3mix':
391
							default:
392
								$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
393
								break;
394
						}
395
						break;
396
				}
397
			}
398
		}
399
		if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
400
			//$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
401
			$encoder_options = strtoupper($info['audio']['bitrate_mode']);
402
		}
403
404
		return $encoder_options;
405
	}
406
407
408
	public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
409
		static $MPEGaudioVersionLookup;
410
		static $MPEGaudioLayerLookup;
411
		static $MPEGaudioBitrateLookup;
412
		static $MPEGaudioFrequencyLookup;
413
		static $MPEGaudioChannelModeLookup;
414
		static $MPEGaudioModeExtensionLookup;
415
		static $MPEGaudioEmphasisLookup;
416 View Code Duplication
		if (empty($MPEGaudioVersionLookup)) {
417
			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
418
			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
419
			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
420
			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
421
			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
422
			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
423
			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
424
		}
425
426
		if ($this->fseek($offset) != 0) {
427
			$info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
428
			return false;
429
		}
430
		//$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
431
		$headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
432
433
		// MP3 audio frame structure:
434
		// $aa $aa $aa $aa [$bb $bb] $cc...
435
		// where $aa..$aa is the four-byte mpeg-audio header (below)
436
		// $bb $bb is the optional 2-byte CRC
437
		// and $cc... is the audio data
438
439
		$head4 = substr($headerstring, 0, 4);
440
441
		static $MPEGaudioHeaderDecodeCache = array();
442
		if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
443
			$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
444
		} else {
445
			$MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
446
			$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
447
		}
448
449
		static $MPEGaudioHeaderValidCache = array();
450
		if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
451
			//$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
452
			$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
453
		}
454
455
		// shortcut
456
		if (!isset($info['mpeg']['audio'])) {
457
			$info['mpeg']['audio'] = array();
458
		}
459
		$thisfile_mpeg_audio = &$info['mpeg']['audio'];
460
461
462
		if ($MPEGaudioHeaderValidCache[$head4]) {
463
			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
464
		} else {
465
			$info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
466
			return false;
467
		}
468
469
		if (!$FastMPEGheaderScan) {
470
			$thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
471
			$thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
472
473
			$thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
474
			$thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
475
			$thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
476
			$thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
477
			$thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
478
			$thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
479
			$thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
480
			$thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
481
			$thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
482
483
			$info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
484
			$info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
485
486
			if ($thisfile_mpeg_audio['protection']) {
487
				$thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
488
			}
489
		}
490
491
		if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
492
			// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
493
			$info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
494
			$thisfile_mpeg_audio['raw']['bitrate'] = 0;
495
		}
496
		$thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
497
		$thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
498
499
		if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
500
			// only skip multiple frame check if free-format bitstream found at beginning of file
501
			// otherwise is quite possibly simply corrupted data
502
			$recursivesearch = false;
503
		}
504
505
		// For Layer 2 there are some combinations of bitrate and mode which are not allowed.
506
		if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
507
508
			$info['audio']['dataformat'] = 'mp2';
509
			switch ($thisfile_mpeg_audio['channelmode']) {
510
511
				case 'mono':
512
					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
513
						// these are ok
514
					} else {
515
						$info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
516
						return false;
517
					}
518
					break;
519
520
				case 'stereo':
521
				case 'joint stereo':
522
				case 'dual channel':
523
					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
524
						// these are ok
525
					} else {
526
						$info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
527
						return false;
528
					}
529
					break;
530
531
			}
532
533
		}
534
535
536
		if ($info['audio']['sample_rate'] > 0) {
537
			$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']);
538
		}
539
540
		$nextframetestoffset = $offset + 1;
541
		if ($thisfile_mpeg_audio['bitrate'] != 'free') {
542
543
			$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
544
545
			if (isset($thisfile_mpeg_audio['framelength'])) {
546
				$nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
547
			} else {
548
				$info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
549
				return false;
550
			}
551
552
		}
553
554
		$ExpectedNumberOfAudioBytes = 0;
555
556
		////////////////////////////////////////////////////////////////////////////////////
557
		// Variable-bitrate headers
558
559
		if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
560
			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
561
			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
562
563
			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
564
			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
565
			$info['audio']['codec']                = 'Fraunhofer';
566
567
			$SideInfoData = substr($headerstring, 4 + 2, 32);
568
569
			$FraunhoferVBROffset = 36;
570
571
			$thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
572
			$thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
573
			$thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
574
			$thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
575
			$thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
576
			$thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
577
			$thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
578
			$thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
579
			$thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
580
581
			$ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
582
583
			$previousbyteoffset = $offset;
584
			for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
585
				$Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
586
				$FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
587
				$thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
588
				$thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
589
				$previousbyteoffset += $Fraunhofer_OffsetN;
590
			}
591
592
593
		} else {
594
595
			// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
596
			// depending on MPEG layer and number of channels
597
598
			$VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
599
			$SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
600
601
			if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
602
				// 'Xing' is traditional Xing VBR frame
603
				// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
604
				// 'Info' *can* legally be used to specify a VBR file as well, however.
605
606
				// http://www.multiweb.cz/twoinches/MP3inside.htm
607
				//00..03 = "Xing" or "Info"
608
				//04..07 = Flags:
609
				//  0x01  Frames Flag     set if value for number of frames in file is stored
610
				//  0x02  Bytes Flag      set if value for filesize in bytes is stored
611
				//  0x04  TOC Flag        set if values for TOC are stored
612
				//  0x08  VBR Scale Flag  set if values for VBR scale is stored
613
				//08..11  Frames: Number of frames in file (including the first Xing/Info one)
614
				//12..15  Bytes:  File length in Bytes
615
				//16..115  TOC (Table of Contents):
616
				//  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
617
				//  Each Byte has a value according this formula:
618
				//  (TOC[i] / 256) * fileLenInBytes
619
				//  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:
620
				//  TOC[(60/240)*100] = TOC[25]
621
				//  and corresponding Byte in file is then approximately at:
622
				//  (TOC[25]/256) * 5000000
623
				//116..119  VBR Scale
624
625
626
				// should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
627
//				if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
628
					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
629
					$thisfile_mpeg_audio['VBR_method']   = 'Xing';
630
//				} else {
631
//					$ScanAsCBR = true;
632
//					$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
633
//				}
634
635
				$thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
636
637
				$thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
638
				$thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
639
				$thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
640
				$thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
641
642
				if ($thisfile_mpeg_audio['xing_flags']['frames']) {
643
					$thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
644
					//$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
645
				}
646
				if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
647
					$thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
648
				}
649
650
				//if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
651
				if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
652
653
					$framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
654
655
					if ($thisfile_mpeg_audio['layer'] == '1') {
656
						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
657
						//$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
658
						$info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
659
					} else {
660
						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
661
						//$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
662
						$info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
663
					}
664
					$thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
665
				}
666
667
				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
668
					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
669
					for ($i = 0; $i < 100; $i++) {
670
						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
671
					}
672
				}
673
				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
674
					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
675
				}
676
677
678
				// http://gabriel.mp3-tech.org/mp3infotag.html
679
				if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
680
681
					// shortcut
682
					$thisfile_mpeg_audio['LAME'] = array();
683
					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
684
685
686
					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
687
					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
688
689
					if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
690
691
						// extra 11 chars are not part of version string when LAMEtag present
692
						unset($thisfile_mpeg_audio_lame['long_version']);
693
694
						// It the LAME tag was only introduced in LAME v3.90
695
						// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
696
697
						// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
698
						// are assuming a 'Xing' identifier offset of 0x24, which is the case for
699
						// MPEG-1 non-mono, but not for other combinations
700
						$LAMEtagOffsetContant = $VBRidOffset - 0x24;
701
702
						// shortcuts
703
						$thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
704
						$thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
705
						$thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
706
						$thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
707
						$thisfile_mpeg_audio_lame['raw'] = array();
708
						$thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
709
710
						// byte $9B  VBR Quality
711
						// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
712
						// Actually overwrites original Xing bytes
713
						unset($thisfile_mpeg_audio['VBR_scale']);
714
						$thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
715
716
						// bytes $9C-$A4  Encoder short VersionString
717
						$thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
718
719
						// byte $A5  Info Tag revision + VBR method
720
						$LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
721
722
						$thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
723
						$thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
724
						$thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
725
						$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'
726
727
						// byte $A6  Lowpass filter value
728
						$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
729
730
						// bytes $A7-$AE  Replay Gain
731
						// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
732
						// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
733
						if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
734
							// LAME 3.94a16 and later - 9.23 fixed point
735
							// ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
736
							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
737
						} else {
738
							// LAME 3.94a15 and earlier - 32-bit floating point
739
							// Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
740
							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
741
						}
742
						if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
743
							unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
744
						} else {
745
							$thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
746
						}
747
748
						$thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
749
						$thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
750
751
752 View Code Duplication
						if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
753
754
							$thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
755
							$thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
756
							$thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
757
							$thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
758
							$thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
759
							$thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
760
							$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']);
761
762
							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
763
								$info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
764
							}
765
							$info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
766
							$info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
767
						} else {
768
							unset($thisfile_mpeg_audio_lame_RGAD['track']);
769
						}
770 View Code Duplication
						if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
771
772
							$thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
773
							$thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
774
							$thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
775
							$thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
776
							$thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
777
							$thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
778
							$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']);
779
780
							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
781
								$info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
782
							}
783
							$info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
784
							$info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
785
						} else {
786
							unset($thisfile_mpeg_audio_lame_RGAD['album']);
787
						}
788
						if (empty($thisfile_mpeg_audio_lame_RGAD)) {
789
							unset($thisfile_mpeg_audio_lame['RGAD']);
790
						}
791
792
793
						// byte $AF  Encoding flags + ATH Type
794
						$EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
795
						$thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
796
						$thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
797
						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
798
						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
799
						$thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
800
801
						// byte $B0  if ABR {specified bitrate} else {minimal bitrate}
802
						$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
803
						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
804
							$thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
805
						} elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
806
							// ignore
807
						} elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
808
							$thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
809
						}
810
811
						// bytes $B1-$B3  Encoder delays
812
						$EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
813
						$thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
814
						$thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
815
816
						// byte $B4  Misc
817
						$MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
818
						$thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
819
						$thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
820
						$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
821
						$thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
822
						$thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
823
						$thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
824
						$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
825
						$thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
826
827
						// byte $B5  MP3 Gain
828
						$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
829
						$thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
830
						$thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
831
832
						// bytes $B6-$B7  Preset and surround info
833
						$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
834
						// Reserved                                                    = ($PresetSurroundBytes & 0xC000);
835
						$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
836
						$thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
837
						$thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
838
						$thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
839
						if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
840
							$info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to [email protected]';
841
						}
842
						if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
843
							// this may change if 3.90.4 ever comes out
844
							$thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
845
						}
846
847
						// bytes $B8-$BB  MusicLength
848
						$thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
849
						$ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
850
851
						// bytes $BC-$BD  MusicCRC
852
						$thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
853
854
						// bytes $BE-$BF  CRC-16 of Info Tag
855
						$thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
856
857
858
						// LAME CBR
859
						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
860
861
							$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
862
							$thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
863
							$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
864
							//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
865
							//	$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
866
							//}
867
868
						}
869
870
					}
871
				}
872
873
			} else {
874
875
				// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
876
				$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
877
				if ($recursivesearch) {
878
					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
879
					if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
880
						$recursivesearch = false;
881
						$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
882
					}
883
					if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
884
						$info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
885
					}
886
				}
887
888
			}
889
890
		}
891
892
		if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
893
			if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
894
				if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
895
					// ignore, audio data is broken into chunks so will always be data "missing"
896
				}
897
				elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
898
					$this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
899
				}
900 View Code Duplication
				else {
901
					$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)');
902
				}
903
			} else {
904
				if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
905
				//	$prenullbytefileoffset = $this->ftell();
906
				//	$this->fseek($info['avdataend']);
907
				//	$PossibleNullByte = $this->fread(1);
908
				//	$this->fseek($prenullbytefileoffset);
909
				//	if ($PossibleNullByte === "\x00") {
910
						$info['avdataend']--;
911
				//		$info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
912
				//	} else {
913
				//		$info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
914
				//	}
915 View Code Duplication
				} else {
916
					$info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
917
				}
918
			}
919
		}
920
921
		if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
922
			if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
923
				$framebytelength = $this->FreeFormatFrameLength($offset, true);
924
				if ($framebytelength > 0) {
925
					$thisfile_mpeg_audio['framelength'] = $framebytelength;
926
					if ($thisfile_mpeg_audio['layer'] == '1') {
927
						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
928
						$info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
929
					} else {
930
						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
931
						$info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
932
					}
933
				} else {
934
					$info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
935
				}
936
			}
937
		}
938
939
		if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
940
			switch ($thisfile_mpeg_audio['bitrate_mode']) {
941
				case 'vbr':
942
				case 'abr':
943
					$bytes_per_frame = 1152;
944
					if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
945
						$bytes_per_frame = 384;
946
					} elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
947
						$bytes_per_frame = 576;
948
					}
949
					$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);
950
					if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
951
						$info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
952
						$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
953
					}
954
					break;
955
			}
956
		}
957
958
		// End variable-bitrate headers
959
		////////////////////////////////////////////////////////////////////////////////////
960
961
		if ($recursivesearch) {
962
963
			if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
964
				return false;
965
			}
966
967
		}
968
969
970
		//if (false) {
971
		//    // experimental side info parsing section - not returning anything useful yet
972
		//
973
		//    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
974
		//    $SideInfoOffset = 0;
975
		//
976
		//    if ($thisfile_mpeg_audio['version'] == '1') {
977
		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
978
		//            // MPEG-1 (mono)
979
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
980
		//            $SideInfoOffset += 9;
981
		//            $SideInfoOffset += 5;
982
		//        } else {
983
		//            // MPEG-1 (stereo, joint-stereo, dual-channel)
984
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
985
		//            $SideInfoOffset += 9;
986
		//            $SideInfoOffset += 3;
987
		//        }
988
		//    } else { // 2 or 2.5
989
		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
990
		//            // MPEG-2, MPEG-2.5 (mono)
991
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
992
		//            $SideInfoOffset += 8;
993
		//            $SideInfoOffset += 1;
994
		//        } else {
995
		//            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
996
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
997
		//            $SideInfoOffset += 8;
998
		//            $SideInfoOffset += 2;
999
		//        }
1000
		//    }
1001
		//
1002
		//    if ($thisfile_mpeg_audio['version'] == '1') {
1003
		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1004
		//            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1005
		//                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1006
		//                $SideInfoOffset += 2;
1007
		//            }
1008
		//        }
1009
		//    }
1010
		//    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1011
		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1012
		//            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1013
		//            $SideInfoOffset += 12;
1014
		//            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1015
		//            $SideInfoOffset += 9;
1016
		//            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1017
		//            $SideInfoOffset += 8;
1018
		//            if ($thisfile_mpeg_audio['version'] == '1') {
1019
		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1020
		//                $SideInfoOffset += 4;
1021
		//            } else {
1022
		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1023
		//                $SideInfoOffset += 9;
1024
		//            }
1025
		//            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1026
		//            $SideInfoOffset += 1;
1027
		//
1028
		//            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1029
		//
1030
		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1031
		//                $SideInfoOffset += 2;
1032
		//                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1033
		//                $SideInfoOffset += 1;
1034
		//
1035
		//                for ($region = 0; $region < 2; $region++) {
1036
		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1037
		//                    $SideInfoOffset += 5;
1038
		//                }
1039
		//                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1040
		//
1041
		//                for ($window = 0; $window < 3; $window++) {
1042
		//                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1043
		//                    $SideInfoOffset += 3;
1044
		//                }
1045
		//
1046
		//            } else {
1047
		//
1048
		//                for ($region = 0; $region < 3; $region++) {
1049
		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1050
		//                    $SideInfoOffset += 5;
1051
		//                }
1052
		//
1053
		//                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1054
		//                $SideInfoOffset += 4;
1055
		//                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1056
		//                $SideInfoOffset += 3;
1057
		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1058
		//            }
1059
		//
1060
		//            if ($thisfile_mpeg_audio['version'] == '1') {
1061
		//                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1062
		//                $SideInfoOffset += 1;
1063
		//            }
1064
		//            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1065
		//            $SideInfoOffset += 1;
1066
		//            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1067
		//            $SideInfoOffset += 1;
1068
		//        }
1069
		//    }
1070
		//}
1071
1072
		return true;
1073
	}
1074
1075
	public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1076
		$info = &$this->getid3->info;
1077
		$firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1078
		$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1079
1080
		for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1081
			// check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1082
			if (($nextframetestoffset + 4) >= $info['avdataend']) {
1083
				// end of file
1084
				return true;
1085
			}
1086
1087
			$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1088
			if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1089
				if ($ScanAsCBR) {
1090
					// force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1091
					if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1092
						return false;
1093
					}
1094
				}
1095
1096
1097
				// next frame is OK, get ready to check the one after that
1098
				if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1099
					$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1100
				} else {
1101
					$info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1102
					return false;
1103
				}
1104
1105
			} elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1106
1107
				// 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
1108
				return true;
1109
1110
			} else {
1111
1112
				// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1113
				$info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1114
1115
				return false;
1116
			}
1117
		}
1118
		return true;
1119
	}
1120
1121
	public function FreeFormatFrameLength($offset, $deepscan=false) {
1122
		$info = &$this->getid3->info;
1123
1124
		$this->fseek($offset);
1125
		$MPEGaudioData = $this->fread(32768);
1126
1127
		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
1128
		// may be different pattern due to padding
1129
		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1130
		if ($SyncPattern2 === $SyncPattern1) {
1131
			$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1132
		}
1133
1134
		$framelength = false;
1135
		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1136
		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1137
		if ($framelength1 > 4) {
1138
			$framelength = $framelength1;
1139
		}
1140
		if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1141
			$framelength = $framelength2;
1142
		}
1143
		if (!$framelength) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $framelength of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1144
1145
			// LAME 3.88 has a different value for modeextension on the first frame vs the rest
1146
			$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1147
			$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1148
1149
			if ($framelength1 > 4) {
1150
				$framelength = $framelength1;
1151
			}
1152
			if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1153
				$framelength = $framelength2;
1154
			}
1155
			if (!$framelength) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $framelength of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1156
				$info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1157
				return false;
1158
			} else {
1159
				$info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1160
				$info['audio']['codec']   = 'LAME';
1161
				$info['audio']['encoder'] = 'LAME3.88';
1162
				$SyncPattern1 = substr($SyncPattern1, 0, 3);
1163
				$SyncPattern2 = substr($SyncPattern2, 0, 3);
1164
			}
1165
		}
1166
1167
		if ($deepscan) {
1168
1169
			$ActualFrameLengthValues = array();
1170
			$nextoffset = $offset + $framelength;
1171
			while ($nextoffset < ($info['avdataend'] - 6)) {
1172
				$this->fseek($nextoffset - 1);
1173
				$NextSyncPattern = $this->fread(6);
1174
				if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1175
					// good - found where expected
1176
					$ActualFrameLengthValues[] = $framelength;
1177
				} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1178
					// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1179
					$ActualFrameLengthValues[] = ($framelength - 1);
1180
					$nextoffset--;
1181
				} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1182
					// ok - found one byte later than expected (last frame was padded, first frame wasn't)
1183
					$ActualFrameLengthValues[] = ($framelength + 1);
1184
					$nextoffset++;
1185
				} else {
1186
					$info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1187
					return false;
1188
				}
1189
				$nextoffset += $framelength;
1190
			}
1191
			if (count($ActualFrameLengthValues) > 0) {
1192
				$framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1193
			}
1194
		}
1195
		return $framelength;
1196
	}
1197
1198
	public function getOnlyMPEGaudioInfoBruteForce() {
1199
		$MPEGaudioHeaderDecodeCache   = array();
1200
		$MPEGaudioHeaderValidCache    = array();
1201
		$MPEGaudioHeaderLengthCache   = array();
1202
		$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1203
		$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1204
		$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1205
		$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1206
		$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1207
		$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1208
		$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1209
		$LongMPEGversionLookup        = array();
1210
		$LongMPEGlayerLookup          = array();
1211
		$LongMPEGbitrateLookup        = array();
1212
		$LongMPEGpaddingLookup        = array();
1213
		$LongMPEGfrequencyLookup      = array();
1214
		$Distribution['bitrate']      = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$Distribution was never initialized. Although not strictly required by PHP, it is generally a good practice to add $Distribution = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1215
		$Distribution['frequency']    = array();
1216
		$Distribution['layer']        = array();
1217
		$Distribution['version']      = array();
1218
		$Distribution['padding']      = array();
1219
1220
		$info = &$this->getid3->info;
1221
		$this->fseek($info['avdataoffset']);
1222
1223
		$max_frames_scan = 5000;
1224
		$frames_scanned  = 0;
1225
1226
		$previousvalidframe = $info['avdataoffset'];
1227
		while ($this->ftell() < $info['avdataend']) {
1228
			set_time_limit(30);
1229
			$head4 = $this->fread(4);
1230
			if (strlen($head4) < 4) {
1231
				break;
1232
			}
1233
			if ($head4{0} != "\xFF") {
1234
				for ($i = 1; $i < 4; $i++) {
1235
					if ($head4{$i} == "\xFF") {
1236
						$this->fseek($i - 4, SEEK_CUR);
1237
						continue 2;
1238
					}
1239
				}
1240
				continue;
1241
			}
1242
			if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1243
				$MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1244
			}
1245 View Code Duplication
			if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1246
				$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1247
			}
1248
			if ($MPEGaudioHeaderValidCache[$head4]) {
1249
1250
				if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1251
					$LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1252
					$LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1253
					$LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1254
					$LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1255
					$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1256
					$MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1257
						$LongMPEGbitrateLookup[$head4],
1258
						$LongMPEGversionLookup[$head4],
1259
						$LongMPEGlayerLookup[$head4],
1260
						$LongMPEGpaddingLookup[$head4],
1261
						$LongMPEGfrequencyLookup[$head4]);
1262
				}
1263
				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1264
					$WhereWeWere = $this->ftell();
1265
					$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1266
					$next4 = $this->fread(4);
1267
					if ($next4{0} == "\xFF") {
1268
						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1269
							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1270
						}
1271 View Code Duplication
						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1272
							$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1273
						}
1274
						if ($MPEGaudioHeaderValidCache[$next4]) {
1275
							$this->fseek(-4, SEEK_CUR);
1276
1277
							getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1278
							getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1279
							getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1280
							getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1281
							getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1282
							if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1283
								$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1284
								$info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1285
								foreach ($Distribution as $key1 => $value1) {
1286
									foreach ($value1 as $key2 => $value2) {
1287
										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1288
									}
1289
								}
1290
								break;
1291
							}
1292
							continue;
1293
						}
1294
					}
1295
					unset($next4);
1296
					$this->fseek($WhereWeWere - 3);
1297
				}
1298
1299
			}
1300
		}
1301
		foreach ($Distribution as $key => $value) {
1302
			ksort($Distribution[$key], SORT_NUMERIC);
1303
		}
1304
		ksort($Distribution['version'], SORT_STRING);
1305
		$info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
1306
		$info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1307
		$info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
1308
		$info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
1309
		$info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
1310
		if (count($Distribution['version']) > 1) {
1311
			$info['error'][] = 'Corrupt file - more than one MPEG version detected';
1312
		}
1313
		if (count($Distribution['layer']) > 1) {
1314
			$info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1315
		}
1316
		if (count($Distribution['frequency']) > 1) {
1317
			$info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1318
		}
1319
1320
1321
		$bittotal = 0;
1322
		foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1323
			if ($bitratevalue != 'free') {
1324
				$bittotal += ($bitratevalue * $bitratecount);
1325
			}
1326
		}
1327
		$info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
1328
		if ($info['mpeg']['audio']['frame_count'] == 0) {
1329
			$info['error'][] = 'no MPEG audio frames found';
1330
			return false;
1331
		}
1332
		$info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
1333
		$info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1334
		$info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
1335
1336
		$info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1337
		$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1338
		$info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1339
		$info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1340
		$info['fileformat']            = $info['audio']['dataformat'];
1341
1342
		return true;
1343
	}
1344
1345
1346
	public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1347
		// looks for synch, decodes MPEG audio header
1348
1349
		$info = &$this->getid3->info;
1350
1351
		static $MPEGaudioVersionLookup;
1352
		static $MPEGaudioLayerLookup;
1353
		static $MPEGaudioBitrateLookup;
1354
		if (empty($MPEGaudioVersionLookup)) {
1355
		   $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1356
		   $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
1357
		   $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1358
1359
		}
1360
1361
		$this->fseek($avdataoffset);
1362
		$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1363
		if ($sync_seek_buffer_size <= 0) {
1364
			$info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1365
			return false;
1366
		}
1367
		$header = $this->fread($sync_seek_buffer_size);
1368
		$sync_seek_buffer_size = strlen($header);
1369
		$SynchSeekOffset = 0;
1370
		while ($SynchSeekOffset < $sync_seek_buffer_size) {
1371
			if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
1372
1373
				if ($SynchSeekOffset > $sync_seek_buffer_size) {
1374
					// if a synch's not found within the first 128k bytes, then give up
1375
					$info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1376
					if (isset($info['audio']['bitrate'])) {
1377
						unset($info['audio']['bitrate']);
1378
					}
1379
					if (isset($info['mpeg']['audio'])) {
1380
						unset($info['mpeg']['audio']);
1381
					}
1382
					if (empty($info['mpeg'])) {
1383
						unset($info['mpeg']);
1384
					}
1385
					return false;
1386
1387
				} elseif (feof($this->getid3->fp)) {
1388
1389
					$info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1390
					if (isset($info['audio']['bitrate'])) {
1391
						unset($info['audio']['bitrate']);
1392
					}
1393
					if (isset($info['mpeg']['audio'])) {
1394
						unset($info['mpeg']['audio']);
1395
					}
1396 View Code Duplication
					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1397
						unset($info['mpeg']);
1398
					}
1399
					return false;
1400
				}
1401
			}
1402
1403
			if (($SynchSeekOffset + 1) >= strlen($header)) {
1404
				$info['error'][] = 'Could not find valid MPEG synch before end of file';
1405
				return false;
1406
			}
1407
1408
			if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1409
				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1410
					$FirstFrameThisfileInfo = $info;
1411
					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1412
					if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1413
						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1414
						// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1415
						unset($FirstFrameThisfileInfo);
1416
					}
1417
				}
1418
1419
				$dummy = $info; // only overwrite real data if valid header found
1420
				if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1421
					$info = $dummy;
1422
					$info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1423
					switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1424
						case '':
1425
						case 'id3':
1426
						case 'ape':
1427
						case 'mp3':
1428
							$info['fileformat']          = 'mp3';
1429
							$info['audio']['dataformat'] = 'mp3';
1430
							break;
1431
					}
1432
					if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1433
						if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1434
							// If there is garbage data between a valid VBR header frame and a sequence
1435
							// of valid MPEG-audio frames the VBR data is no longer discarded.
1436
							$info = $FirstFrameThisfileInfo;
1437
							$info['avdataoffset']        = $FirstFrameAVDataOffset;
0 ignored issues
show
Bug introduced by
The variable $FirstFrameAVDataOffset does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1438
							$info['fileformat']          = 'mp3';
1439
							$info['audio']['dataformat'] = 'mp3';
1440
							$dummy                       = $info;
1441
							unset($dummy['mpeg']['audio']);
1442
							$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1443
							$GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
1444
							if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1445
								$info = $dummy;
1446
								$info['avdataoffset'] = $GarbageOffsetEnd;
1447
								$info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1448
							} else {
1449
								$info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1450
							}
1451
						}
1452
					}
1453
					if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1454
						// VBR file with no VBR header
1455
						$BitrateHistogram = true;
1456
					}
1457
1458
					if ($BitrateHistogram) {
1459
1460
						$info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1461
						$info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1462
1463
						if ($info['mpeg']['audio']['version'] == '1') {
1464
							if ($info['mpeg']['audio']['layer'] == 3) {
1465
								$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);
1466 View Code Duplication
							} elseif ($info['mpeg']['audio']['layer'] == 2) {
1467
								$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);
1468
							} elseif ($info['mpeg']['audio']['layer'] == 1) {
1469
								$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);
1470
							}
1471 View Code Duplication
						} elseif ($info['mpeg']['audio']['layer'] == 1) {
1472
							$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);
1473
						} else {
1474
							$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);
1475
						}
1476
1477
						$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1478
						$synchstartoffset = $info['avdataoffset'];
1479
						$this->fseek($info['avdataoffset']);
1480
1481
						// you can play with these numbers:
1482
						$max_frames_scan  = 50000;
1483
						$max_scan_segments = 10;
1484
1485
						// don't play with these numbers:
1486
						$FastMode = false;
1487
						$SynchErrorsFound = 0;
1488
						$frames_scanned   = 0;
1489
						$this_scan_segment = 0;
1490
						$frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1491
						$pct_data_scanned = 0;
1492
						for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1493
							$frames_scanned_this_segment = 0;
1494
							if ($this->ftell() >= $info['avdataend']) {
1495
								break;
1496
							}
1497
							$scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$scan_start_offset was never initialized. Although not strictly required by PHP, it is generally a good practice to add $scan_start_offset = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1498
							if ($current_segment > 0) {
1499
								$this->fseek($scan_start_offset[$current_segment]);
0 ignored issues
show
Bug introduced by
The variable $scan_start_offset does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1500
								$buffer_4k = $this->fread(4096);
1501
								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1502
									if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1503
										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1504
											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1505
											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1506
												$scan_start_offset[$current_segment] += $j;
1507
												break;
1508
											}
1509
										}
1510
									}
1511
								}
1512
							}
1513
							$synchstartoffset = $scan_start_offset[$current_segment];
1514
							while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1515
								$FastMode = true;
1516
								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1517
1518
								if (empty($dummy['mpeg']['audio']['framelength'])) {
1519
									$SynchErrorsFound++;
1520
									$synchstartoffset++;
1521
								} else {
1522
									getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1523
									getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1524
									getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1525
									$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1526
								}
1527
								$frames_scanned++;
1528
								if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1529
									$this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1530
									if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1531
										// file likely contains < $max_frames_scan, just scan as one segment
1532
										$max_scan_segments = 1;
1533
										$frames_scan_per_segment = $max_frames_scan;
1534
									} else {
1535
										$pct_data_scanned += $this_pct_scanned;
1536
										break;
1537
									}
1538
								}
1539
							}
1540
						}
1541
						if ($pct_data_scanned > 0) {
1542
							$info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1543
							foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1544
								if (!preg_match('#_distribution$#i', $key1)) {
1545
									continue;
1546
								}
1547
								foreach ($value1 as $key2 => $value2) {
1548
									$info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1549
								}
1550
							}
1551
						}
1552
1553
						if ($SynchErrorsFound > 0) {
1554
							$info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1555
							//return false;
1556
						}
1557
1558
						$bittotal     = 0;
1559
						$framecounter = 0;
1560
						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1561
							$framecounter += $bitratecount;
1562
							if ($bitratevalue != 'free') {
1563
								$bittotal += ($bitratevalue * $bitratecount);
1564
							}
1565
						}
1566
						if ($framecounter == 0) {
1567
							$info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1568
							return false;
1569
						}
1570
						$info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1571
						$info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
1572
1573
						$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1574
1575
1576
						// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1577
						$distinct_bitrates = 0;
1578
						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1579
							if ($bitrate_count > 0) {
1580
								$distinct_bitrates++;
1581
							}
1582
						}
1583 View Code Duplication
						if ($distinct_bitrates > 1) {
1584
							$info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1585
						} else {
1586
							$info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1587
						}
1588
						$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1589
1590
					}
1591
1592
					break; // exit while()
1593
				}
1594
			}
1595
1596
			$SynchSeekOffset++;
1597
			if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1598
				// end of file/data
1599
1600
				if (empty($info['mpeg']['audio'])) {
1601
1602
					$info['error'][] = 'could not find valid MPEG synch before end of file';
1603
					if (isset($info['audio']['bitrate'])) {
1604
						unset($info['audio']['bitrate']);
1605
					}
1606
					if (isset($info['mpeg']['audio'])) {
1607
						unset($info['mpeg']['audio']);
1608
					}
1609 View Code Duplication
					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1610
						unset($info['mpeg']);
1611
					}
1612
					return false;
1613
1614
				}
1615
				break;
1616
			}
1617
1618
		}
1619
		$info['audio']['channels']        = $info['mpeg']['audio']['channels'];
1620
		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
1621
		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
1622
		return true;
1623
	}
1624
1625
1626
	public static function MPEGaudioVersionArray() {
1627
		static $MPEGaudioVersion = array('2.5', false, '2', '1');
1628
		return $MPEGaudioVersion;
1629
	}
1630
1631
	public static function MPEGaudioLayerArray() {
1632
		static $MPEGaudioLayer = array(false, 3, 2, 1);
1633
		return $MPEGaudioLayer;
1634
	}
1635
1636
	public static function MPEGaudioBitrateArray() {
1637
		static $MPEGaudioBitrate;
1638
		if (empty($MPEGaudioBitrate)) {
1639
			$MPEGaudioBitrate = array (
1640
				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1641
								2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1642
								3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1643
							   ),
1644
1645
				'2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1646
								2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
1647
							   )
1648
			);
1649
			$MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1650
			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
1651
		}
1652
		return $MPEGaudioBitrate;
1653
	}
1654
1655
	public static function MPEGaudioFrequencyArray() {
1656
		static $MPEGaudioFrequency;
1657
		if (empty($MPEGaudioFrequency)) {
1658
			$MPEGaudioFrequency = array (
1659
				'1'   => array(44100, 48000, 32000),
1660
				'2'   => array(22050, 24000, 16000),
1661
				'2.5' => array(11025, 12000,  8000)
1662
			);
1663
		}
1664
		return $MPEGaudioFrequency;
1665
	}
1666
1667
	public static function MPEGaudioChannelModeArray() {
1668
		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1669
		return $MPEGaudioChannelMode;
1670
	}
1671
1672
	public static function MPEGaudioModeExtensionArray() {
1673
		static $MPEGaudioModeExtension;
1674
		if (empty($MPEGaudioModeExtension)) {
1675
			$MPEGaudioModeExtension = array (
1676
				1 => array('4-31', '8-31', '12-31', '16-31'),
1677
				2 => array('4-31', '8-31', '12-31', '16-31'),
1678
				3 => array('', 'IS', 'MS', 'IS+MS')
1679
			);
1680
		}
1681
		return $MPEGaudioModeExtension;
1682
	}
1683
1684
	public static function MPEGaudioEmphasisArray() {
1685
		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1686
		return $MPEGaudioEmphasis;
1687
	}
1688
1689
	public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1690
		return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1691
	}
1692
1693
	public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1694
		if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1695
			return false;
1696
		}
1697
1698
		static $MPEGaudioVersionLookup;
1699
		static $MPEGaudioLayerLookup;
1700
		static $MPEGaudioBitrateLookup;
1701
		static $MPEGaudioFrequencyLookup;
1702
		static $MPEGaudioChannelModeLookup;
1703
		static $MPEGaudioModeExtensionLookup;
1704
		static $MPEGaudioEmphasisLookup;
1705 View Code Duplication
		if (empty($MPEGaudioVersionLookup)) {
1706
			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1707
			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1708
			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1709
			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1710
			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1711
			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1712
			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1713
		}
1714
1715 View Code Duplication
		if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1716
			$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1717
		} else {
1718
			echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1719
			return false;
1720
		}
1721 View Code Duplication
		if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1722
			$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1723
		} else {
1724
			echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1725
			return false;
1726
		}
1727
		if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1728
			echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1729
			if ($rawarray['bitrate'] == 15) {
1730
				// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1731
				// let it go through here otherwise file will not be identified
1732
				if (!$allowBitrate15) {
1733
					return false;
1734
				}
1735
			} else {
1736
				return false;
1737
			}
1738
		}
1739 View Code Duplication
		if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1740
			echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1741
			return false;
1742
		}
1743 View Code Duplication
		if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1744
			echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1745
			return false;
1746
		}
1747 View Code Duplication
		if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1748
			echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1749
			return false;
1750
		}
1751 View Code Duplication
		if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1752
			echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1753
			return false;
1754
		}
1755
		// These are just either set or not set, you can't mess that up :)
1756
		// $rawarray['protection'];
1757
		// $rawarray['padding'];
1758
		// $rawarray['private'];
1759
		// $rawarray['copyright'];
1760
		// $rawarray['original'];
1761
1762
		return true;
1763
	}
1764
1765
	public static function MPEGaudioHeaderDecode($Header4Bytes) {
1766
		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
1767
		// A - Frame sync (all bits set)
1768
		// B - MPEG Audio version ID
1769
		// C - Layer description
1770
		// D - Protection bit
1771
		// E - Bitrate index
1772
		// F - Sampling rate frequency index
1773
		// G - Padding bit
1774
		// H - Private bit
1775
		// I - Channel Mode
1776
		// J - Mode extension (Only if Joint stereo)
1777
		// K - Copyright
1778
		// L - Original
1779
		// M - Emphasis
1780
1781
		if (strlen($Header4Bytes) != 4) {
1782
			return false;
1783
		}
1784
1785
		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$MPEGrawHeader was never initialized. Although not strictly required by PHP, it is generally a good practice to add $MPEGrawHeader = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1786
		$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
1787
		$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
1788
		$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
1789
		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1790
		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
1791
		$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
1792
		$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
1793
		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1794
		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
1795
		$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
1796
		$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
1797
		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
1798
1799
		return $MPEGrawHeader;
1800
	}
1801
1802
	public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1803
		static $AudioFrameLengthCache = array();
1804
1805
		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1806
			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1807
			if ($bitrate != 'free') {
1808
1809
				if ($version == '1') {
1810
1811
					if ($layer == '1') {
1812
1813
						// For Layer I slot is 32 bits long
1814
						$FrameLengthCoefficient = 48;
1815
						$SlotLength = 4;
1816
1817
					} else { // Layer 2 / 3
1818
1819
						// for Layer 2 and Layer 3 slot is 8 bits long.
1820
						$FrameLengthCoefficient = 144;
1821
						$SlotLength = 1;
1822
1823
					}
1824
1825
				} else { // MPEG-2 / MPEG-2.5
1826
1827
					if ($layer == '1') {
1828
1829
						// For Layer I slot is 32 bits long
1830
						$FrameLengthCoefficient = 24;
1831
						$SlotLength = 4;
1832
1833
					} elseif ($layer == '2') {
1834
1835
						// for Layer 2 and Layer 3 slot is 8 bits long.
1836
						$FrameLengthCoefficient = 144;
1837
						$SlotLength = 1;
1838
1839
					} else { // layer 3
1840
1841
						// for Layer 2 and Layer 3 slot is 8 bits long.
1842
						$FrameLengthCoefficient = 72;
1843
						$SlotLength = 1;
1844
1845
					}
1846
1847
				}
1848
1849
				// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1850
				if ($samplerate > 0) {
1851
					$NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1852
					$NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1853
					if ($padding) {
1854
						$NewFramelength += $SlotLength;
1855
					}
1856
					$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1857
				}
1858
			}
1859
		}
1860
		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1861
	}
1862
1863
	public static function ClosestStandardMP3Bitrate($bit_rate) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1864
		static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1865
		static $bit_rate_table = array (0=>'-');
1866
		$round_bit_rate = intval(round($bit_rate, -3));
1867
		if (!isset($bit_rate_table[$round_bit_rate])) {
1868
			if ($round_bit_rate > max($standard_bit_rates)) {
1869
				$bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
1870
			} else {
1871
				$bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
1872
				foreach ($standard_bit_rates as $standard_bit_rate) {
1873
					if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
1874
						break;
1875
					}
1876
					$bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1877
				}
1878
			}
1879
		}
1880
		return $bit_rate_table[$round_bit_rate];
1881
	}
1882
1883
	public static function XingVBRidOffset($version, $channelmode) {
1884
		static $XingVBRidOffsetCache = array();
1885
		if (empty($XingVBRidOffset)) {
0 ignored issues
show
Bug introduced by
The variable $XingVBRidOffset seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1886
			$XingVBRidOffset = array (
1887
				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
1888
								'stereo'        => 0x24, // 4 + 32 = 36
1889
								'joint stereo'  => 0x24,
1890
								'dual channel'  => 0x24
1891
							   ),
1892
1893
				'2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
1894
								'stereo'        => 0x15, // 4 + 17 = 21
1895
								'joint stereo'  => 0x15,
1896
								'dual channel'  => 0x15
1897
							   ),
1898
1899
				'2.5' => array ('mono'          => 0x15,
1900
								'stereo'        => 0x15,
1901
								'joint stereo'  => 0x15,
1902
								'dual channel'  => 0x15
1903
							   )
1904
			);
1905
		}
1906
		return $XingVBRidOffset[$version][$channelmode];
1907
	}
1908
1909
	public static function LAMEvbrMethodLookup($VBRmethodID) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1910
		static $LAMEvbrMethodLookup = array(
1911
			0x00 => 'unknown',
1912
			0x01 => 'cbr',
1913
			0x02 => 'abr',
1914
			0x03 => 'vbr-old / vbr-rh',
1915
			0x04 => 'vbr-new / vbr-mtrh',
1916
			0x05 => 'vbr-mt',
1917
			0x06 => 'vbr (full vbr method 4)',
1918
			0x08 => 'cbr (constant bitrate 2 pass)',
1919
			0x09 => 'abr (2 pass)',
1920
			0x0F => 'reserved'
1921
		);
1922
		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1923
	}
1924
1925 View Code Duplication
	public static function LAMEmiscStereoModeLookup($StereoModeID) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1926
		static $LAMEmiscStereoModeLookup = array(
1927
			0 => 'mono',
1928
			1 => 'stereo',
1929
			2 => 'dual mono',
1930
			3 => 'joint stereo',
1931
			4 => 'forced stereo',
1932
			5 => 'auto',
1933
			6 => 'intensity stereo',
1934
			7 => 'other'
1935
		);
1936
		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1937
	}
1938
1939
	public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1940
		static $LAMEmiscSourceSampleFrequencyLookup = array(
1941
			0 => '<= 32 kHz',
1942
			1 => '44.1 kHz',
1943
			2 => '48 kHz',
1944
			3 => '> 48kHz'
1945
		);
1946
		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1947
	}
1948
1949
	public static function LAMEsurroundInfoLookup($SurroundInfoID) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1950
		static $LAMEsurroundInfoLookup = array(
1951
			0 => 'no surround info',
1952
			1 => 'DPL encoding',
1953
			2 => 'DPL2 encoding',
1954
			3 => 'Ambisonic encoding'
1955
		);
1956
		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1957
	}
1958
1959
	public static function LAMEpresetUsedLookup($LAMEtag) {
1960
1961
		if ($LAMEtag['preset_used_id'] == 0) {
1962
			// no preset used (LAME >=3.93)
1963
			// no preset recorded (LAME <3.93)
1964
			return '';
1965
		}
1966
		$LAMEpresetUsedLookup = array();
1967
1968
		/////  THIS PART CANNOT BE STATIC .
1969
		for ($i = 8; $i <= 320; $i++) {
1970
			switch ($LAMEtag['vbr_method']) {
1971
				case 'cbr':
1972
					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1973
					break;
1974
				case 'abr':
1975
				default: // other VBR modes shouldn't be here(?)
1976
					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1977
					break;
1978
			}
1979
		}
1980
1981
		// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1982
1983
		// named alt-presets
1984
		$LAMEpresetUsedLookup[1000] = '--r3mix';
1985
		$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1986
		$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1987
		$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1988
		$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1989
		$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
1990
		$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
1991
		$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
1992
1993
		// LAME 3.94 additions/changes
1994
		$LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
1995
		$LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
1996
1997
		$LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
1998
		$LAMEpresetUsedLookup[410]  = '-V9';
1999
		$LAMEpresetUsedLookup[420]  = '-V8';
2000
		$LAMEpresetUsedLookup[440]  = '-V6';
2001
		$LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
2002
		$LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
2003
		$LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
2004
		$LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
2005
		$LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
2006
		$LAMEpresetUsedLookup[490]  = '-V1';
2007
		$LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
2008
2009
		return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to [email protected]');
2010
	}
2011
2012
}
2013