1
|
|
|
<?php |
2
|
|
|
///////////////////////////////////////////////////////////////// |
3
|
|
|
/// getID3() by James Heinrich <[email protected]> // |
4
|
|
|
// available at https://github.com/JamesHeinrich/getID3 // |
5
|
|
|
// or https://www.getid3.org // |
6
|
|
|
// or http://getid3.sourceforge.net // |
7
|
|
|
// // |
8
|
|
|
// /demo/demo.mp3header.php - part of getID3() // |
9
|
|
|
// Sample script for decoding MP3 header bytes // |
10
|
|
|
// see readme.txt for more details // |
11
|
|
|
// /// |
12
|
|
|
///////////////////////////////////////////////////////////////// |
13
|
|
|
|
14
|
|
|
if (!function_exists('PrintHexBytes')) { |
15
|
|
|
function PrintHexBytes($string) { |
16
|
|
|
$returnstring = ''; |
17
|
|
|
for ($i = 0; $i < strlen($string); $i++) { |
18
|
|
|
$returnstring .= str_pad(dechex(ord(substr($string, $i, 1))), 2, '0', STR_PAD_LEFT).' '; |
19
|
|
|
} |
20
|
|
|
return $returnstring; |
21
|
|
|
} |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
if (!function_exists('PrintTextBytes')) { |
25
|
|
|
function PrintTextBytes($string) { |
26
|
|
|
$returnstring = ''; |
27
|
|
|
for ($i = 0; $i < strlen($string); $i++) { |
28
|
|
|
if (ord(substr($string, $i, 1)) <= 31) { |
29
|
|
|
$returnstring .= ' '; |
30
|
|
|
} else { |
31
|
|
|
$returnstring .= ' '.substr($string, $i, 1).' '; |
32
|
|
|
} |
33
|
|
|
} |
34
|
|
|
return $returnstring; |
35
|
|
|
} |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
if (!function_exists('table_var_dump')) { |
39
|
|
|
function table_var_dump($variable) { |
|
|
|
|
40
|
|
|
$returnstring = ''; |
41
|
|
|
switch (gettype($variable)) { |
42
|
|
|
case 'array': |
43
|
|
|
$returnstring .= '<table border="1" cellspacing="0" cellpadding="2">'; |
44
|
|
|
foreach ($variable as $key => $value) { |
45
|
|
|
$returnstring .= '<tr><td valign="top"><b>'.str_replace(chr(0), ' ', $key).'</b></td>'; |
46
|
|
|
$returnstring .= '<td valign="top">'.gettype($value); |
47
|
|
View Code Duplication |
if (is_array($value)) { |
|
|
|
|
48
|
|
|
$returnstring .= ' ('.count($value).')'; |
49
|
|
|
} elseif (is_string($value)) { |
50
|
|
|
$returnstring .= ' ('.strlen($value).')'; |
51
|
|
|
} |
52
|
|
|
if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) { |
53
|
|
|
require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php'); |
54
|
|
|
$imageinfo = array(); |
55
|
|
|
if ($imagechunkcheck = GetDataImageSize($value, $imageinfo)) { |
56
|
|
|
$DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.image_type_to_mime_type($imagechunkcheck[2]); |
57
|
|
|
if ($tempimagefile = fopen($DumpedImageSRC, 'wb')) { |
58
|
|
|
fwrite($tempimagefile, $value); |
59
|
|
|
fclose($tempimagefile); |
60
|
|
|
} |
61
|
|
|
$returnstring .= '</td><td><img src="'.$DumpedImageSRC.'" width="'.$imagechunkcheck[0].'" height="'.$imagechunkcheck[1].'"></td></tr>'; |
62
|
|
|
} else { |
63
|
|
|
$returnstring .= '</td><td><i>invalid image data</i></td></tr>'; |
64
|
|
|
} |
65
|
|
|
} else { |
66
|
|
|
$returnstring .= '</td><td>'.table_var_dump($value).'</td></tr>'; |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
$returnstring .= '</table>'; |
70
|
|
|
break; |
71
|
|
|
|
72
|
|
|
case 'boolean': |
73
|
|
|
$returnstring .= ($variable ? 'TRUE' : 'FALSE'); |
74
|
|
|
break; |
75
|
|
|
|
76
|
|
|
case 'integer': |
77
|
|
|
case 'double': |
78
|
|
|
case 'float': |
79
|
|
|
$returnstring .= $variable; |
80
|
|
|
break; |
81
|
|
|
|
82
|
|
|
case 'object': |
83
|
|
|
case 'null': |
84
|
|
|
$returnstring .= string_var_dump($variable); |
85
|
|
|
break; |
86
|
|
|
|
87
|
|
|
case 'string': |
88
|
|
|
$variable = str_replace(chr(0), ' ', $variable); |
89
|
|
|
$varlen = strlen($variable); |
90
|
|
|
for ($i = 0; $i < $varlen; $i++) { |
91
|
|
|
if (preg_match('#['.chr(0x0A).chr(0x0D).' -;0-9A-Za-z]#', $variable[$i])) { |
92
|
|
|
$returnstring .= $variable[$i]; |
93
|
|
|
} else { |
94
|
|
|
$returnstring .= '&#'.str_pad(ord($variable[$i]), 3, '0', STR_PAD_LEFT).';'; |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
$returnstring = nl2br($returnstring); |
98
|
|
|
break; |
99
|
|
|
|
100
|
|
|
default: |
101
|
|
|
require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php'); |
102
|
|
|
$imageinfo = array(); |
103
|
|
|
if (($imagechunkcheck = GetDataImageSize(substr($variable, 0, 32768), $imageinfo)) && ($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { |
104
|
|
|
$returnstring .= '<table border="1" cellspacing="0" cellpadding="2">'; |
105
|
|
|
$returnstring .= '<tr><td><b>type</b></td><td>'.image_type_to_mime_type($imagechunkcheck[2]).'</td></tr>'; |
106
|
|
|
$returnstring .= '<tr><td><b>width</b></td><td>'.number_format($imagechunkcheck[0]).' px</td></tr>'; |
107
|
|
|
$returnstring .= '<tr><td><b>height</b></td><td>'.number_format($imagechunkcheck[1]).' px</td></tr>'; |
108
|
|
|
$returnstring .= '<tr><td><b>size</b></td><td>'.number_format(strlen($variable)).' bytes</td></tr></table>'; |
109
|
|
|
} else { |
110
|
|
|
$returnstring .= nl2br(htmlspecialchars(str_replace(chr(0), ' ', $variable))); |
111
|
|
|
} |
112
|
|
|
break; |
113
|
|
|
} |
114
|
|
|
return $returnstring; |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
if (!function_exists('string_var_dump')) { |
119
|
|
View Code Duplication |
function string_var_dump($variable) { |
|
|
|
|
120
|
|
|
if (version_compare(PHP_VERSION, '4.3.0', '>=')) { |
121
|
|
|
return print_r($variable, true); |
122
|
|
|
} |
123
|
|
|
ob_start(); |
124
|
|
|
var_dump($variable); |
|
|
|
|
125
|
|
|
$dumpedvariable = ob_get_contents(); |
126
|
|
|
ob_end_clean(); |
127
|
|
|
return $dumpedvariable; |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (!function_exists('fileextension')) { |
132
|
|
View Code Duplication |
function fileextension($filename, $numextensions=1) { |
|
|
|
|
133
|
|
|
if (strstr($filename, '.')) { |
134
|
|
|
$reversedfilename = strrev($filename); |
135
|
|
|
$offset = 0; |
136
|
|
|
for ($i = 0; $i < $numextensions; $i++) { |
137
|
|
|
$offset = strpos($reversedfilename, '.', $offset + 1); |
138
|
|
|
if ($offset === false) { |
139
|
|
|
return ''; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
return strrev(substr($reversedfilename, 0, $offset)); |
143
|
|
|
} |
144
|
|
|
return ''; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
if (!function_exists('RemoveAccents')) { |
149
|
|
|
function RemoveAccents($string) { |
|
|
|
|
150
|
|
|
// return strtr($string, '¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ', 'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy'); |
151
|
|
|
// Revised version by [email protected] |
152
|
|
|
return strtr(strtr($string, 'ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'), array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', '' => 'OE', '' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u')); |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
if (!function_exists('MoreNaturalSort')) { |
157
|
|
View Code Duplication |
function MoreNaturalSort($ar1, $ar2) { |
|
|
|
|
158
|
|
|
if ($ar1 === $ar2) { |
159
|
|
|
return 0; |
160
|
|
|
} |
161
|
|
|
$len1 = strlen($ar1); |
162
|
|
|
$len2 = strlen($ar2); |
163
|
|
|
$shortest = min($len1, $len2); |
164
|
|
|
if (substr($ar1, 0, $shortest) === substr($ar2, 0, $shortest)) { |
165
|
|
|
// the shorter argument is the beginning of the longer one, like "str" and "string" |
166
|
|
|
if ($len1 < $len2) { |
167
|
|
|
return -1; |
168
|
|
|
} elseif ($len1 > $len2) { |
169
|
|
|
return 1; |
170
|
|
|
} |
171
|
|
|
return 0; |
172
|
|
|
} |
173
|
|
|
$ar1 = RemoveAccents(strtolower(trim($ar1))); |
174
|
|
|
$ar2 = RemoveAccents(strtolower(trim($ar2))); |
175
|
|
|
$translatearray = array('\''=>'', '"'=>'', '_'=>' ', '('=>'', ')'=>'', '-'=>' ', ' '=>' ', '.'=>'', ','=>''); |
176
|
|
|
foreach ($translatearray as $key => $val) { |
177
|
|
|
$ar1 = str_replace($key, $val, $ar1); |
178
|
|
|
$ar2 = str_replace($key, $val, $ar2); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
if ($ar1 < $ar2) { |
182
|
|
|
return -1; |
183
|
|
|
} elseif ($ar1 > $ar2) { |
184
|
|
|
return 1; |
185
|
|
|
} |
186
|
|
|
return 0; |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
if (!function_exists('trunc')) { |
191
|
|
View Code Duplication |
function trunc($floatnumber) { |
|
|
|
|
192
|
|
|
// truncates a floating-point number at the decimal point |
193
|
|
|
// returns int (if possible, otherwise float) |
194
|
|
|
if ($floatnumber >= 1) { |
195
|
|
|
$truncatednumber = floor($floatnumber); |
196
|
|
|
} elseif ($floatnumber <= -1) { |
197
|
|
|
$truncatednumber = ceil($floatnumber); |
198
|
|
|
} else { |
199
|
|
|
$truncatednumber = 0; |
200
|
|
|
} |
201
|
|
|
if ($truncatednumber <= pow(2, 30)) { |
202
|
|
|
$truncatednumber = (int) $truncatednumber; |
203
|
|
|
} |
204
|
|
|
return $truncatednumber; |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
if (!function_exists('CastAsInt')) { |
209
|
|
|
function CastAsInt($floatnum) { |
210
|
|
|
// convert to float if not already |
211
|
|
|
$floatnum = (float) $floatnum; |
212
|
|
|
|
213
|
|
|
// convert a float to type int, only if possible |
214
|
|
|
if (trunc($floatnum) == $floatnum) { |
215
|
|
|
// it's not floating point |
216
|
|
|
if ($floatnum <= pow(2, 30)) { |
217
|
|
|
// it's within int range |
218
|
|
|
$floatnum = (int) $floatnum; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
return $floatnum; |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
if (!function_exists('getmicrotime')) { |
226
|
|
|
function getmicrotime() { |
227
|
|
|
list($usec, $sec) = explode(' ', microtime()); |
228
|
|
|
return ((float) $usec + (float) $sec); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
if (!function_exists('DecimalBinary2Float')) { |
233
|
|
|
function DecimalBinary2Float($binarynumerator) { |
234
|
|
|
$numerator = Bin2Dec($binarynumerator); |
235
|
|
|
$denominator = Bin2Dec(str_repeat('1', strlen($binarynumerator))); |
236
|
|
|
return ($numerator / $denominator); |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
if (!function_exists('NormalizeBinaryPoint')) { |
241
|
|
View Code Duplication |
function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { |
|
|
|
|
242
|
|
|
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html |
243
|
|
|
if (strpos($binarypointnumber, '.') === false) { |
244
|
|
|
$binarypointnumber = '0.'.$binarypointnumber; |
245
|
|
|
} elseif ($binarypointnumber[0] == '.') { |
246
|
|
|
$binarypointnumber = '0'.$binarypointnumber; |
247
|
|
|
} |
248
|
|
|
$exponent = 0; |
249
|
|
|
while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) { |
250
|
|
|
if (substr($binarypointnumber, 1, 1) == '.') { |
251
|
|
|
$exponent--; |
252
|
|
|
$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3); |
253
|
|
|
} else { |
254
|
|
|
$pointpos = strpos($binarypointnumber, '.'); |
255
|
|
|
$exponent += ($pointpos - 1); |
256
|
|
|
$binarypointnumber = str_replace('.', '', $binarypointnumber); |
257
|
|
|
$binarypointnumber = $binarypointnumber[0].'.'.substr($binarypointnumber, 1); |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT); |
261
|
|
|
return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
if (!function_exists('Float2BinaryDecimal')) { |
266
|
|
|
function Float2BinaryDecimal($floatvalue) { |
267
|
|
|
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html |
268
|
|
|
$maxbits = 128; // to how many bits of precision should the calculations be taken? |
269
|
|
|
$intpart = trunc($floatvalue); |
270
|
|
|
$floatpart = abs($floatvalue - $intpart); |
271
|
|
|
$pointbitstring = ''; |
272
|
|
View Code Duplication |
while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { |
|
|
|
|
273
|
|
|
$floatpart *= 2; |
274
|
|
|
$pointbitstring .= (string) trunc($floatpart); |
275
|
|
|
$floatpart -= trunc($floatpart); |
276
|
|
|
} |
277
|
|
|
$binarypointnumber = decbin($intpart).'.'.$pointbitstring; |
278
|
|
|
return $binarypointnumber; |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
if (!function_exists('Float2String')) { |
283
|
|
|
function Float2String($floatvalue, $bits) { |
284
|
|
|
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html |
285
|
|
View Code Duplication |
switch ($bits) { |
|
|
|
|
286
|
|
|
case 32: |
287
|
|
|
$exponentbits = 8; |
288
|
|
|
$fractionbits = 23; |
289
|
|
|
break; |
290
|
|
|
|
291
|
|
|
case 64: |
292
|
|
|
$exponentbits = 11; |
293
|
|
|
$fractionbits = 52; |
294
|
|
|
break; |
295
|
|
|
|
296
|
|
|
default: |
297
|
|
|
return false; |
298
|
|
|
break; |
|
|
|
|
299
|
|
|
} |
300
|
|
|
if ($floatvalue >= 0) { |
301
|
|
|
$signbit = '0'; |
302
|
|
|
} else { |
303
|
|
|
$signbit = '1'; |
304
|
|
|
} |
305
|
|
|
$normalizedbinary = NormalizeBinaryPoint(Float2BinaryDecimal($floatvalue), $fractionbits); |
306
|
|
|
$biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent |
307
|
|
|
$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); |
308
|
|
|
$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); |
309
|
|
|
|
310
|
|
|
return BigEndian2String(Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
if (!function_exists('LittleEndian2Float')) { |
315
|
|
|
function LittleEndian2Float($byteword) { |
316
|
|
|
return BigEndian2Float(strrev($byteword)); |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
if (!function_exists('BigEndian2Float')) { |
321
|
|
|
function BigEndian2Float($byteword) { |
322
|
|
|
// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic |
323
|
|
|
// http://www.psc.edu/general/software/packages/ieee/ieee.html |
324
|
|
|
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html |
325
|
|
|
|
326
|
|
|
$bitword = BigEndian2Bin($byteword); |
327
|
|
|
$signbit = $bitword[0]; |
328
|
|
|
|
329
|
|
|
switch (strlen($byteword) * 8) { |
330
|
|
|
case 32: |
331
|
|
|
$exponentbits = 8; |
332
|
|
|
$fractionbits = 23; |
333
|
|
|
break; |
334
|
|
|
|
335
|
|
|
case 64: |
336
|
|
|
$exponentbits = 11; |
337
|
|
|
$fractionbits = 52; |
338
|
|
|
break; |
339
|
|
|
|
340
|
|
|
case 80: |
341
|
|
|
$exponentbits = 16; |
342
|
|
|
$fractionbits = 64; |
343
|
|
|
break; |
344
|
|
|
|
345
|
|
|
default: |
346
|
|
|
return false; |
347
|
|
|
break; |
|
|
|
|
348
|
|
|
} |
349
|
|
|
$exponentstring = substr($bitword, 1, $exponentbits - 1); |
350
|
|
|
$fractionstring = substr($bitword, $exponentbits, $fractionbits); |
351
|
|
|
$exponent = Bin2Dec($exponentstring); |
352
|
|
|
$fraction = Bin2Dec($fractionstring); |
353
|
|
|
|
354
|
|
|
if (($exponentbits == 16) && ($fractionbits == 64)) { |
355
|
|
|
// 80-bit |
356
|
|
|
// As used in Apple AIFF for sample_rate |
357
|
|
|
// A bit of a hack, but it works ;) |
358
|
|
|
return pow(2, ($exponent - 16382)) * DecimalBinary2Float($fractionstring); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
|
362
|
|
View Code Duplication |
if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { |
|
|
|
|
363
|
|
|
// Not a Number |
364
|
|
|
$floatvalue = false; |
365
|
|
|
} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) { |
366
|
|
|
if ($signbit == '1') { |
367
|
|
|
$floatvalue = '-infinity'; |
368
|
|
|
} else { |
369
|
|
|
$floatvalue = '+infinity'; |
370
|
|
|
} |
371
|
|
|
} elseif (($exponent == 0) && ($fraction == 0)) { |
372
|
|
|
if ($signbit == '1') { |
373
|
|
|
$floatvalue = -0; |
|
|
|
|
374
|
|
|
} else { |
375
|
|
|
$floatvalue = 0; |
|
|
|
|
376
|
|
|
} |
377
|
|
|
$floatvalue = ($signbit ? 0 : -0); |
378
|
|
|
} elseif (($exponent == 0) && ($fraction != 0)) { |
379
|
|
|
// These are 'unnormalized' values |
380
|
|
|
$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * DecimalBinary2Float($fractionstring); |
381
|
|
|
if ($signbit == '1') { |
382
|
|
|
$floatvalue *= -1; |
383
|
|
|
} |
384
|
|
|
} elseif ($exponent != 0) { |
385
|
|
|
$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + DecimalBinary2Float($fractionstring)); |
386
|
|
|
if ($signbit == '1') { |
387
|
|
|
$floatvalue *= -1; |
388
|
|
|
} |
389
|
|
|
} |
390
|
|
|
return (float) $floatvalue; |
|
|
|
|
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
if (!function_exists('BigEndian2Int')) { |
395
|
|
|
function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { |
396
|
|
|
$intvalue = 0; |
397
|
|
|
$bytewordlen = strlen($byteword); |
398
|
|
View Code Duplication |
for ($i = 0; $i < $bytewordlen; $i++) { |
|
|
|
|
399
|
|
|
if ($synchsafe) { // disregard MSB, effectively 7-bit bytes |
400
|
|
|
$intvalue = $intvalue | (ord($byteword[$i]) & 0x7F) << (($bytewordlen - 1 - $i) * 7); |
401
|
|
|
} else { |
402
|
|
|
$intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i)); |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
if ($signed && !$synchsafe) { |
406
|
|
|
// synchsafe ints are not allowed to be signed |
407
|
|
|
switch ($bytewordlen) { |
408
|
|
|
case 1: |
409
|
|
|
case 2: |
410
|
|
|
case 3: |
411
|
|
|
case 4: |
412
|
|
|
$signmaskbit = 0x80 << (8 * ($bytewordlen - 1)); |
413
|
|
|
if ($intvalue & $signmaskbit) { |
414
|
|
|
$intvalue = 0 - ($intvalue & ($signmaskbit - 1)); |
415
|
|
|
} |
416
|
|
|
break; |
417
|
|
|
|
418
|
|
|
default: |
419
|
|
|
die('ERROR: Cannot have signed integers larger than 32-bits in BigEndian2Int()'); |
420
|
|
|
break; |
|
|
|
|
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
return CastAsInt($intvalue); |
424
|
|
|
} |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
if (!function_exists('LittleEndian2Int')) { |
428
|
|
|
function LittleEndian2Int($byteword, $signed=false) { |
429
|
|
|
return BigEndian2Int(strrev($byteword), false, $signed); |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
if (!function_exists('BigEndian2Bin')) { |
434
|
|
View Code Duplication |
function BigEndian2Bin($byteword) { |
|
|
|
|
435
|
|
|
$binvalue = ''; |
436
|
|
|
$bytewordlen = strlen($byteword); |
437
|
|
|
for ($i = 0; $i < $bytewordlen; $i++) { |
438
|
|
|
$binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT); |
439
|
|
|
} |
440
|
|
|
return $binvalue; |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
if (!function_exists('BigEndian2String')) { |
445
|
|
|
function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { |
446
|
|
|
if ($number < 0) { |
447
|
|
|
return false; |
448
|
|
|
} |
449
|
|
|
$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); |
450
|
|
|
$intstring = ''; |
451
|
|
|
if ($signed) { |
452
|
|
|
if ($minbytes > 4) { |
453
|
|
|
die('ERROR: Cannot have signed integers larger than 32-bits in BigEndian2String()'); |
454
|
|
|
} |
455
|
|
|
$number = $number & (0x80 << (8 * ($minbytes - 1))); |
456
|
|
|
} |
457
|
|
View Code Duplication |
while ($number != 0) { |
|
|
|
|
458
|
|
|
$quotient = ($number / ($maskbyte + 1)); |
459
|
|
|
$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring; |
460
|
|
|
$number = floor($quotient); |
461
|
|
|
} |
462
|
|
|
return str_pad($intstring, $minbytes, chr(0), STR_PAD_LEFT); |
463
|
|
|
} |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
if (!function_exists('Dec2Bin')) { |
467
|
|
View Code Duplication |
function Dec2Bin($number) { |
|
|
|
|
468
|
|
|
while ($number >= 256) { |
469
|
|
|
$bytes[] = (($number / 256) - (floor($number / 256))) * 256; |
|
|
|
|
470
|
|
|
$number = floor($number / 256); |
471
|
|
|
} |
472
|
|
|
$bytes[] = $number; |
|
|
|
|
473
|
|
|
$binstring = ''; |
474
|
|
|
for ($i = 0; $i < count($bytes); $i++) { |
|
|
|
|
475
|
|
|
$binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring; |
476
|
|
|
} |
477
|
|
|
return $binstring; |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
if (!function_exists('Bin2Dec')) { |
482
|
|
|
function Bin2Dec($binstring) { |
483
|
|
|
$decvalue = 0; |
484
|
|
View Code Duplication |
for ($i = 0; $i < strlen($binstring); $i++) { |
|
|
|
|
485
|
|
|
$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); |
486
|
|
|
} |
487
|
|
|
return CastAsInt($decvalue); |
488
|
|
|
} |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
if (!function_exists('Bin2String')) { |
492
|
|
View Code Duplication |
function Bin2String($binstring) { |
|
|
|
|
493
|
|
|
// return 'hi' for input of '0110100001101001' |
494
|
|
|
$string = ''; |
495
|
|
|
$binstringreversed = strrev($binstring); |
496
|
|
|
for ($i = 0; $i < strlen($binstringreversed); $i += 8) { |
497
|
|
|
$string = chr(Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; |
498
|
|
|
} |
499
|
|
|
return $string; |
500
|
|
|
} |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
if (!function_exists('LittleEndian2String')) { |
504
|
|
View Code Duplication |
function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { |
|
|
|
|
505
|
|
|
$intstring = ''; |
506
|
|
|
while ($number > 0) { |
507
|
|
|
if ($synchsafe) { |
508
|
|
|
$intstring = $intstring.chr($number & 127); |
509
|
|
|
$number >>= 7; |
510
|
|
|
} else { |
511
|
|
|
$intstring = $intstring.chr($number & 255); |
512
|
|
|
$number >>= 8; |
513
|
|
|
} |
514
|
|
|
} |
515
|
|
|
return str_pad($intstring, $minbytes, chr(0), STR_PAD_RIGHT); |
516
|
|
|
} |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
if (!function_exists('Bool2IntString')) { |
520
|
|
|
function Bool2IntString($intvalue) { |
521
|
|
|
return ($intvalue ? '1' : '0'); |
522
|
|
|
} |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
if (!function_exists('IntString2Bool')) { |
526
|
|
View Code Duplication |
function IntString2Bool($char) { |
|
|
|
|
527
|
|
|
if ($char == '1') { |
528
|
|
|
return true; |
529
|
|
|
} elseif ($char == '0') { |
530
|
|
|
return false; |
531
|
|
|
} |
532
|
|
|
return null; |
533
|
|
|
} |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
if (!function_exists('InverseBoolean')) { |
537
|
|
|
function InverseBoolean($value) { |
538
|
|
|
return ($value ? false : true); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
if (!function_exists('DeUnSynchronise')) { |
543
|
|
|
function DeUnSynchronise($data) { |
544
|
|
|
return str_replace(chr(0xFF).chr(0x00), chr(0xFF), $data); |
545
|
|
|
} |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
if (!function_exists('Unsynchronise')) { |
549
|
|
|
function Unsynchronise($data) { |
550
|
|
|
// Whenever a false synchronisation is found within the tag, one zeroed |
551
|
|
|
// byte is inserted after the first false synchronisation byte. The |
552
|
|
|
// format of a correct sync that should be altered by ID3 encoders is as |
553
|
|
|
// follows: |
554
|
|
|
// %11111111 111xxxxx |
555
|
|
|
// And should be replaced with: |
556
|
|
|
// %11111111 00000000 111xxxxx |
557
|
|
|
// This has the side effect that all $FF 00 combinations have to be |
558
|
|
|
// altered, so they won't be affected by the decoding process. Therefore |
559
|
|
|
// all the $FF 00 combinations have to be replaced with the $FF 00 00 |
560
|
|
|
// combination during the unsynchronisation. |
561
|
|
|
|
562
|
|
|
$data = str_replace(chr(0xFF).chr(0x00), chr(0xFF).chr(0x00).chr(0x00), $data); |
563
|
|
|
$unsyncheddata = ''; |
564
|
|
|
for ($i = 0; $i < strlen($data); $i++) { |
565
|
|
|
$thischar = $data[$i]; |
566
|
|
|
$unsyncheddata .= $thischar; |
567
|
|
|
if ($thischar == chr(255)) { |
568
|
|
|
$nextchar = ord(substr($data, $i + 1, 1)); |
569
|
|
|
if (($nextchar | 0xE0) == 0xE0) { |
570
|
|
|
// previous byte = 11111111, this byte = 111????? |
571
|
|
|
$unsyncheddata .= chr(0); |
572
|
|
|
} |
573
|
|
|
} |
574
|
|
|
} |
575
|
|
|
return $unsyncheddata; |
576
|
|
|
} |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
if (!function_exists('is_hash')) { |
580
|
|
View Code Duplication |
function is_hash($var) { |
|
|
|
|
581
|
|
|
// written by [email protected] |
582
|
|
|
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php |
583
|
|
|
if (is_array($var)) { |
584
|
|
|
$keys = array_keys($var); |
585
|
|
|
$all_num = true; |
|
|
|
|
586
|
|
|
for ($i = 0; $i < count($keys); $i++) { |
|
|
|
|
587
|
|
|
if (is_string($keys[$i])) { |
588
|
|
|
return true; |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
} |
592
|
|
|
return false; |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
if (!function_exists('array_join_merge')) { |
597
|
|
|
function array_join_merge($arr1, $arr2) { |
598
|
|
|
// written by [email protected] |
599
|
|
|
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php |
600
|
|
|
if (is_array($arr1) && is_array($arr2)) { |
601
|
|
|
// the same -> merge |
602
|
|
|
$new_array = array(); |
603
|
|
|
|
604
|
|
|
if (is_hash($arr1) && is_hash($arr2)) { |
605
|
|
|
// hashes -> merge based on keys |
606
|
|
|
$keys = array_merge(array_keys($arr1), array_keys($arr2)); |
607
|
|
|
foreach ($keys as $key) { |
608
|
|
|
$arr1[$key] = (isset($arr1[$key]) ? $arr1[$key] : ''); |
609
|
|
|
$arr2[$key] = (isset($arr2[$key]) ? $arr2[$key] : ''); |
610
|
|
|
$new_array[$key] = array_join_merge($arr1[$key], $arr2[$key]); |
611
|
|
|
} |
612
|
|
|
} else { |
613
|
|
|
// two real arrays -> merge |
614
|
|
|
$new_array = array_reverse(array_unique(array_reverse(array_merge($arr1,$arr2)))); |
615
|
|
|
} |
616
|
|
|
return $new_array; |
617
|
|
|
} else { |
618
|
|
|
// not the same ... take new one if defined, else the old one stays |
619
|
|
|
return $arr2 ? $arr2 : $arr1; |
620
|
|
|
} |
621
|
|
|
} |
622
|
|
|
} |
623
|
|
|
|
624
|
|
View Code Duplication |
if (!function_exists('array_merge_clobber')) { |
|
|
|
|
625
|
|
|
function array_merge_clobber($array1, $array2) { |
626
|
|
|
// written by [email protected] |
627
|
|
|
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php |
628
|
|
|
if (!is_array($array1) || !is_array($array2)) { |
629
|
|
|
return false; |
630
|
|
|
} |
631
|
|
|
$newarray = $array1; |
632
|
|
|
foreach ($array2 as $key => $val) { |
633
|
|
|
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { |
634
|
|
|
$newarray[$key] = array_merge_clobber($newarray[$key], $val); |
635
|
|
|
} else { |
636
|
|
|
$newarray[$key] = $val; |
637
|
|
|
} |
638
|
|
|
} |
639
|
|
|
return $newarray; |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
|
643
|
|
View Code Duplication |
if (!function_exists('array_merge_noclobber')) { |
|
|
|
|
644
|
|
|
function array_merge_noclobber($array1, $array2) { |
645
|
|
|
if (!is_array($array1) || !is_array($array2)) { |
646
|
|
|
return false; |
647
|
|
|
} |
648
|
|
|
$newarray = $array1; |
649
|
|
|
foreach ($array2 as $key => $val) { |
650
|
|
|
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { |
651
|
|
|
$newarray[$key] = array_merge_noclobber($newarray[$key], $val); |
652
|
|
|
} elseif (!isset($newarray[$key])) { |
653
|
|
|
$newarray[$key] = $val; |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
return $newarray; |
657
|
|
|
} |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
if (!function_exists('RoughTranslateUnicodeToASCII')) { |
661
|
|
|
function RoughTranslateUnicodeToASCII($rawdata, $frame_textencoding) { |
662
|
|
|
// rough translation of data for application that can't handle Unicode data |
663
|
|
|
|
664
|
|
|
$tempstring = ''; |
665
|
|
|
switch ($frame_textencoding) { |
666
|
|
|
case 0: // ISO-8859-1. Terminated with $00. |
667
|
|
|
$asciidata = $rawdata; |
668
|
|
|
break; |
669
|
|
|
|
670
|
|
|
case 1: // UTF-16 encoded Unicode with BOM. Terminated with $00 00. |
671
|
|
|
$asciidata = $rawdata; |
672
|
|
|
if (substr($asciidata, 0, 2) == chr(0xFF).chr(0xFE)) { |
673
|
|
|
// remove BOM, only if present (it should be, but...) |
674
|
|
|
$asciidata = substr($asciidata, 2); |
675
|
|
|
} |
676
|
|
|
if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) { |
677
|
|
|
$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...) |
678
|
|
|
} |
679
|
|
View Code Duplication |
for ($i = 0; $i < strlen($asciidata); $i += 2) { |
|
|
|
|
680
|
|
|
if ((ord($asciidata[$i]) <= 0x7F) || (ord($asciidata[$i]) >= 0xA0)) { |
681
|
|
|
$tempstring .= $asciidata[$i]; |
682
|
|
|
} else { |
683
|
|
|
$tempstring .= '?'; |
684
|
|
|
} |
685
|
|
|
} |
686
|
|
|
$asciidata = $tempstring; |
687
|
|
|
break; |
688
|
|
|
|
689
|
|
|
case 2: // UTF-16BE encoded Unicode without BOM. Terminated with $00 00. |
690
|
|
|
$asciidata = $rawdata; |
691
|
|
|
if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) { |
692
|
|
|
$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...) |
693
|
|
|
} |
694
|
|
View Code Duplication |
for ($i = 0; $i < strlen($asciidata); $i += 2) { |
|
|
|
|
695
|
|
|
if ((ord($asciidata[$i]) <= 0x7F) || (ord($asciidata[$i]) >= 0xA0)) { |
696
|
|
|
$tempstring .= $asciidata[$i]; |
697
|
|
|
} else { |
698
|
|
|
$tempstring .= '?'; |
699
|
|
|
} |
700
|
|
|
} |
701
|
|
|
$asciidata = $tempstring; |
702
|
|
|
break; |
703
|
|
|
|
704
|
|
|
case 3: // UTF-8 encoded Unicode. Terminated with $00. |
705
|
|
|
$asciidata = utf8_decode($rawdata); |
706
|
|
|
break; |
707
|
|
|
|
708
|
|
|
case 255: // Unicode, Big-Endian. Terminated with $00 00. |
709
|
|
|
$asciidata = $rawdata; |
710
|
|
|
if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) { |
711
|
|
|
$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...) |
712
|
|
|
} |
713
|
|
|
for ($i = 0; ($i + 1) < strlen($asciidata); $i += 2) { |
714
|
|
|
if ((ord($asciidata[($i + 1)]) <= 0x7F) || (ord($asciidata[($i + 1)]) >= 0xA0)) { |
715
|
|
|
$tempstring .= $asciidata[($i + 1)]; |
716
|
|
|
} else { |
717
|
|
|
$tempstring .= '?'; |
718
|
|
|
} |
719
|
|
|
} |
720
|
|
|
$asciidata = $tempstring; |
721
|
|
|
break; |
722
|
|
|
|
723
|
|
|
|
724
|
|
|
default: |
725
|
|
|
// shouldn't happen, but in case $frame_textencoding is not 1 <= $frame_textencoding <= 4 |
726
|
|
|
// just pass the data through unchanged. |
727
|
|
|
$asciidata = $rawdata; |
728
|
|
|
break; |
729
|
|
|
} |
730
|
|
|
if (substr($asciidata, strlen($asciidata) - 1, 1) == chr(0)) { |
731
|
|
|
// remove null terminator, if present |
732
|
|
|
$asciidata = NoNullString($asciidata); |
733
|
|
|
} |
734
|
|
|
return $asciidata; |
735
|
|
|
// return str_replace(chr(0), '', $asciidata); // just in case any nulls slipped through |
736
|
|
|
} |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
if (!function_exists('PlaytimeString')) { |
740
|
|
|
function PlaytimeString($playtimeseconds) { |
741
|
|
|
$contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60); |
742
|
|
|
$contentminutes = floor($playtimeseconds / 60); |
743
|
|
|
if ($contentseconds >= 60) { |
744
|
|
|
$contentseconds -= 60; |
745
|
|
|
$contentminutes++; |
746
|
|
|
} |
747
|
|
|
return number_format($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT); |
748
|
|
|
} |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
if (!function_exists('CloseMatch')) { |
752
|
|
|
function CloseMatch($value1, $value2, $tolerance) { |
753
|
|
|
return (abs($value1 - $value2) <= $tolerance); |
754
|
|
|
} |
755
|
|
|
} |
756
|
|
|
|
757
|
|
|
if (!function_exists('ID3v1matchesID3v2')) { |
758
|
|
|
function ID3v1matchesID3v2($id3v1, $id3v2) { |
759
|
|
|
|
760
|
|
|
$requiredindices = array('title', 'artist', 'album', 'year', 'genre', 'comment'); |
761
|
|
|
foreach ($requiredindices as $requiredindex) { |
762
|
|
|
if (!isset($id3v1["$requiredindex"])) { |
763
|
|
|
$id3v1["$requiredindex"] = ''; |
764
|
|
|
} |
765
|
|
|
if (!isset($id3v2["$requiredindex"])) { |
766
|
|
|
$id3v2["$requiredindex"] = ''; |
767
|
|
|
} |
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
if (trim($id3v1['title']) != trim(substr($id3v2['title'], 0, 30))) { |
771
|
|
|
return false; |
772
|
|
|
} |
773
|
|
|
if (trim($id3v1['artist']) != trim(substr($id3v2['artist'], 0, 30))) { |
774
|
|
|
return false; |
775
|
|
|
} |
776
|
|
|
if (trim($id3v1['album']) != trim(substr($id3v2['album'], 0, 30))) { |
777
|
|
|
return false; |
778
|
|
|
} |
779
|
|
View Code Duplication |
if (trim($id3v1['year']) != trim(substr($id3v2['year'], 0, 4))) { |
|
|
|
|
780
|
|
|
return false; |
781
|
|
|
} |
782
|
|
|
if (trim($id3v1['genre']) != trim($id3v2['genre'])) { |
783
|
|
|
return false; |
784
|
|
|
} |
785
|
|
|
if (isset($id3v1['track_number'])) { |
786
|
|
|
if (!isset($id3v1['track_number']) || (trim($id3v1['track_number']) != trim($id3v2['track_number']))) { |
787
|
|
|
return false; |
788
|
|
|
} |
789
|
|
View Code Duplication |
if (trim($id3v1['comment']) != trim(substr($id3v2['comment'], 0, 28))) { |
|
|
|
|
790
|
|
|
return false; |
791
|
|
|
} |
792
|
|
View Code Duplication |
} else { |
|
|
|
|
793
|
|
|
if (trim($id3v1['comment']) != trim(substr($id3v2['comment'], 0, 30))) { |
794
|
|
|
return false; |
795
|
|
|
} |
796
|
|
|
} |
797
|
|
|
return true; |
798
|
|
|
} |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
if (!function_exists('FILETIMEtoUNIXtime')) { |
802
|
|
|
function FILETIMEtoUNIXtime($FILETIME, $round=true) { |
803
|
|
|
// FILETIME is a 64-bit unsigned integer representing |
804
|
|
|
// the number of 100-nanosecond intervals since January 1, 1601 |
805
|
|
|
// UNIX timestamp is number of seconds since January 1, 1970 |
806
|
|
|
// 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days |
807
|
|
|
if ($round) { |
808
|
|
|
return round(($FILETIME - 116444736000000000) / 10000000); |
809
|
|
|
} |
810
|
|
|
return ($FILETIME - 116444736000000000) / 10000000; |
811
|
|
|
} |
812
|
|
|
} |
813
|
|
|
|
814
|
|
|
if (!function_exists('GUIDtoBytestring')) { |
815
|
|
View Code Duplication |
function GUIDtoBytestring($GUIDstring) { |
|
|
|
|
816
|
|
|
// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: |
817
|
|
|
// first 4 bytes are in little-endian order |
818
|
|
|
// next 2 bytes are appended in little-endian order |
819
|
|
|
// next 2 bytes are appended in little-endian order |
820
|
|
|
// next 2 bytes are appended in big-endian order |
821
|
|
|
// next 6 bytes are appended in big-endian order |
822
|
|
|
|
823
|
|
|
// AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string: |
824
|
|
|
// $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp |
825
|
|
|
|
826
|
|
|
$hexbytecharstring = chr(hexdec(substr($GUIDstring, 6, 2))); |
827
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 4, 2))); |
828
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 2, 2))); |
829
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 0, 2))); |
830
|
|
|
|
831
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2))); |
832
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 9, 2))); |
833
|
|
|
|
834
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2))); |
835
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2))); |
836
|
|
|
|
837
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2))); |
838
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2))); |
839
|
|
|
|
840
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2))); |
841
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2))); |
842
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2))); |
843
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2))); |
844
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2))); |
845
|
|
|
$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2))); |
846
|
|
|
|
847
|
|
|
return $hexbytecharstring; |
848
|
|
|
} |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
if (!function_exists('BytestringToGUID')) { |
852
|
|
View Code Duplication |
function BytestringToGUID($Bytestring) { |
|
|
|
|
853
|
|
|
$GUIDstring = str_pad(dechex(ord($Bytestring[3])), 2, '0', STR_PAD_LEFT); |
854
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[2])), 2, '0', STR_PAD_LEFT); |
855
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[1])), 2, '0', STR_PAD_LEFT); |
856
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[0])), 2, '0', STR_PAD_LEFT); |
857
|
|
|
$GUIDstring .= '-'; |
858
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[5])), 2, '0', STR_PAD_LEFT); |
859
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[4])), 2, '0', STR_PAD_LEFT); |
860
|
|
|
$GUIDstring .= '-'; |
861
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[7])), 2, '0', STR_PAD_LEFT); |
862
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[6])), 2, '0', STR_PAD_LEFT); |
863
|
|
|
$GUIDstring .= '-'; |
864
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[8])), 2, '0', STR_PAD_LEFT); |
865
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[9])), 2, '0', STR_PAD_LEFT); |
866
|
|
|
$GUIDstring .= '-'; |
867
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[10])), 2, '0', STR_PAD_LEFT); |
868
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[11])), 2, '0', STR_PAD_LEFT); |
869
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[12])), 2, '0', STR_PAD_LEFT); |
870
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[13])), 2, '0', STR_PAD_LEFT); |
871
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[14])), 2, '0', STR_PAD_LEFT); |
872
|
|
|
$GUIDstring .= str_pad(dechex(ord($Bytestring[15])), 2, '0', STR_PAD_LEFT); |
873
|
|
|
|
874
|
|
|
return strtoupper($GUIDstring); |
875
|
|
|
} |
876
|
|
|
} |
877
|
|
|
|
878
|
|
|
if (!function_exists('BitrateColor')) { |
879
|
|
View Code Duplication |
function BitrateColor($bitrate) { |
|
|
|
|
880
|
|
|
$bitrate /= 3; // scale from 1-768kbps to 1-256kbps |
881
|
|
|
$bitrate--; // scale from 1-256kbps to 0-255kbps |
882
|
|
|
$bitrate = max($bitrate, 0); |
883
|
|
|
$bitrate = min($bitrate, 255); |
884
|
|
|
//$bitrate = max($bitrate, 32); |
885
|
|
|
//$bitrate = min($bitrate, 143); |
886
|
|
|
//$bitrate = ($bitrate * 2) - 32; |
887
|
|
|
|
888
|
|
|
$Rcomponent = max(255 - ($bitrate * 2), 0); |
889
|
|
|
$Gcomponent = max(($bitrate * 2) - 255, 0); |
890
|
|
|
if ($bitrate > 127) { |
891
|
|
|
$Bcomponent = max((255 - $bitrate) * 2, 0); |
892
|
|
|
} else { |
893
|
|
|
$Bcomponent = max($bitrate * 2, 0); |
894
|
|
|
} |
895
|
|
|
return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT); |
896
|
|
|
} |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
if (!function_exists('BitrateText')) { |
900
|
|
|
function BitrateText($bitrate) { |
|
|
|
|
901
|
|
|
return '<SPAN STYLE="color: #'.BitrateColor($bitrate).'">'.round($bitrate).' kbps</SPAN>'; |
902
|
|
|
} |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
if (!function_exists('image_type_to_mime_type')) { |
906
|
|
|
function image_type_to_mime_type($imagetypeid) { |
907
|
|
|
// only available in PHP v4.3.0+ |
908
|
|
|
static $image_type_to_mime_type = array(); |
909
|
|
|
if (empty($image_type_to_mime_type)) { |
910
|
|
|
$image_type_to_mime_type[1] = 'image/gif'; // GIF |
911
|
|
|
$image_type_to_mime_type[2] = 'image/jpeg'; // JPEG |
912
|
|
|
$image_type_to_mime_type[3] = 'image/png'; // PNG |
913
|
|
|
$image_type_to_mime_type[4] = 'application/x-shockwave-flash'; // Flash |
914
|
|
|
$image_type_to_mime_type[5] = 'image/psd'; // PSD |
915
|
|
|
$image_type_to_mime_type[6] = 'image/bmp'; // BMP |
916
|
|
|
$image_type_to_mime_type[7] = 'image/tiff'; // TIFF: little-endian (Intel) |
917
|
|
|
$image_type_to_mime_type[8] = 'image/tiff'; // TIFF: big-endian (Motorola) |
918
|
|
|
//$image_type_to_mime_type[9] = 'image/jpc'; // JPC |
919
|
|
|
//$image_type_to_mime_type[10] = 'image/jp2'; // JPC |
920
|
|
|
//$image_type_to_mime_type[11] = 'image/jpx'; // JPC |
921
|
|
|
//$image_type_to_mime_type[12] = 'image/jb2'; // JPC |
922
|
|
|
$image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave |
923
|
|
|
$image_type_to_mime_type[14] = 'image/iff'; // IFF |
924
|
|
|
} |
925
|
|
|
return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream'); |
926
|
|
|
} |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
if (!function_exists('utf8_decode')) { |
930
|
|
|
// PHP has this function built-in if it's configured with the --with-xml option |
931
|
|
|
// This version of the function is only provided in case XML isn't installed |
932
|
|
|
function utf8_decode($utf8text) { |
933
|
|
|
// http://www.php.net/manual/en/function.utf8-encode.php |
934
|
|
|
// bytes bits representation |
935
|
|
|
// 1 7 0bbbbbbb |
936
|
|
|
// 2 11 110bbbbb 10bbbbbb |
937
|
|
|
// 3 16 1110bbbb 10bbbbbb 10bbbbbb |
938
|
|
|
// 4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb |
939
|
|
|
|
940
|
|
|
$utf8length = strlen($utf8text); |
941
|
|
|
$decodedtext = ''; |
942
|
|
|
for ($i = 0; $i < $utf8length; $i++) { |
943
|
|
|
if ((ord($utf8text[$i]) & 0x80) == 0) { |
944
|
|
|
$decodedtext .= $utf8text[$i]; |
945
|
|
|
} elseif ((ord($utf8text[$i]) & 0xF0) == 0xF0) { |
946
|
|
|
$decodedtext .= '?'; |
947
|
|
|
$i += 3; |
948
|
|
|
} elseif ((ord($utf8text[$i]) & 0xE0) == 0xE0) { |
949
|
|
|
$decodedtext .= '?'; |
950
|
|
|
$i += 2; |
951
|
|
|
} elseif ((ord($utf8text[$i]) & 0xC0) == 0xC0) { |
952
|
|
|
// 2 11 110bbbbb 10bbbbbb |
953
|
|
|
$decodedchar = Bin2Dec(substr(Dec2Bin(ord($utf8text[$i])), 3, 5).substr(Dec2Bin(ord($utf8text[($i + 1)])), 2, 6)); |
954
|
|
|
if ($decodedchar <= 255) { |
955
|
|
|
$decodedtext .= chr($decodedchar); |
956
|
|
|
} else { |
957
|
|
|
$decodedtext .= '?'; |
958
|
|
|
} |
959
|
|
|
$i += 1; |
960
|
|
|
} |
961
|
|
|
} |
962
|
|
|
return $decodedtext; |
963
|
|
|
} |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
if (!function_exists('DateMac2Unix')) { |
967
|
|
|
function DateMac2Unix($macdate) { |
968
|
|
|
// Macintosh timestamp: seconds since 00:00h January 1, 1904 |
969
|
|
|
// UNIX timestamp: seconds since 00:00h January 1, 1970 |
970
|
|
|
return CastAsInt($macdate - 2082844800); |
971
|
|
|
} |
972
|
|
|
} |
973
|
|
|
|
974
|
|
|
|
975
|
|
View Code Duplication |
if (!function_exists('FixedPoint8_8')) { |
|
|
|
|
976
|
|
|
function FixedPoint8_8($rawdata) { |
977
|
|
|
return BigEndian2Int(substr($rawdata, 0, 1)) + (float) (BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); |
978
|
|
|
} |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
|
982
|
|
View Code Duplication |
if (!function_exists('FixedPoint16_16')) { |
|
|
|
|
983
|
|
|
function FixedPoint16_16($rawdata) { |
984
|
|
|
return BigEndian2Int(substr($rawdata, 0, 2)) + (float) (BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); |
985
|
|
|
} |
986
|
|
|
} |
987
|
|
|
|
988
|
|
|
|
989
|
|
|
if (!function_exists('FixedPoint2_30')) { |
990
|
|
|
function FixedPoint2_30($rawdata) { |
991
|
|
|
$binarystring = BigEndian2Bin($rawdata); |
992
|
|
|
return Bin2Dec(substr($binarystring, 0, 2)) + (float) (Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); |
993
|
|
|
} |
994
|
|
|
} |
995
|
|
|
|
996
|
|
|
|
997
|
|
|
if (!function_exists('Pascal2String')) { |
998
|
|
|
function Pascal2String($pascalstring) { |
999
|
|
|
// Pascal strings have 1 byte at the beginning saying how many chars are in the string |
1000
|
|
|
return substr($pascalstring, 1); |
1001
|
|
|
} |
1002
|
|
|
} |
1003
|
|
|
|
1004
|
|
|
if (!function_exists('NoNullString')) { |
1005
|
|
View Code Duplication |
function NoNullString($nullterminatedstring) { |
|
|
|
|
1006
|
|
|
// remove the single null terminator on null terminated strings |
1007
|
|
|
if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === chr(0)) { |
1008
|
|
|
return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1); |
1009
|
|
|
} |
1010
|
|
|
return $nullterminatedstring; |
1011
|
|
|
} |
1012
|
|
|
} |
1013
|
|
|
|
1014
|
|
|
if (!function_exists('FileSizeNiceDisplay')) { |
1015
|
|
|
function FileSizeNiceDisplay($filesize, $precision=2) { |
1016
|
|
|
if ($filesize < 1000) { |
1017
|
|
|
$sizeunit = 'bytes'; |
1018
|
|
|
$precision = 0; |
1019
|
|
|
} else { |
1020
|
|
|
$filesize /= 1024; |
1021
|
|
|
$sizeunit = 'kB'; |
1022
|
|
|
} |
1023
|
|
|
if ($filesize >= 1000) { |
1024
|
|
|
$filesize /= 1024; |
1025
|
|
|
$sizeunit = 'MB'; |
1026
|
|
|
} |
1027
|
|
|
if ($filesize >= 1000) { |
1028
|
|
|
$filesize /= 1024; |
1029
|
|
|
$sizeunit = 'GB'; |
1030
|
|
|
} |
1031
|
|
|
return number_format($filesize, $precision).' '.$sizeunit; |
1032
|
|
|
} |
1033
|
|
|
} |
1034
|
|
|
|
1035
|
|
|
if (!function_exists('DOStime2UNIXtime')) { |
1036
|
|
View Code Duplication |
function DOStime2UNIXtime($DOSdate, $DOStime) { |
|
|
|
|
1037
|
|
|
// wFatDate |
1038
|
|
|
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: |
1039
|
|
|
// Bits Contents |
1040
|
|
|
// 0-4 Day of the month (1-31) |
1041
|
|
|
// 5-8 Month (1 = January, 2 = February, and so on) |
1042
|
|
|
// 9-15 Year offset from 1980 (add 1980 to get actual year) |
1043
|
|
|
|
1044
|
|
|
$UNIXday = ($DOSdate & 0x001F); |
1045
|
|
|
$UNIXmonth = (($DOSdate & 0x01E0) >> 5); |
1046
|
|
|
$UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980; |
1047
|
|
|
|
1048
|
|
|
// wFatTime |
1049
|
|
|
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format: |
1050
|
|
|
// Bits Contents |
1051
|
|
|
// 0-4 Second divided by 2 |
1052
|
|
|
// 5-10 Minute (0-59) |
1053
|
|
|
// 11-15 Hour (0-23 on a 24-hour clock) |
1054
|
|
|
|
1055
|
|
|
$UNIXsecond = ($DOStime & 0x001F) * 2; |
1056
|
|
|
$UNIXminute = (($DOStime & 0x07E0) >> 5); |
1057
|
|
|
$UNIXhour = (($DOStime & 0xF800) >> 11); |
1058
|
|
|
|
1059
|
|
|
return mktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); |
1060
|
|
|
} |
1061
|
|
|
} |
1062
|
|
|
|
1063
|
|
|
if (!function_exists('CreateDeepArray')) { |
1064
|
|
|
function CreateDeepArray($ArrayPath, $Separator, $Value) { |
1065
|
|
|
// assigns $Value to a nested array path: |
1066
|
|
|
// $foo = CreateDeepArray('/path/to/my', '/', 'file.txt') |
1067
|
|
|
// is the same as: |
1068
|
|
|
// $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); |
1069
|
|
|
// or |
1070
|
|
|
// $foo['path']['to']['my'] = 'file.txt'; |
1071
|
|
|
while ($ArrayPath[0] == $Separator) { |
1072
|
|
|
$ArrayPath = substr($ArrayPath, 1); |
1073
|
|
|
} |
1074
|
|
View Code Duplication |
if (($pos = strpos($ArrayPath, $Separator)) !== false) { |
|
|
|
|
1075
|
|
|
$ReturnedArray[substr($ArrayPath, 0, $pos)] = CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); |
|
|
|
|
1076
|
|
|
} else { |
1077
|
|
|
$ReturnedArray["$ArrayPath"] = $Value; |
|
|
|
|
1078
|
|
|
} |
1079
|
|
|
return $ReturnedArray; |
1080
|
|
|
} |
1081
|
|
|
} |
1082
|
|
|
|
1083
|
|
|
if (!function_exists('md5_data')) { |
1084
|
|
|
// Allan Hansen <[email protected]> |
1085
|
|
|
// md5_data() - returns md5sum for a file from startuing position to absolute end position |
1086
|
|
|
|
1087
|
|
|
function md5_data($file, $offset, $end, $invertsign=false) { |
1088
|
|
|
// first try and create a temporary file in the same directory as the file being scanned |
1089
|
|
|
if (($dataMD5filename = tempnam(dirname($file), preg_replace('#[^[:alnum:]]#i', '', basename($file)))) === false) { |
1090
|
|
|
// if that fails, create a temporary file in the system temp directory |
1091
|
|
|
if (($dataMD5filename = tempnam('/tmp', 'getID3')) === false) { |
1092
|
|
|
// if that fails, create a temporary file in the current directory |
1093
|
|
|
if (($dataMD5filename = tempnam('.', preg_replace('#[^[:alnum:]]#i', '', basename($file)))) === false) { |
1094
|
|
|
// can't find anywhere to create a temp file, just die |
1095
|
|
|
return false; |
1096
|
|
|
} |
1097
|
|
|
} |
1098
|
|
|
} |
1099
|
|
|
$md5 = false; |
1100
|
|
|
set_time_limit(max(filesize($file) / 1000000, 30)); |
1101
|
|
|
|
1102
|
|
|
// copy parts of file |
1103
|
|
|
ob_start(); |
1104
|
|
|
if ($fp = fopen($file, 'rb')) { |
1105
|
|
|
ob_end_clean(); |
1106
|
|
|
|
1107
|
|
|
ob_start(); |
1108
|
|
|
if ($MD5fp = fopen($dataMD5filename, 'wb')) { |
1109
|
|
|
|
1110
|
|
|
ob_end_clean(); |
1111
|
|
|
if ($invertsign) { |
1112
|
|
|
// Load conversion lookup strings for 8-bit unsigned->signed conversion below |
1113
|
|
|
$from = ''; |
1114
|
|
|
$to = ''; |
1115
|
|
View Code Duplication |
for ($i = 0; $i < 128; $i++) { |
|
|
|
|
1116
|
|
|
$from .= chr($i); |
1117
|
|
|
$to .= chr($i + 128); |
1118
|
|
|
} |
1119
|
|
View Code Duplication |
for ($i = 128; $i < 256; $i++) { |
|
|
|
|
1120
|
|
|
$from .= chr($i); |
1121
|
|
|
$to .= chr($i - 128); |
1122
|
|
|
} |
1123
|
|
|
} |
1124
|
|
|
|
1125
|
|
|
fseek($fp, $offset, SEEK_SET); |
1126
|
|
|
$byteslefttowrite = $end - $offset; |
1127
|
|
|
while (($byteslefttowrite > 0) && ($buffer = fread($fp, 32768))) { |
1128
|
|
|
if ($invertsign) { |
1129
|
|
|
// Possibly FLAC-specific (?) |
1130
|
|
|
// FLAC calculates the MD5sum of the source data of 8-bit files |
1131
|
|
|
// not on the actual byte values in the source file, but of those |
1132
|
|
|
// values converted from unsigned to signed, or more specifcally, |
1133
|
|
|
// with the MSB inverted. ex: 01 -> 81; F5 -> 75; etc |
1134
|
|
|
|
1135
|
|
|
// Therefore, 8-bit WAV data has to be converted before getting the |
1136
|
|
|
// md5_data value so as to match the FLAC value |
1137
|
|
|
|
1138
|
|
|
// Flip the MSB for each byte in the buffer before copying |
1139
|
|
|
$buffer = strtr($buffer, $from, $to); |
|
|
|
|
1140
|
|
|
} |
1141
|
|
|
$byteswritten = fwrite($MD5fp, $buffer, $byteslefttowrite); |
1142
|
|
|
$byteslefttowrite -= $byteswritten; |
1143
|
|
|
} |
1144
|
|
|
fclose($MD5fp); |
1145
|
|
|
$md5 = md5_file($dataMD5filename); |
1146
|
|
|
|
1147
|
|
|
} else { |
1148
|
|
|
$errormessage = ob_get_contents(); |
|
|
|
|
1149
|
|
|
ob_end_clean(); |
1150
|
|
|
} |
1151
|
|
|
fclose($fp); |
1152
|
|
|
|
1153
|
|
|
} else { |
1154
|
|
|
$errormessage = ob_get_contents(); |
|
|
|
|
1155
|
|
|
ob_end_clean(); |
1156
|
|
|
} |
1157
|
|
|
unlink($dataMD5filename); |
1158
|
|
|
return $md5; |
1159
|
|
|
} |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
if (!function_exists('TwosCompliment2Decimal')) { |
1163
|
|
|
function TwosCompliment2Decimal($BinaryValue) { |
1164
|
|
|
// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html |
1165
|
|
|
// First check if the number is negative or positive by looking at the sign bit. |
1166
|
|
|
// If it is positive, simply convert it to decimal. |
1167
|
|
|
// If it is negative, make it positive by inverting the bits and adding one. |
1168
|
|
|
// Then, convert the result to decimal. |
1169
|
|
|
// The negative of this number is the value of the original binary. |
1170
|
|
|
|
1171
|
|
View Code Duplication |
if ($BinaryValue & 0x80) { |
|
|
|
|
1172
|
|
|
|
1173
|
|
|
// negative number |
1174
|
|
|
return (0 - ((~$BinaryValue & 0xFF) + 1)); |
1175
|
|
|
|
1176
|
|
|
} else { |
1177
|
|
|
|
1178
|
|
|
// positive number |
1179
|
|
|
return $BinaryValue; |
1180
|
|
|
|
1181
|
|
|
} |
1182
|
|
|
|
1183
|
|
|
} |
1184
|
|
|
} |
1185
|
|
|
|
1186
|
|
|
if (!function_exists('LastArrayElement')) { |
1187
|
|
|
function LastArrayElement($MyArray) { |
1188
|
|
|
if (!is_array($MyArray)) { |
1189
|
|
|
return false; |
1190
|
|
|
} |
1191
|
|
|
if (empty($MyArray)) { |
1192
|
|
|
return null; |
1193
|
|
|
} |
1194
|
|
|
foreach ($MyArray as $key => $value) { |
1195
|
|
|
} |
1196
|
|
|
return $value; |
|
|
|
|
1197
|
|
|
} |
1198
|
|
|
} |
1199
|
|
|
|
1200
|
|
|
if (!function_exists('safe_inc')) { |
1201
|
|
|
function safe_inc(&$variable, $increment=1) { |
1202
|
|
|
if (isset($variable)) { |
1203
|
|
|
$variable += $increment; |
1204
|
|
|
} else { |
1205
|
|
|
$variable = $increment; |
1206
|
|
|
} |
1207
|
|
|
return true; |
1208
|
|
|
} |
1209
|
|
|
} |
1210
|
|
|
|
1211
|
|
|
if (!function_exists('CalculateCompressionRatioVideo')) { |
1212
|
|
|
function CalculateCompressionRatioVideo(&$ThisFileInfo) { |
1213
|
|
|
if (empty($ThisFileInfo['video'])) { |
1214
|
|
|
return false; |
1215
|
|
|
} |
1216
|
|
|
if (empty($ThisFileInfo['video']['resolution_x']) || empty($ThisFileInfo['video']['resolution_y'])) { |
1217
|
|
|
return false; |
1218
|
|
|
} |
1219
|
|
|
if (empty($ThisFileInfo['video']['bits_per_sample'])) { |
1220
|
|
|
return false; |
1221
|
|
|
} |
1222
|
|
|
|
1223
|
|
|
switch ($ThisFileInfo['video']['dataformat']) { |
1224
|
|
|
case 'bmp': |
1225
|
|
|
case 'gif': |
1226
|
|
|
case 'jpeg': |
1227
|
|
|
case 'jpg': |
1228
|
|
|
case 'png': |
1229
|
|
View Code Duplication |
case 'tiff': |
|
|
|
|
1230
|
|
|
$FrameRate = 1; |
1231
|
|
|
$PlaytimeSeconds = 1; |
|
|
|
|
1232
|
|
|
$BitrateCompressed = $ThisFileInfo['filesize'] * 8; |
1233
|
|
|
break; |
1234
|
|
|
|
1235
|
|
|
default: |
1236
|
|
|
if (!empty($ThisFileInfo['video']['frame_rate'])) { |
1237
|
|
|
$FrameRate = $ThisFileInfo['video']['frame_rate']; |
1238
|
|
|
} else { |
1239
|
|
|
return false; |
1240
|
|
|
} |
1241
|
|
|
if (!empty($ThisFileInfo['playtime_seconds'])) { |
1242
|
|
|
$PlaytimeSeconds = $ThisFileInfo['playtime_seconds']; |
|
|
|
|
1243
|
|
|
} else { |
1244
|
|
|
return false; |
1245
|
|
|
} |
1246
|
|
|
if (!empty($ThisFileInfo['video']['bitrate'])) { |
1247
|
|
|
$BitrateCompressed = $ThisFileInfo['video']['bitrate']; |
1248
|
|
|
} else { |
1249
|
|
|
return false; |
1250
|
|
|
} |
1251
|
|
|
break; |
1252
|
|
|
} |
1253
|
|
|
$BitrateUncompressed = $ThisFileInfo['video']['resolution_x'] * $ThisFileInfo['video']['resolution_y'] * $ThisFileInfo['video']['bits_per_sample'] * $FrameRate; |
1254
|
|
|
|
1255
|
|
|
$ThisFileInfo['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; |
1256
|
|
|
return true; |
1257
|
|
|
} |
1258
|
|
|
} |
1259
|
|
|
|
1260
|
|
|
if (!function_exists('CalculateCompressionRatioAudio')) { |
1261
|
|
|
function CalculateCompressionRatioAudio(&$ThisFileInfo) { |
1262
|
|
|
if (empty($ThisFileInfo['audio']['bitrate']) || empty($ThisFileInfo['audio']['channels']) || empty($ThisFileInfo['audio']['sample_rate']) || empty($ThisFileInfo['audio']['bits_per_sample'])) { |
1263
|
|
|
return false; |
1264
|
|
|
} |
1265
|
|
|
$ThisFileInfo['audio']['compression_ratio'] = $ThisFileInfo['audio']['bitrate'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * $ThisFileInfo['audio']['bits_per_sample']); |
1266
|
|
|
return true; |
1267
|
|
|
} |
1268
|
|
|
} |
1269
|
|
|
|
1270
|
|
|
if (!function_exists('IsValidMIMEstring')) { |
1271
|
|
|
function IsValidMIMEstring($mimestring) { |
1272
|
|
|
if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) { |
1273
|
|
|
return true; |
1274
|
|
|
} |
1275
|
|
|
return false; |
1276
|
|
|
} |
1277
|
|
|
} |
1278
|
|
|
|
1279
|
|
|
if (!function_exists('IsWithinBitRange')) { |
1280
|
|
View Code Duplication |
function IsWithinBitRange($number, $maxbits, $signed=false) { |
|
|
|
|
1281
|
|
|
if ($signed) { |
1282
|
|
|
if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) { |
1283
|
|
|
return true; |
1284
|
|
|
} |
1285
|
|
|
} else { |
1286
|
|
|
if (($number >= 0) && ($number <= pow(2, $maxbits))) { |
1287
|
|
|
return true; |
1288
|
|
|
} |
1289
|
|
|
} |
1290
|
|
|
return false; |
1291
|
|
|
} |
1292
|
|
|
} |
1293
|
|
|
|
1294
|
|
|
if (!function_exists('safe_parse_url')) { |
1295
|
|
|
function safe_parse_url($url) { |
1296
|
|
|
ob_start(); |
1297
|
|
|
$parts = parse_url($url); |
1298
|
|
|
$errormessage = ob_get_contents(); |
|
|
|
|
1299
|
|
|
ob_end_clean(); |
1300
|
|
|
$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); |
1301
|
|
|
$parts['host'] = (isset($parts['host']) ? $parts['host'] : ''); |
1302
|
|
|
$parts['user'] = (isset($parts['user']) ? $parts['user'] : ''); |
1303
|
|
|
$parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : ''); |
1304
|
|
|
$parts['path'] = (isset($parts['path']) ? $parts['path'] : ''); |
1305
|
|
|
$parts['query'] = (isset($parts['query']) ? $parts['query'] : ''); |
1306
|
|
|
return $parts; |
1307
|
|
|
} |
1308
|
|
|
} |
1309
|
|
|
|
1310
|
|
|
if (!function_exists('IsValidURL')) { |
1311
|
|
|
function IsValidURL($url, $allowUserPass=false) { |
1312
|
|
|
if ($url == '') { |
1313
|
|
|
return false; |
1314
|
|
|
} |
1315
|
|
|
if ($allowUserPass !== true) { |
1316
|
|
|
if (strstr($url, '@')) { |
1317
|
|
|
// in the format http://user:[email protected] or http://[email protected] |
1318
|
|
|
// but could easily be somebody incorrectly entering an email address in place of a URL |
1319
|
|
|
return false; |
1320
|
|
|
} |
1321
|
|
|
} |
1322
|
|
|
if ($parts = safe_parse_url($url)) { |
1323
|
|
|
if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) { |
1324
|
|
|
return false; |
1325
|
|
|
} elseif (!preg_match("#^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}#i$", $parts['host'], $regs) && !preg_match('#^[0-9]{1,3}(\.[0-9]{1,3}){3}$#', $parts['host'])) { |
1326
|
|
|
return false; |
1327
|
|
|
} elseif (!preg_match("#^([[:alnum:]-]|[\_])*$#i", $parts['user'], $regs)) { |
1328
|
|
|
return false; |
1329
|
|
|
} elseif (!preg_match("#^([[:alnum:]-]|[\_])*$#i", $parts['pass'], $regs)) { |
1330
|
|
|
return false; |
1331
|
|
|
} elseif (!preg_match("#^[[:alnum:]/_\.@~-]*$#i", $parts['path'], $regs)) { |
1332
|
|
|
return false; |
1333
|
|
|
} elseif (!preg_match("#^[[:alnum:]?&=+:;_()%#/,\.-]*$#i", $parts['query'], $regs)) { |
1334
|
|
|
return false; |
1335
|
|
|
} else { |
1336
|
|
|
return true; |
1337
|
|
|
} |
1338
|
|
|
} |
1339
|
|
|
return false; |
1340
|
|
|
} |
1341
|
|
|
} |
1342
|
|
|
|
1343
|
|
|
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'" method="get">'; |
1344
|
|
|
echo 'Enter 4 hex bytes of MPEG-audio header (ie <I>FF FA 92 44</I>)<BR>'; |
1345
|
|
|
echo '<input type="text" name="HeaderHexBytes" value="'.htmlentities(isset($_POST['HeaderHexBytes']) ? strtoupper($_POST['HeaderHexBytes']) : '', ENT_QUOTES).'" size="11" maxlength="11">'; |
1346
|
|
|
echo '<input type="submit" name="Analyze" value="Analyze"></form>'; |
1347
|
|
|
echo '<hr>'; |
1348
|
|
|
|
1349
|
|
|
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'" method="get">'; |
1350
|
|
|
echo 'Generate a MPEG-audio 4-byte header from these values:<BR>'; |
1351
|
|
|
echo '<table border="0">'; |
1352
|
|
|
|
1353
|
|
|
$MPEGgenerateValues = array( |
1354
|
|
|
'version' => array('1', '2', '2.5'), |
1355
|
|
|
'layer' => array('I', 'II', 'III'), |
1356
|
|
|
'protection' => array('Y', 'N'), |
1357
|
|
|
'bitrate' => array('free', '8', '16', '24', '32', '40', '48', '56', '64', '80', '96', '112', '128', '144', '160', '176', '192', '224', '256', '288', '320', '352', '384', '416', '448'), |
1358
|
|
|
'frequency' => array('8000', '11025', '12000', '16000', '22050', '24000', '32000', '44100', '48000'), |
1359
|
|
|
'padding' => array('Y', 'N'), |
1360
|
|
|
'private' => array('Y', 'N'), |
1361
|
|
|
'channelmode' => array('stereo', 'joint stereo', 'dual channel', 'mono'), |
1362
|
|
|
'modeextension' => array('none', 'IS', 'MS', 'IS+MS', '4-31', '8-31', '12-31', '16-31'), |
1363
|
|
|
'copyright' => array('Y', 'N'), |
1364
|
|
|
'original' => array('Y', 'N'), |
1365
|
|
|
'emphasis' => array('none', '50/15ms', 'CCIT J.17'), |
1366
|
|
|
); |
1367
|
|
|
|
1368
|
|
|
foreach ($MPEGgenerateValues as $name => $dataarray) { |
1369
|
|
|
echo '<tr><th>'.$name.':</th><td><select name="'.$name.'">'; |
1370
|
|
|
foreach ($dataarray as $key => $value) { |
1371
|
|
|
echo '<option'.((isset($_POST["$name"]) && ($_POST["$name"] == $value)) ? ' SELECTED' : '').'>'.$value.'</option>'; |
1372
|
|
|
} |
1373
|
|
|
echo '</select></td></tr>'; |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
|
|
if (isset($_POST['bitrate'])) { |
1377
|
|
|
echo '<tr><th>Frame Length:</th><td>'.(int) MPEGaudioFrameLength($_POST['bitrate'], $_POST['version'], $_POST['layer'], (($_POST['padding'] == 'Y') ? '1' : '0'), $_POST['frequency']).'</td></tr>'; |
1378
|
|
|
} |
1379
|
|
|
echo '</table>'; |
1380
|
|
|
echo '<input type="submit" name="Generate" value="Generate"></form>'; |
1381
|
|
|
echo '<hr>'; |
1382
|
|
|
|
1383
|
|
|
|
1384
|
|
|
if (isset($_POST['Analyze']) && $_POST['HeaderHexBytes']) { |
1385
|
|
|
|
1386
|
|
|
$headerbytearray = explode(' ', $_POST['HeaderHexBytes']); |
1387
|
|
|
if (count($headerbytearray) != 4) { |
1388
|
|
|
die('Invalid byte pattern'); |
1389
|
|
|
} |
1390
|
|
|
$headerstring = ''; |
1391
|
|
|
foreach ($headerbytearray as $textbyte) { |
1392
|
|
|
$headerstring .= chr(hexdec($textbyte)); |
1393
|
|
|
} |
1394
|
|
|
|
1395
|
|
|
$MP3fileInfo['error'] = ''; |
1396
|
|
|
|
1397
|
|
|
$MPEGheaderRawArray = MPEGaudioHeaderDecode(substr($headerstring, 0, 4)); |
1398
|
|
|
|
1399
|
|
|
if (MPEGaudioHeaderValid($MPEGheaderRawArray, true)) { |
1400
|
|
|
|
1401
|
|
|
$MP3fileInfo['raw'] = $MPEGheaderRawArray; |
1402
|
|
|
|
1403
|
|
|
$MP3fileInfo['version'] = MPEGaudioVersionLookup($MP3fileInfo['raw']['version']); |
1404
|
|
|
$MP3fileInfo['layer'] = MPEGaudioLayerLookup($MP3fileInfo['raw']['layer']); |
1405
|
|
|
$MP3fileInfo['protection'] = MPEGaudioCRCLookup($MP3fileInfo['raw']['protection']); |
1406
|
|
|
$MP3fileInfo['bitrate'] = MPEGaudioBitrateLookup($MP3fileInfo['version'], $MP3fileInfo['layer'], $MP3fileInfo['raw']['bitrate']); |
1407
|
|
|
$MP3fileInfo['frequency'] = MPEGaudioFrequencyLookup($MP3fileInfo['version'], $MP3fileInfo['raw']['sample_rate']); |
1408
|
|
|
$MP3fileInfo['padding'] = (bool) $MP3fileInfo['raw']['padding']; |
1409
|
|
|
$MP3fileInfo['private'] = (bool) $MP3fileInfo['raw']['private']; |
1410
|
|
|
$MP3fileInfo['channelmode'] = MPEGaudioChannelModeLookup($MP3fileInfo['raw']['channelmode']); |
1411
|
|
|
$MP3fileInfo['channels'] = (($MP3fileInfo['channelmode'] == 'mono') ? 1 : 2); |
1412
|
|
|
$MP3fileInfo['modeextension'] = MPEGaudioModeExtensionLookup($MP3fileInfo['layer'], $MP3fileInfo['raw']['modeextension']); |
1413
|
|
|
$MP3fileInfo['copyright'] = (bool) $MP3fileInfo['raw']['copyright']; |
1414
|
|
|
$MP3fileInfo['original'] = (bool) $MP3fileInfo['raw']['original']; |
1415
|
|
|
$MP3fileInfo['emphasis'] = MPEGaudioEmphasisLookup($MP3fileInfo['raw']['emphasis']); |
1416
|
|
|
|
1417
|
|
|
if ($MP3fileInfo['protection']) { |
1418
|
|
|
$MP3fileInfo['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); |
1419
|
|
|
} |
1420
|
|
|
|
1421
|
|
|
if ($MP3fileInfo['frequency'] > 0) { |
1422
|
|
|
$MP3fileInfo['framelength'] = MPEGaudioFrameLength($MP3fileInfo['bitrate'], $MP3fileInfo['version'], $MP3fileInfo['layer'], (int) $MP3fileInfo['padding'], $MP3fileInfo['frequency']); |
1423
|
|
|
} |
1424
|
|
|
if ($MP3fileInfo['bitrate'] != 'free') { |
1425
|
|
|
$MP3fileInfo['bitrate'] *= 1000; |
1426
|
|
|
} |
1427
|
|
|
|
1428
|
|
|
} else { |
1429
|
|
|
|
1430
|
|
|
$MP3fileInfo['error'] .= "\n".'Invalid MPEG audio header'; |
1431
|
|
|
|
1432
|
|
|
} |
1433
|
|
|
|
1434
|
|
|
if (!$MP3fileInfo['error']) { |
1435
|
|
|
unset($MP3fileInfo['error']); |
1436
|
|
|
} |
1437
|
|
|
|
1438
|
|
|
echo table_var_dump($MP3fileInfo); |
1439
|
|
|
|
1440
|
|
|
} elseif (isset($_POST['Generate'])) { |
1441
|
|
|
|
1442
|
|
|
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM |
1443
|
|
|
|
1444
|
|
|
$headerbitstream = '11111111111'; // A - Frame sync (all bits set) |
1445
|
|
|
|
1446
|
|
|
$MPEGversionLookup = array('2.5'=>'00', '2'=>'10', '1'=>'11'); |
1447
|
|
|
$headerbitstream .= $MPEGversionLookup[$_POST['version']]; // B - MPEG Audio version ID |
1448
|
|
|
|
1449
|
|
|
$MPEGlayerLookup = array('III'=>'01', 'II'=>'10', 'I'=>'11'); |
1450
|
|
|
$headerbitstream .= $MPEGlayerLookup[$_POST['layer']]; // C - Layer description |
1451
|
|
|
|
1452
|
|
|
$headerbitstream .= (($_POST['protection'] == 'Y') ? '0' : '1'); // D - Protection bit |
1453
|
|
|
|
1454
|
|
|
$MPEGaudioBitrateLookup['1']['I'] = array('free'=>'0000', '32'=>'0001', '64'=>'0010', '96'=>'0011', '128'=>'0100', '160'=>'0101', '192'=>'0110', '224'=>'0111', '256'=>'1000', '288'=>'1001', '320'=>'1010', '352'=>'1011', '384'=>'1100', '416'=>'1101', '448'=>'1110'); |
1455
|
|
|
$MPEGaudioBitrateLookup['1']['II'] = array('free'=>'0000', '32'=>'0001', '48'=>'0010', '56'=>'0011', '64'=>'0100', '80'=>'0101', '96'=>'0110', '112'=>'0111', '128'=>'1000', '160'=>'1001', '192'=>'1010', '224'=>'1011', '256'=>'1100', '320'=>'1101', '384'=>'1110'); |
1456
|
|
|
$MPEGaudioBitrateLookup['1']['III'] = array('free'=>'0000', '32'=>'0001', '40'=>'0010', '48'=>'0011', '56'=>'0100', '64'=>'0101', '80'=>'0110', '96'=>'0111', '112'=>'1000', '128'=>'1001', '160'=>'1010', '192'=>'1011', '224'=>'1100', '256'=>'1101', '320'=>'1110'); |
1457
|
|
|
$MPEGaudioBitrateLookup['2']['I'] = array('free'=>'0000', '32'=>'0001', '48'=>'0010', '56'=>'0011', '64'=>'0100', '80'=>'0101', '96'=>'0110', '112'=>'0111', '128'=>'1000', '144'=>'1001', '160'=>'1010', '176'=>'1011', '192'=>'1100', '224'=>'1101', '256'=>'1110'); |
1458
|
|
|
$MPEGaudioBitrateLookup['2']['II'] = array('free'=>'0000', '8'=>'0001', '16'=>'0010', '24'=>'0011', '32'=>'0100', '40'=>'0101', '48'=>'0110', '56'=>'0111', '64'=>'1000', '80'=>'1001', '96'=>'1010', '112'=>'1011', '128'=>'1100', '144'=>'1101', '160'=>'1110'); |
1459
|
|
|
$MPEGaudioBitrateLookup['2']['III'] = $MPEGaudioBitrateLookup['2']['II']; |
1460
|
|
|
$MPEGaudioBitrateLookup['2.5']['I'] = $MPEGaudioBitrateLookup['2']['I']; |
1461
|
|
|
$MPEGaudioBitrateLookup['2.5']['II'] = $MPEGaudioBitrateLookup['2']['II']; |
1462
|
|
|
$MPEGaudioBitrateLookup['2.5']['III'] = $MPEGaudioBitrateLookup['2']['II']; |
1463
|
|
|
if (isset($MPEGaudioBitrateLookup[$_POST['version']][$_POST['layer']][$_POST['bitrate']])) { |
1464
|
|
|
$headerbitstream .= $MPEGaudioBitrateLookup[$_POST['version']][$_POST['layer']][$_POST['bitrate']]; // E - Bitrate index |
1465
|
|
|
} else { |
1466
|
|
|
die('Invalid <B>Bitrate</B>'); |
1467
|
|
|
} |
1468
|
|
|
|
1469
|
|
|
$MPEGaudioFrequencyLookup['1'] = array('44100'=>'00', '48000'=>'01', '32000'=>'10'); |
1470
|
|
|
$MPEGaudioFrequencyLookup['2'] = array('22050'=>'00', '24000'=>'01', '16000'=>'10'); |
1471
|
|
|
$MPEGaudioFrequencyLookup['2.5'] = array('11025'=>'00', '12000'=>'01', '8000'=>'10'); |
1472
|
|
|
if (isset($MPEGaudioFrequencyLookup[$_POST['version']][$_POST['frequency']])) { |
1473
|
|
|
$headerbitstream .= $MPEGaudioFrequencyLookup[$_POST['version']][$_POST['frequency']]; // F - Sampling rate frequency index |
1474
|
|
|
} else { |
1475
|
|
|
die('Invalid <B>Frequency</B>'); |
1476
|
|
|
} |
1477
|
|
|
|
1478
|
|
|
$headerbitstream .= (($_POST['padding'] == 'Y') ? '1' : '0'); // G - Padding bit |
1479
|
|
|
|
1480
|
|
|
$headerbitstream .= (($_POST['private'] == 'Y') ? '1' : '0'); // H - Private bit |
1481
|
|
|
|
1482
|
|
|
$MPEGaudioChannelModeLookup = array('stereo'=>'00', 'joint stereo'=>'01', 'dual channel'=>'10', 'mono'=>'11'); |
1483
|
|
|
$headerbitstream .= $MPEGaudioChannelModeLookup[$_POST['channelmode']]; // I - Channel Mode |
1484
|
|
|
|
1485
|
|
|
$MPEGaudioModeExtensionLookup['I'] = array('4-31'=>'00', '8-31'=>'01', '12-31'=>'10', '16-31'=>'11'); |
1486
|
|
|
$MPEGaudioModeExtensionLookup['II'] = $MPEGaudioModeExtensionLookup['I']; |
1487
|
|
|
$MPEGaudioModeExtensionLookup['III'] = array('none'=>'00', 'IS'=>'01', 'MS'=>'10', 'IS+MS'=>'11'); |
1488
|
|
|
if ($_POST['channelmode'] != 'joint stereo') { |
1489
|
|
|
$headerbitstream .= '00'; |
1490
|
|
|
} elseif (isset($MPEGaudioModeExtensionLookup[$_POST['layer']][$_POST['modeextension']])) { |
1491
|
|
|
$headerbitstream .= $MPEGaudioModeExtensionLookup[$_POST['layer']][$_POST['modeextension']]; // J - Mode extension (Only if Joint stereo) |
1492
|
|
|
} else { |
1493
|
|
|
die('Invalid <B>Mode Extension</B>'); |
1494
|
|
|
} |
1495
|
|
|
|
1496
|
|
|
$headerbitstream .= (($_POST['copyright'] == 'Y') ? '1' : '0'); // K - Copyright |
1497
|
|
|
|
1498
|
|
|
$headerbitstream .= (($_POST['original'] == 'Y') ? '1' : '0'); // L - Original |
1499
|
|
|
|
1500
|
|
|
$MPEGaudioEmphasisLookup = array('none'=>'00', '50/15ms'=>'01', 'CCIT J.17'=>'11'); |
1501
|
|
|
if (isset($MPEGaudioEmphasisLookup[$_POST['emphasis']])) { |
1502
|
|
|
$headerbitstream .= $MPEGaudioEmphasisLookup[$_POST['emphasis']]; // M - Emphasis |
1503
|
|
|
} else { |
1504
|
|
|
die('Invalid <B>Emphasis</B>'); |
1505
|
|
|
} |
1506
|
|
|
|
1507
|
|
|
echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 0, 8))), 2, '0', STR_PAD_LEFT)).' '; |
1508
|
|
|
echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 8, 8))), 2, '0', STR_PAD_LEFT)).' '; |
1509
|
|
|
echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 16, 8))), 2, '0', STR_PAD_LEFT)).' '; |
1510
|
|
|
echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 24, 8))), 2, '0', STR_PAD_LEFT)).'<BR>'; |
1511
|
|
|
|
1512
|
|
|
} |
1513
|
|
|
|
1514
|
|
|
function MPEGaudioVersionLookup($rawversion) { |
1515
|
|
|
$MPEGaudioVersionLookup = array('2.5', FALSE, '2', '1'); |
1516
|
|
|
return (isset($MPEGaudioVersionLookup["$rawversion"]) ? $MPEGaudioVersionLookup["$rawversion"] : FALSE); |
1517
|
|
|
} |
1518
|
|
|
|
1519
|
|
|
function MPEGaudioLayerLookup($rawlayer) { |
1520
|
|
|
$MPEGaudioLayerLookup = array(FALSE, 'III', 'II', 'I'); |
1521
|
|
|
return (isset($MPEGaudioLayerLookup["$rawlayer"]) ? $MPEGaudioLayerLookup["$rawlayer"] : FALSE); |
1522
|
|
|
} |
1523
|
|
|
|
1524
|
|
|
function MPEGaudioBitrateLookup($version, $layer, $rawbitrate) { |
1525
|
|
|
static $MPEGaudioBitrateLookup; |
1526
|
|
|
if (empty($MPEGaudioBitrateLookup)) { |
1527
|
|
|
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); |
1528
|
|
|
} |
1529
|
|
|
return (isset($MPEGaudioBitrateLookup["$version"]["$layer"]["$rawbitrate"]) ? $MPEGaudioBitrateLookup["$version"]["$layer"]["$rawbitrate"] : FALSE); |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
function MPEGaudioFrequencyLookup($version, $rawfrequency) { |
1533
|
|
|
static $MPEGaudioFrequencyLookup; |
1534
|
|
|
if (empty($MPEGaudioFrequencyLookup)) { |
1535
|
|
|
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); |
1536
|
|
|
} |
1537
|
|
|
return (isset($MPEGaudioFrequencyLookup["$version"]["$rawfrequency"]) ? $MPEGaudioFrequencyLookup["$version"]["$rawfrequency"] : FALSE); |
1538
|
|
|
} |
1539
|
|
|
|
1540
|
|
|
function MPEGaudioChannelModeLookup($rawchannelmode) { |
1541
|
|
|
$MPEGaudioChannelModeLookup = array('stereo', 'joint stereo', 'dual channel', 'mono'); |
1542
|
|
|
return (isset($MPEGaudioChannelModeLookup["$rawchannelmode"]) ? $MPEGaudioChannelModeLookup["$rawchannelmode"] : FALSE); |
1543
|
|
|
} |
1544
|
|
|
|
1545
|
|
|
function MPEGaudioModeExtensionLookup($layer, $rawmodeextension) { |
1546
|
|
|
$MPEGaudioModeExtensionLookup['I'] = array('4-31', '8-31', '12-31', '16-31'); |
|
|
|
|
1547
|
|
|
$MPEGaudioModeExtensionLookup['II'] = array('4-31', '8-31', '12-31', '16-31'); |
1548
|
|
|
$MPEGaudioModeExtensionLookup['III'] = array('', 'IS', 'MS', 'IS+MS'); |
1549
|
|
|
return (isset($MPEGaudioModeExtensionLookup["$layer"]["$rawmodeextension"]) ? $MPEGaudioModeExtensionLookup["$layer"]["$rawmodeextension"] : FALSE); |
1550
|
|
|
} |
1551
|
|
|
|
1552
|
|
|
function MPEGaudioEmphasisLookup($rawemphasis) { |
1553
|
|
|
$MPEGaudioEmphasisLookup = array('none', '50/15ms', FALSE, 'CCIT J.17'); |
1554
|
|
|
return (isset($MPEGaudioEmphasisLookup["$rawemphasis"]) ? $MPEGaudioEmphasisLookup["$rawemphasis"] : FALSE); |
1555
|
|
|
} |
1556
|
|
|
|
1557
|
|
|
function MPEGaudioCRCLookup($CRCbit) { |
1558
|
|
|
// inverse boolean cast :) |
1559
|
|
|
if ($CRCbit == '0') { |
1560
|
|
|
return TRUE; |
1561
|
|
|
} else { |
1562
|
|
|
return FALSE; |
1563
|
|
|
} |
1564
|
|
|
} |
1565
|
|
|
|
1566
|
|
|
///////////////////////////////////////////////////////////////// |
1567
|
|
|
/// getID3() by James Heinrich <[email protected]> // |
1568
|
|
|
// available at http://getid3.sourceforge.net /// |
1569
|
|
|
// or https://www.getid3.org /// |
1570
|
|
|
///////////////////////////////////////////////////////////////// |
1571
|
|
|
// // |
1572
|
|
|
// getid3.mp3.php - part of getID3() // |
1573
|
|
|
// See getid3.readme.txt for more details // |
1574
|
|
|
// // |
1575
|
|
|
///////////////////////////////////////////////////////////////// |
1576
|
|
|
|
1577
|
|
|
// number of frames to scan to determine if MPEG-audio sequence is valid |
1578
|
|
|
// Lower this number to 5-20 for faster scanning |
1579
|
|
|
// Increase this number to 50+ for most accurate detection of valid VBR/CBR |
1580
|
|
|
// mpeg-audio streams |
1581
|
|
|
define('MPEG_VALID_CHECK_FRAMES', 35); |
1582
|
|
|
|
1583
|
|
|
function getMP3headerFilepointer(&$fd, &$ThisFileInfo) { |
1584
|
|
|
|
1585
|
|
|
getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset']); |
1586
|
|
|
|
1587
|
|
View Code Duplication |
if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) { |
|
|
|
|
1588
|
|
|
$ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); |
1589
|
|
|
} |
1590
|
|
|
|
1591
|
|
|
if (((isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) { |
1592
|
|
|
|
1593
|
|
|
$ThisFileInfo['warning'] .= "\n".'Unknown data before synch '; |
1594
|
|
|
if (isset($ThisFileInfo['id3v2']['headerlength'])) { |
1595
|
|
|
$ThisFileInfo['warning'] .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, '; |
1596
|
|
|
} else { |
1597
|
|
|
$ThisFileInfo['warning'] .= '(should be at beginning of file, '; |
1598
|
|
|
} |
1599
|
|
|
$ThisFileInfo['warning'] .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')'; |
1600
|
|
|
if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { |
1601
|
|
|
if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) { |
1602
|
|
|
$ThisFileInfo['warning'] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.'; |
1603
|
|
|
$ThisFileInfo['audio']['codec'] = 'LAME'; |
1604
|
|
View Code Duplication |
} elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) { |
|
|
|
|
1605
|
|
|
$ThisFileInfo['warning'] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.'; |
1606
|
|
|
$ThisFileInfo['audio']['codec'] = 'LAME'; |
1607
|
|
|
} |
1608
|
|
|
} |
1609
|
|
|
|
1610
|
|
|
} |
1611
|
|
|
|
1612
|
|
|
if (isset($ThisFileInfo['mpeg']['audio']['layer']) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'II')) { |
1613
|
|
|
$ThisFileInfo['audio']['dataformat'] = 'mp2'; |
1614
|
|
|
} elseif (isset($ThisFileInfo['mpeg']['audio']['layer']) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'I')) { |
1615
|
|
|
$ThisFileInfo['audio']['dataformat'] = 'mp1'; |
1616
|
|
|
} |
1617
|
|
View Code Duplication |
if ($ThisFileInfo['fileformat'] == 'mp3') { |
|
|
|
|
1618
|
|
|
switch ($ThisFileInfo['audio']['dataformat']) { |
1619
|
|
|
case 'mp1': |
1620
|
|
|
case 'mp2': |
1621
|
|
|
case 'mp3': |
1622
|
|
|
$ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; |
1623
|
|
|
break; |
1624
|
|
|
|
1625
|
|
|
default: |
1626
|
|
|
$ThisFileInfo['warning'] .= "\n".'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"'; |
1627
|
|
|
break; |
1628
|
|
|
} |
1629
|
|
|
} |
1630
|
|
|
|
1631
|
|
|
if (empty($ThisFileInfo['fileformat'])) { |
1632
|
|
|
$ThisFileInfo['error'] .= "\n".'Synch not found'; |
1633
|
|
|
unset($ThisFileInfo['fileformat']); |
1634
|
|
|
unset($ThisFileInfo['audio']['bitrate_mode']); |
1635
|
|
|
unset($ThisFileInfo['avdataoffset']); |
1636
|
|
|
unset($ThisFileInfo['avdataend']); |
1637
|
|
|
return false; |
1638
|
|
|
} |
1639
|
|
|
|
1640
|
|
|
$ThisFileInfo['mime_type'] = 'audio/mpeg'; |
1641
|
|
|
$ThisFileInfo['audio']['lossless'] = false; |
1642
|
|
|
|
1643
|
|
|
// Calculate playtime |
1644
|
|
|
if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) { |
1645
|
|
|
$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; |
1646
|
|
|
} |
1647
|
|
|
|
1648
|
|
|
if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) { |
1649
|
|
|
$ThisFileInfo['audio']['codec'] = 'LAME'; |
1650
|
|
View Code Duplication |
if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) { |
|
|
|
|
1651
|
|
|
$ThisFileInfo['audio']['encoder'] = trim($ThisFileInfo['mpeg']['audio']['LAME']['long_version']); |
1652
|
|
|
} |
1653
|
|
|
} |
1654
|
|
|
|
1655
|
|
|
return true; |
1656
|
|
|
} |
1657
|
|
|
|
1658
|
|
|
|
1659
|
|
|
function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { |
1660
|
|
|
|
1661
|
|
|
static $MPEGaudioVersionLookup; |
1662
|
|
|
static $MPEGaudioLayerLookup; |
1663
|
|
|
static $MPEGaudioBitrateLookup; |
1664
|
|
|
static $MPEGaudioFrequencyLookup; |
1665
|
|
|
static $MPEGaudioChannelModeLookup; |
1666
|
|
|
static $MPEGaudioModeExtensionLookup; |
1667
|
|
|
static $MPEGaudioEmphasisLookup; |
1668
|
|
View Code Duplication |
if (empty($MPEGaudioVersionLookup)) { |
|
|
|
|
1669
|
|
|
$MPEGaudioVersionLookup = MPEGaudioVersionArray(); |
1670
|
|
|
$MPEGaudioLayerLookup = MPEGaudioLayerArray(); |
1671
|
|
|
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); |
1672
|
|
|
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); |
1673
|
|
|
$MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray(); |
1674
|
|
|
$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray(); |
1675
|
|
|
$MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray(); |
1676
|
|
|
} |
1677
|
|
|
|
1678
|
|
|
if ($offset >= $ThisFileInfo['avdataend']) { |
1679
|
|
|
$ThisFileInfo['error'] .= "\n".'end of file encounter looking for MPEG synch'; |
1680
|
|
|
return false; |
1681
|
|
|
} |
1682
|
|
|
fseek($fd, $offset, SEEK_SET); |
1683
|
|
|
$headerstring = fread($fd, 1441); // worse-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame |
1684
|
|
|
|
1685
|
|
|
// MP3 audio frame structure: |
1686
|
|
|
// $aa $aa $aa $aa [$bb $bb] $cc... |
1687
|
|
|
// where $aa..$aa is the four-byte mpeg-audio header (below) |
1688
|
|
|
// $bb $bb is the optional 2-byte CRC |
1689
|
|
|
// and $cc... is the audio data |
1690
|
|
|
|
1691
|
|
|
$head4 = substr($headerstring, 0, 4); |
1692
|
|
|
|
1693
|
|
|
static $MPEGaudioHeaderDecodeCache = array(); |
1694
|
|
View Code Duplication |
if (isset($MPEGaudioHeaderDecodeCache[$head4])) { |
|
|
|
|
1695
|
|
|
$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; |
1696
|
|
|
} else { |
1697
|
|
|
$MPEGheaderRawArray = MPEGaudioHeaderDecode($head4); |
1698
|
|
|
$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; |
1699
|
|
|
} |
1700
|
|
|
|
1701
|
|
|
static $MPEGaudioHeaderValidCache = array(); |
1702
|
|
|
|
1703
|
|
|
// Not in cache |
1704
|
|
|
if (!isset($MPEGaudioHeaderValidCache[$head4])) { |
1705
|
|
|
$MPEGaudioHeaderValidCache[$head4] = MPEGaudioHeaderValid($MPEGheaderRawArray); |
1706
|
|
|
} |
1707
|
|
|
|
1708
|
|
|
if ($MPEGaudioHeaderValidCache[$head4]) { |
1709
|
|
|
$ThisFileInfo['mpeg']['audio']['raw'] = $MPEGheaderRawArray; |
1710
|
|
|
} else { |
1711
|
|
|
$ThisFileInfo['error'] .= "\n".'Invalid MPEG audio header at offset '.$offset; |
1712
|
|
|
return false; |
1713
|
|
|
} |
1714
|
|
|
|
1715
|
|
|
if (!$FastMPEGheaderScan) { |
1716
|
|
|
|
1717
|
|
|
$ThisFileInfo['mpeg']['audio']['version'] = $MPEGaudioVersionLookup[$ThisFileInfo['mpeg']['audio']['raw']['version']]; |
1718
|
|
|
$ThisFileInfo['mpeg']['audio']['layer'] = $MPEGaudioLayerLookup[$ThisFileInfo['mpeg']['audio']['raw']['layer']]; |
1719
|
|
|
|
1720
|
|
|
$ThisFileInfo['mpeg']['audio']['channelmode'] = $MPEGaudioChannelModeLookup[$ThisFileInfo['mpeg']['audio']['raw']['channelmode']]; |
1721
|
|
|
$ThisFileInfo['mpeg']['audio']['channels'] = (($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') ? 1 : 2); |
1722
|
|
|
$ThisFileInfo['mpeg']['audio']['sample_rate'] = $MPEGaudioFrequencyLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['raw']['sample_rate']]; |
1723
|
|
|
$ThisFileInfo['mpeg']['audio']['protection'] = !$ThisFileInfo['mpeg']['audio']['raw']['protection']; |
1724
|
|
|
$ThisFileInfo['mpeg']['audio']['private'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['private']; |
1725
|
|
|
$ThisFileInfo['mpeg']['audio']['modeextension'] = $MPEGaudioModeExtensionLookup[$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['modeextension']]; |
1726
|
|
|
$ThisFileInfo['mpeg']['audio']['copyright'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['copyright']; |
1727
|
|
|
$ThisFileInfo['mpeg']['audio']['original'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['original']; |
1728
|
|
|
$ThisFileInfo['mpeg']['audio']['emphasis'] = $MPEGaudioEmphasisLookup[$ThisFileInfo['mpeg']['audio']['raw']['emphasis']]; |
1729
|
|
|
|
1730
|
|
|
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; |
1731
|
|
|
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; |
1732
|
|
|
|
1733
|
|
|
if ($ThisFileInfo['mpeg']['audio']['protection']) { |
1734
|
|
|
$ThisFileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); |
1735
|
|
|
} |
1736
|
|
|
|
1737
|
|
|
} |
1738
|
|
|
|
1739
|
|
|
if ($ThisFileInfo['mpeg']['audio']['raw']['bitrate'] == 15) { |
1740
|
|
|
// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 |
1741
|
|
|
$ThisFileInfo['warning'] .= "\n".'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; |
1742
|
|
|
$ThisFileInfo['mpeg']['audio']['raw']['bitrate'] = 0; |
1743
|
|
|
} |
1744
|
|
|
$ThisFileInfo['mpeg']['audio']['padding'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['padding']; |
1745
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate'] = $MPEGaudioBitrateLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['bitrate']]; |
1746
|
|
|
|
1747
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) { |
1748
|
|
|
// only skip multiple frame check if free-format bitstream found at beginning of file |
1749
|
|
|
// otherwise is quite possibly simply corrupted data |
1750
|
|
|
$recursivesearch = false; |
1751
|
|
|
} |
1752
|
|
|
|
1753
|
|
|
// For Layer II there are some combinations of bitrate and mode which are not allowed. |
1754
|
|
|
if (!$FastMPEGheaderScan && ($ThisFileInfo['mpeg']['audio']['layer'] == 'II')) { |
1755
|
|
|
|
1756
|
|
|
$ThisFileInfo['audio']['dataformat'] = 'mp2'; |
1757
|
|
|
switch ($ThisFileInfo['mpeg']['audio']['channelmode']) { |
1758
|
|
|
|
1759
|
|
|
case 'mono': |
1760
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') || ($ThisFileInfo['mpeg']['audio']['bitrate'] <= 192)) { |
1761
|
|
|
// these are ok |
1762
|
|
View Code Duplication |
} else { |
|
|
|
|
1763
|
|
|
$ThisFileInfo['error'] .= "\n".$ThisFileInfo['mpeg']['audio']['bitrate'].'kbps not allowed in Layer II, '.$ThisFileInfo['mpeg']['audio']['channelmode'].'.'; |
1764
|
|
|
return false; |
1765
|
|
|
} |
1766
|
|
|
break; |
1767
|
|
|
|
1768
|
|
|
case 'stereo': |
1769
|
|
|
case 'joint stereo': |
1770
|
|
|
case 'dual channel': |
1771
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') || ($ThisFileInfo['mpeg']['audio']['bitrate'] == 64) || ($ThisFileInfo['mpeg']['audio']['bitrate'] >= 96)) { |
1772
|
|
|
// these are ok |
1773
|
|
View Code Duplication |
} else { |
|
|
|
|
1774
|
|
|
$ThisFileInfo['error'] .= "\n".$ThisFileInfo['mpeg']['audio']['bitrate'].'kbps not allowed in Layer II, '.$ThisFileInfo['mpeg']['audio']['channelmode'].'.'; |
1775
|
|
|
return false; |
1776
|
|
|
} |
1777
|
|
|
break; |
1778
|
|
|
|
1779
|
|
|
} |
1780
|
|
|
|
1781
|
|
|
} |
1782
|
|
|
|
1783
|
|
|
|
1784
|
|
|
if ($ThisFileInfo['audio']['sample_rate'] > 0) { |
1785
|
|
|
$ThisFileInfo['mpeg']['audio']['framelength'] = MPEGaudioFrameLength($ThisFileInfo['mpeg']['audio']['bitrate'], $ThisFileInfo['mpeg']['audio']['version'], $ThisFileInfo['mpeg']['audio']['layer'], (int) $ThisFileInfo['mpeg']['audio']['padding'], $ThisFileInfo['audio']['sample_rate']); |
1786
|
|
|
} |
1787
|
|
|
|
1788
|
|
|
if ($ThisFileInfo['mpeg']['audio']['bitrate'] != 'free') { |
1789
|
|
|
|
1790
|
|
|
$ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['bitrate']; |
1791
|
|
|
|
1792
|
|
|
if (isset($ThisFileInfo['mpeg']['audio']['framelength'])) { |
1793
|
|
|
$nextframetestoffset = $offset + $ThisFileInfo['mpeg']['audio']['framelength']; |
1794
|
|
|
} else { |
1795
|
|
|
$ThisFileInfo['error'] .= "\n".'Frame at offset('.$offset.') is has an invalid frame length.'; |
1796
|
|
|
return false; |
1797
|
|
|
} |
1798
|
|
|
|
1799
|
|
|
} |
1800
|
|
|
|
1801
|
|
|
$ExpectedNumberOfAudioBytes = 0; |
1802
|
|
|
|
1803
|
|
|
//////////////////////////////////////////////////////////////////////////////////// |
1804
|
|
|
// Variable-bitrate headers |
1805
|
|
|
|
1806
|
|
|
if (substr($headerstring, 4 + 32, 4) == 'VBRI') { |
1807
|
|
|
// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) |
1808
|
|
|
// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html |
1809
|
|
|
|
1810
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; |
1811
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer'; |
1812
|
|
|
$ThisFileInfo['audio']['codec'] = 'Fraunhofer'; |
1813
|
|
|
|
1814
|
|
|
$SideInfoData = substr($headerstring, 4 + 2, 32); |
|
|
|
|
1815
|
|
|
|
1816
|
|
|
$FraunhoferVBROffset = 36; |
1817
|
|
|
|
1818
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_encoder_version'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); |
1819
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_encoder_delay'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); |
1820
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); |
1821
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); |
1822
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); |
1823
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_seek_offsets'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); |
1824
|
|
|
//$ThisFileInfo['mpeg']['audio']['reserved'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 4)); // hardcoded $00 $01 $00 $02 - purpose unknown |
1825
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_seek_offsets_stride'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); |
1826
|
|
|
|
1827
|
|
|
$ExpectedNumberOfAudioBytes = $ThisFileInfo['mpeg']['audio']['VBR_bytes']; |
1828
|
|
|
|
1829
|
|
|
$previousbyteoffset = $offset; |
1830
|
|
|
for ($i = 0; $i < $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']; $i++) { |
1831
|
|
|
$Fraunhofer_OffsetN = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, 2)); |
1832
|
|
|
$FraunhoferVBROffset += 2; |
1833
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_offsets_relative'][$i] = $Fraunhofer_OffsetN; |
1834
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_offsets_absolute'][$i] = $Fraunhofer_OffsetN + $previousbyteoffset; |
1835
|
|
|
$previousbyteoffset += $Fraunhofer_OffsetN; |
1836
|
|
|
} |
1837
|
|
|
|
1838
|
|
|
|
1839
|
|
|
} else { |
1840
|
|
|
|
1841
|
|
|
// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) |
1842
|
|
|
// depending on MPEG layer and number of channels |
1843
|
|
|
|
1844
|
|
|
if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
1845
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { |
|
|
|
|
1846
|
|
|
// MPEG-1 (mono) |
1847
|
|
|
$VBRidOffset = 4 + 17; // 0x15 |
1848
|
|
|
$SideInfoData = substr($headerstring, 4 + 2, 17); |
|
|
|
|
1849
|
|
|
} else { |
1850
|
|
|
// MPEG-1 (stereo, joint-stereo, dual-channel) |
1851
|
|
|
$VBRidOffset = 4 + 32; // 0x24 |
1852
|
|
|
$SideInfoData = substr($headerstring, 4 + 2, 32); |
|
|
|
|
1853
|
|
|
} |
1854
|
|
View Code Duplication |
} else { // 2 or 2.5 |
|
|
|
|
1855
|
|
|
if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { |
1856
|
|
|
// MPEG-2, MPEG-2.5 (mono) |
1857
|
|
|
$VBRidOffset = 4 + 9; // 0x0D |
1858
|
|
|
$SideInfoData = substr($headerstring, 4 + 2, 9); |
|
|
|
|
1859
|
|
|
} else { |
1860
|
|
|
// MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) |
1861
|
|
|
$VBRidOffset = 4 + 17; // 0x15 |
1862
|
|
|
$SideInfoData = substr($headerstring, 4 + 2, 17); |
|
|
|
|
1863
|
|
|
} |
1864
|
|
|
} |
1865
|
|
|
|
1866
|
|
|
if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { |
1867
|
|
|
// 'Xing' is traditional Xing VBR frame |
1868
|
|
|
// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) |
1869
|
|
|
|
1870
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; |
1871
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_method'] = 'Xing'; |
1872
|
|
|
|
1873
|
|
|
$ThisFileInfo['mpeg']['audio']['xing_flags_raw'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); |
1874
|
|
|
|
1875
|
|
|
$ThisFileInfo['mpeg']['audio']['xing_flags']['frames'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000001); |
1876
|
|
|
$ThisFileInfo['mpeg']['audio']['xing_flags']['bytes'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000002); |
1877
|
|
|
$ThisFileInfo['mpeg']['audio']['xing_flags']['toc'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000004); |
1878
|
|
|
$ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000008); |
1879
|
|
|
|
1880
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['xing_flags']['frames']) { |
|
|
|
|
1881
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); |
1882
|
|
|
} |
1883
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']) { |
|
|
|
|
1884
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); |
1885
|
|
|
} |
1886
|
|
|
|
1887
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && !empty($ThisFileInfo['mpeg']['audio']['VBR_frames']) && !empty($ThisFileInfo['mpeg']['audio']['VBR_bytes'])) { |
1888
|
|
|
$framelengthfloat = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']; |
1889
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { |
|
|
|
|
1890
|
|
|
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 |
1891
|
|
|
$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 12; |
1892
|
|
|
} else { |
1893
|
|
|
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 |
1894
|
|
|
$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 144; |
1895
|
|
|
} |
1896
|
|
|
$ThisFileInfo['mpeg']['audio']['framelength'] = floor($framelengthfloat); |
1897
|
|
|
} |
1898
|
|
|
|
1899
|
|
|
if ($ThisFileInfo['mpeg']['audio']['xing_flags']['toc']) { |
1900
|
|
|
$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); |
1901
|
|
|
for ($i = 0; $i < 100; $i++) { |
1902
|
|
|
$ThisFileInfo['mpeg']['audio']['toc'][$i] = ord($LAMEtocData[$i]); |
1903
|
|
|
} |
1904
|
|
|
} |
1905
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale']) { |
|
|
|
|
1906
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_scale'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); |
1907
|
|
|
} |
1908
|
|
|
|
1909
|
|
|
// http://gabriel.mp3-tech.org/mp3infotag.html |
1910
|
|
|
if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { |
1911
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); |
1912
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], 0, 9); |
1913
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x55\xAA"); |
1914
|
|
|
|
1915
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['short_version'] >= 'LAME3.90.') { |
1916
|
|
|
|
1917
|
|
|
// It the LAME tag was only introduced in LAME v3.90 |
1918
|
|
|
// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 |
1919
|
|
|
|
1920
|
|
|
// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html |
1921
|
|
|
// are assuming a 'Xing' identifier offset of 0x24, which is the case for |
1922
|
|
|
// MPEG-1 non-mono, but not for other combinations |
1923
|
|
|
$LAMEtagOffsetContant = $VBRidOffset - 0x24; |
1924
|
|
|
|
1925
|
|
|
// byte $9B VBR Quality |
1926
|
|
|
// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. |
1927
|
|
|
// Actually overwrites original Xing bytes |
1928
|
|
|
unset($ThisFileInfo['mpeg']['audio']['VBR_scale']); |
1929
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['vbr_quality'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1)); |
1930
|
|
|
|
1931
|
|
|
// bytes $9C-$A4 Encoder short VersionString |
1932
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9); |
1933
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['long_version'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; |
1934
|
|
|
|
1935
|
|
|
// byte $A5 Info Tag revision + VBR method |
1936
|
|
|
$LAMEtagRevisionVBRmethod = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1)); |
1937
|
|
|
|
1938
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; |
1939
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; |
1940
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['vbr_method'] = LAMEvbrMethodLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method']); |
1941
|
|
|
|
1942
|
|
|
// byte $A6 Lowpass filter value |
1943
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['lowpass_frequency'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100; |
1944
|
|
|
|
1945
|
|
|
// bytes $A7-$AE Replay Gain |
1946
|
|
|
// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html |
1947
|
|
|
// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" |
1948
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = BigEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4)); |
1949
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2)); |
1950
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2)); |
1951
|
|
|
|
1952
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] == 0) { |
1953
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = false; |
1954
|
|
|
} |
1955
|
|
|
|
1956
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] != 0) { |
|
|
|
|
1957
|
|
|
require_once(GETID3_INCLUDEPATH.'getid3.rgad.php'); |
1958
|
|
|
|
1959
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0xE000) >> 13; |
1960
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x1C00) >> 10; |
1961
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x0200) >> 9; |
1962
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x01FF; |
1963
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name']); |
1964
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator']); |
1965
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit']); |
1966
|
|
|
|
1967
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) { |
1968
|
|
|
$ThisFileInfo['replay_gain']['radio']['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; |
1969
|
|
|
} |
1970
|
|
|
$ThisFileInfo['replay_gain']['radio']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator']; |
1971
|
|
|
$ThisFileInfo['replay_gain']['radio']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db']; |
1972
|
|
|
} |
1973
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] != 0) { |
|
|
|
|
1974
|
|
|
require_once(GETID3_INCLUDEPATH.'getid3.rgad.php'); |
1975
|
|
|
|
1976
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0xE000) >> 13; |
1977
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x1C00) >> 10; |
1978
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit'] = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x0200) >> 9; |
1979
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x01FF; |
1980
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['name'] = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name']); |
1981
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator']); |
1982
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db'] = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit']); |
1983
|
|
|
|
1984
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) { |
1985
|
|
|
$ThisFileInfo['replay_gain']['audiophile']['peak'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude']; |
1986
|
|
|
} |
1987
|
|
|
$ThisFileInfo['replay_gain']['audiophile']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator']; |
1988
|
|
|
$ThisFileInfo['replay_gain']['audiophile']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db']; |
1989
|
|
|
} |
1990
|
|
|
|
1991
|
|
|
|
1992
|
|
|
// byte $AF Encoding flags + ATH Type |
1993
|
|
|
$EncodingFlagsATHtype = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1)); |
1994
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); |
1995
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); |
1996
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); |
1997
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); |
1998
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['ath_type'] = $EncodingFlagsATHtype & 0x0F; |
1999
|
|
|
|
2000
|
|
|
// byte $B0 if ABR {specified bitrate} else {minimal bitrate} |
2001
|
|
|
$ABRbitrateMinBitrate = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1)); |
2002
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 2) { // Average BitRate (ABR) |
2003
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['bitrate_abr'] = $ABRbitrateMinBitrate; |
2004
|
|
|
} elseif ($ABRbitrateMinBitrate > 0) { // Variable BitRate (VBR) - minimum bitrate |
2005
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] = $ABRbitrateMinBitrate; |
2006
|
|
|
} |
2007
|
|
|
|
2008
|
|
|
// bytes $B1-$B3 Encoder delays |
2009
|
|
|
$EncoderDelays = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3)); |
2010
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12; |
2011
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['end_padding'] = $EncoderDelays & 0x000FFF; |
2012
|
|
|
|
2013
|
|
|
// byte $B4 Misc |
2014
|
|
|
$MiscByte = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1)); |
2015
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping'] = ($MiscByte & 0x03); |
2016
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode'] = ($MiscByte & 0x1C) >> 2; |
2017
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; |
2018
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; |
2019
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping']; |
2020
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode'] = LAMEmiscStereoModeLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode']); |
2021
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality'] = (bool) $ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality']; |
2022
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq'] = LAMEmiscSourceSampleFrequencyLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq']); |
2023
|
|
|
|
2024
|
|
|
// byte $B5 MP3 Gain |
2025
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); |
2026
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] = 1.5 * $ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain']; |
2027
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_factor'] = pow(2, ($ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] / 6)); |
2028
|
|
|
|
2029
|
|
|
// bytes $B6-$B7 Preset and surround info |
2030
|
|
|
$PresetSurroundBytes = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); |
2031
|
|
|
// Reserved = ($PresetSurroundBytes & 0xC000); |
2032
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info'] = ($PresetSurroundBytes & 0x3800); |
2033
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['surround_info'] = LAMEsurroundInfoLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info']); |
2034
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); |
2035
|
|
|
|
2036
|
|
|
// bytes $B8-$BB MusicLength |
2037
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4)); |
2038
|
|
|
$ExpectedNumberOfAudioBytes = (($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] > 0) ? $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] : $ThisFileInfo['mpeg']['audio']['VBR_bytes']); |
2039
|
|
|
|
2040
|
|
|
// bytes $BC-$BD MusicCRC |
2041
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['music_crc'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2)); |
2042
|
|
|
|
2043
|
|
|
// bytes $BE-$BF CRC-16 of Info Tag |
2044
|
|
|
$ThisFileInfo['mpeg']['audio']['LAME']['lame_tag_crc'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2)); |
2045
|
|
|
|
2046
|
|
|
|
2047
|
|
|
// LAME CBR |
2048
|
|
|
if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 1) { |
2049
|
|
|
|
2050
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; |
2051
|
|
View Code Duplication |
if (empty($ThisFileInfo['mpeg']['audio']['bitrate']) || ($ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] != 255)) { |
|
|
|
|
2052
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min']; |
2053
|
|
|
} |
2054
|
|
|
|
2055
|
|
|
} |
2056
|
|
|
|
2057
|
|
|
} |
2058
|
|
|
} |
2059
|
|
|
|
2060
|
|
|
} else { |
2061
|
|
|
|
2062
|
|
|
// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) |
2063
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; |
2064
|
|
|
if ($recursivesearch) { |
2065
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; |
2066
|
|
|
if (RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { |
2067
|
|
|
$recursivesearch = false; |
2068
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; |
2069
|
|
|
} |
2070
|
|
|
if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') { |
2071
|
|
|
$ThisFileInfo['warning'] .= "\n".'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; |
2072
|
|
|
} |
2073
|
|
|
} |
2074
|
|
|
|
2075
|
|
|
} |
2076
|
|
|
|
2077
|
|
|
} |
2078
|
|
|
|
2079
|
|
|
if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) { |
2080
|
|
|
if (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) { |
2081
|
|
|
$ThisFileInfo['warning'] .= "\n".'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; |
2082
|
|
|
} elseif ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { |
2083
|
|
|
$ThisFileInfo['warning'] .= "\n".'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)'; |
2084
|
|
|
} else { |
2085
|
|
|
$ThisFileInfo['warning'] .= "\n".'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; |
2086
|
|
|
} |
2087
|
|
|
} |
2088
|
|
|
|
2089
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) { |
2090
|
|
|
if (($offset == $ThisFileInfo['avdataoffset']) && empty($ThisFileInfo['mpeg']['audio']['VBR_frames'])) { |
2091
|
|
|
$framebytelength = FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); |
2092
|
|
|
if ($framebytelength > 0) { |
2093
|
|
|
$ThisFileInfo['mpeg']['audio']['framelength'] = $framebytelength; |
2094
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { |
|
|
|
|
2095
|
|
|
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 |
2096
|
|
|
$ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 12; |
2097
|
|
|
} else { |
2098
|
|
|
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 |
2099
|
|
|
$ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 144; |
2100
|
|
|
} |
2101
|
|
|
} else { |
2102
|
|
|
$ThisFileInfo['error'] .= "\n".'Error calculating frame length of free-format MP3 without Xing/LAME header'; |
2103
|
|
|
} |
2104
|
|
|
} |
2105
|
|
|
} |
2106
|
|
|
|
2107
|
|
|
if (($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && isset($ThisFileInfo['mpeg']['audio']['VBR_frames']) && ($ThisFileInfo['mpeg']['audio']['VBR_frames'] > 1)) { |
2108
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame |
2109
|
|
|
if (($ThisFileInfo['mpeg']['audio']['version'] == '1') && ($ThisFileInfo['mpeg']['audio']['layer'] == 'I')) { |
2110
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384)) / 1000; |
2111
|
|
|
} elseif ((($ThisFileInfo['mpeg']['audio']['version'] == '2') || ($ThisFileInfo['mpeg']['audio']['version'] == '2.5')) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'III')) { |
2112
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576)) / 1000; |
2113
|
|
|
} else { |
2114
|
|
|
$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152)) / 1000; |
2115
|
|
|
} |
2116
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['VBR_bitrate'] > 0) { |
|
|
|
|
2117
|
|
|
$ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; |
2118
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; // to avoid confusion |
2119
|
|
|
} |
2120
|
|
|
} |
2121
|
|
|
|
2122
|
|
|
// End variable-bitrate headers |
2123
|
|
|
//////////////////////////////////////////////////////////////////////////////////// |
2124
|
|
|
|
2125
|
|
|
if ($recursivesearch) { |
2126
|
|
|
|
2127
|
|
|
if (!RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { |
2128
|
|
|
return false; |
2129
|
|
|
} |
2130
|
|
|
|
2131
|
|
|
} |
2132
|
|
|
|
2133
|
|
|
|
2134
|
|
|
//if (false) { |
2135
|
|
|
// // experimental side info parsing section - not returning anything useful yet |
2136
|
|
|
// |
2137
|
|
|
// $SideInfoBitstream = BigEndian2Bin($SideInfoData); |
2138
|
|
|
// $SideInfoOffset = 0; |
2139
|
|
|
// |
2140
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
2141
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { |
2142
|
|
|
// // MPEG-1 (mono) |
2143
|
|
|
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); |
2144
|
|
|
// $SideInfoOffset += 9; |
2145
|
|
|
// $SideInfoOffset += 5; |
2146
|
|
|
// } else { |
2147
|
|
|
// // MPEG-1 (stereo, joint-stereo, dual-channel) |
2148
|
|
|
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); |
2149
|
|
|
// $SideInfoOffset += 9; |
2150
|
|
|
// $SideInfoOffset += 3; |
2151
|
|
|
// } |
2152
|
|
|
// } else { // 2 or 2.5 |
2153
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') { |
2154
|
|
|
// // MPEG-2, MPEG-2.5 (mono) |
2155
|
|
|
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); |
2156
|
|
|
// $SideInfoOffset += 8; |
2157
|
|
|
// $SideInfoOffset += 1; |
2158
|
|
|
// } else { |
2159
|
|
|
// // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) |
2160
|
|
|
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); |
2161
|
|
|
// $SideInfoOffset += 8; |
2162
|
|
|
// $SideInfoOffset += 2; |
2163
|
|
|
// } |
2164
|
|
|
// } |
2165
|
|
|
// |
2166
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
2167
|
|
|
// for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { |
2168
|
|
|
// for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { |
2169
|
|
|
// $ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2170
|
|
|
// $SideInfoOffset += 2; |
2171
|
|
|
// } |
2172
|
|
|
// } |
2173
|
|
|
// } |
2174
|
|
|
// for ($granule = 0; $granule < (($ThisFileInfo['mpeg']['audio']['version'] == '1') ? 2 : 1); $granule++) { |
2175
|
|
|
// for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { |
2176
|
|
|
// $ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); |
2177
|
|
|
// $SideInfoOffset += 12; |
2178
|
|
|
// $ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); |
2179
|
|
|
// $SideInfoOffset += 9; |
2180
|
|
|
// $ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); |
2181
|
|
|
// $SideInfoOffset += 8; |
2182
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
2183
|
|
|
// $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); |
2184
|
|
|
// $SideInfoOffset += 4; |
2185
|
|
|
// } else { |
2186
|
|
|
// $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); |
2187
|
|
|
// $SideInfoOffset += 9; |
2188
|
|
|
// } |
2189
|
|
|
// $ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2190
|
|
|
// $SideInfoOffset += 1; |
2191
|
|
|
// |
2192
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') { |
2193
|
|
|
// |
2194
|
|
|
// $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); |
2195
|
|
|
// $SideInfoOffset += 2; |
2196
|
|
|
// $ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2197
|
|
|
// $SideInfoOffset += 1; |
2198
|
|
|
// |
2199
|
|
|
// for ($region = 0; $region < 2; $region++) { |
2200
|
|
|
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); |
2201
|
|
|
// $SideInfoOffset += 5; |
2202
|
|
|
// } |
2203
|
|
|
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0; |
2204
|
|
|
// |
2205
|
|
|
// for ($window = 0; $window < 3; $window++) { |
2206
|
|
|
// $ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); |
2207
|
|
|
// $SideInfoOffset += 3; |
2208
|
|
|
// } |
2209
|
|
|
// |
2210
|
|
|
// } else { |
2211
|
|
|
// |
2212
|
|
|
// for ($region = 0; $region < 3; $region++) { |
2213
|
|
|
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); |
2214
|
|
|
// $SideInfoOffset += 5; |
2215
|
|
|
// } |
2216
|
|
|
// |
2217
|
|
|
// $ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); |
2218
|
|
|
// $SideInfoOffset += 4; |
2219
|
|
|
// $ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); |
2220
|
|
|
// $SideInfoOffset += 3; |
2221
|
|
|
// $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0; |
2222
|
|
|
// } |
2223
|
|
|
// |
2224
|
|
|
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
2225
|
|
|
// $ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2226
|
|
|
// $SideInfoOffset += 1; |
2227
|
|
|
// } |
2228
|
|
|
// $ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2229
|
|
|
// $SideInfoOffset += 1; |
2230
|
|
|
// $ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); |
2231
|
|
|
// $SideInfoOffset += 1; |
2232
|
|
|
// } |
2233
|
|
|
// } |
2234
|
|
|
//} |
2235
|
|
|
|
2236
|
|
|
return true; |
2237
|
|
|
} |
2238
|
|
|
|
2239
|
|
|
function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) { |
2240
|
|
|
for ($i = 0; $i < MPEG_VALID_CHECK_FRAMES; $i++) { |
2241
|
|
|
// check next MPEG_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch |
2242
|
|
|
if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) { |
2243
|
|
|
// end of file |
2244
|
|
|
return true; |
2245
|
|
|
} |
2246
|
|
|
|
2247
|
|
|
$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); |
2248
|
|
|
if (decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) { |
2249
|
|
View Code Duplication |
if ($ScanAsCBR) { |
|
|
|
|
2250
|
|
|
// force CBR mode, used for trying to pick out invalid audio streams with |
2251
|
|
|
// valid(?) VBR headers, or VBR streams with no VBR header |
2252
|
|
|
if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) { |
2253
|
|
|
return false; |
2254
|
|
|
} |
2255
|
|
|
} |
2256
|
|
|
|
2257
|
|
|
|
2258
|
|
|
// next frame is OK, get ready to check the one after that |
2259
|
|
|
if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { |
2260
|
|
|
$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; |
2261
|
|
|
} else { |
2262
|
|
|
$ThisFileInfo['error'] .= "\n".'Frame at offset ('.$offset.') is has an invalid frame length.'; |
2263
|
|
|
return false; |
2264
|
|
|
} |
2265
|
|
|
|
2266
|
|
|
} else { |
2267
|
|
|
|
2268
|
|
|
// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence |
2269
|
|
|
$ThisFileInfo['error'] .= "\n".'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; |
2270
|
|
|
|
2271
|
|
|
return false; |
2272
|
|
|
} |
2273
|
|
|
} |
2274
|
|
|
return true; |
2275
|
|
|
} |
2276
|
|
|
|
2277
|
|
|
function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) { |
2278
|
|
|
fseek($fd, $offset, SEEK_SET); |
2279
|
|
|
$MPEGaudioData = fread($fd, 32768); |
2280
|
|
|
|
2281
|
|
|
$SyncPattern1 = substr($MPEGaudioData, 0, 4); |
2282
|
|
|
// may be different pattern due to padding |
2283
|
|
|
$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3]; |
2284
|
|
View Code Duplication |
if ($SyncPattern2 === $SyncPattern1) { |
|
|
|
|
2285
|
|
|
$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3]; |
2286
|
|
|
} |
2287
|
|
|
|
2288
|
|
|
$framelength = false; |
2289
|
|
|
$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4); |
2290
|
|
|
$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4); |
2291
|
|
|
if ($framelength1 > 4) { |
2292
|
|
|
$framelength = $framelength1; |
2293
|
|
|
} |
2294
|
|
|
if (($framelength2 > 4) && ($framelength2 < $framelength1)) { |
2295
|
|
|
$framelength = $framelength2; |
2296
|
|
|
} |
2297
|
|
|
if (!$framelength) { |
|
|
|
|
2298
|
|
|
|
2299
|
|
|
// LAME 3.88 has a different value for modeextension on the first frame vs the rest |
2300
|
|
|
$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4); |
2301
|
|
|
$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4); |
2302
|
|
|
|
2303
|
|
|
if ($framelength1 > 4) { |
2304
|
|
|
$framelength = $framelength1; |
2305
|
|
|
} |
2306
|
|
|
if (($framelength2 > 4) && ($framelength2 < $framelength1)) { |
2307
|
|
|
$framelength = $framelength2; |
2308
|
|
|
} |
2309
|
|
|
if (!$framelength) { |
|
|
|
|
2310
|
|
|
$ThisFileInfo['error'] .= "\n".'Cannot find next free-format synch pattern ('.PrintHexBytes($SyncPattern1).' or '.PrintHexBytes($SyncPattern2).') after offset '.$offset; |
2311
|
|
|
return false; |
2312
|
|
|
} else { |
2313
|
|
|
$ThisFileInfo['warning'] .= "\n".'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; |
2314
|
|
|
$ThisFileInfo['audio']['codec'] = 'LAME'; |
2315
|
|
|
$ThisFileInfo['audio']['encoder'] = 'LAME3.88'; |
2316
|
|
|
$SyncPattern1 = substr($SyncPattern1, 0, 3); |
2317
|
|
|
$SyncPattern2 = substr($SyncPattern2, 0, 3); |
2318
|
|
|
} |
2319
|
|
|
} |
2320
|
|
|
|
2321
|
|
|
if ($deepscan) { |
2322
|
|
|
|
2323
|
|
|
$ActualFrameLengthValues = array(); |
2324
|
|
|
$nextoffset = $offset + $framelength; |
2325
|
|
|
while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) { |
2326
|
|
|
fseek($fd, $nextoffset - 1, SEEK_SET); |
2327
|
|
|
$NextSyncPattern = fread($fd, 6); |
2328
|
|
|
if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { |
2329
|
|
|
// good - found where expected |
2330
|
|
|
$ActualFrameLengthValues[] = $framelength; |
2331
|
|
View Code Duplication |
} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) { |
|
|
|
|
2332
|
|
|
// ok - found one byte earlier than expected (last frame wasn't padded, first frame was) |
2333
|
|
|
$ActualFrameLengthValues[] = ($framelength - 1); |
2334
|
|
|
$nextoffset--; |
2335
|
|
|
} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { |
2336
|
|
|
// ok - found one byte later than expected (last frame was padded, first frame wasn't) |
2337
|
|
|
$ActualFrameLengthValues[] = ($framelength + 1); |
2338
|
|
|
$nextoffset++; |
2339
|
|
|
} else { |
2340
|
|
|
$ThisFileInfo['error'] .= "\n".'Did not find expected free-format sync pattern at offset '.$nextoffset; |
2341
|
|
|
return false; |
2342
|
|
|
} |
2343
|
|
|
$nextoffset += $framelength; |
2344
|
|
|
} |
2345
|
|
View Code Duplication |
if (count($ActualFrameLengthValues) > 0) { |
|
|
|
|
2346
|
|
|
$framelength = round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)); |
2347
|
|
|
} |
2348
|
|
|
} |
2349
|
|
|
return $framelength; |
2350
|
|
|
} |
2351
|
|
|
|
2352
|
|
|
|
2353
|
|
|
function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) { |
2354
|
|
|
// looks for synch, decodes MPEG audio header |
2355
|
|
|
|
2356
|
|
|
fseek($fd, $avdataoffset, SEEK_SET); |
2357
|
|
|
$header = ''; |
2358
|
|
|
$SynchSeekOffset = 0; |
2359
|
|
|
|
2360
|
|
|
if (!defined('CONST_FF')) { |
2361
|
|
|
define('CONST_FF', chr(0xFF)); |
2362
|
|
|
define('CONST_E0', chr(0xE0)); |
2363
|
|
|
} |
2364
|
|
|
|
2365
|
|
|
static $MPEGaudioVersionLookup; |
2366
|
|
|
static $MPEGaudioLayerLookup; |
2367
|
|
|
static $MPEGaudioBitrateLookup; |
2368
|
|
|
if (empty($MPEGaudioVersionLookup)) { |
2369
|
|
|
$MPEGaudioVersionLookup = MPEGaudioVersionArray(); |
2370
|
|
|
$MPEGaudioLayerLookup = MPEGaudioLayerArray(); |
2371
|
|
|
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); |
2372
|
|
|
|
2373
|
|
|
} |
2374
|
|
|
|
2375
|
|
|
$header_len = strlen($header) - round(32768 / 2); |
2376
|
|
|
while (true) { |
2377
|
|
|
|
2378
|
|
|
if (($SynchSeekOffset > $header_len) && (($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) { |
2379
|
|
|
|
2380
|
|
|
if ($SynchSeekOffset > 131072) { |
2381
|
|
|
// if a synch's not found within the first 128k bytes, then give up |
2382
|
|
|
$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch within the first 131072 bytes'; |
2383
|
|
|
if (isset($ThisFileInfo['audio']['bitrate'])) { |
2384
|
|
|
unset($ThisFileInfo['audio']['bitrate']); |
2385
|
|
|
} |
2386
|
|
|
if (isset($ThisFileInfo['mpeg']['audio'])) { |
2387
|
|
|
unset($ThisFileInfo['mpeg']['audio']); |
2388
|
|
|
} |
2389
|
|
View Code Duplication |
if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { |
|
|
|
|
2390
|
|
|
unset($ThisFileInfo['mpeg']); |
2391
|
|
|
} |
2392
|
|
|
return false; |
2393
|
|
|
|
2394
|
|
|
} elseif ($header .= fread($fd, 32768)) { |
2395
|
|
|
|
2396
|
|
|
// great |
2397
|
|
|
$header_len = strlen($header) - round(32768 / 2); |
2398
|
|
|
|
2399
|
|
|
} else { |
2400
|
|
|
|
2401
|
|
|
$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file'; |
2402
|
|
|
if (isset($ThisFileInfo['audio']['bitrate'])) { |
2403
|
|
|
unset($ThisFileInfo['audio']['bitrate']); |
2404
|
|
|
} |
2405
|
|
|
if (isset($ThisFileInfo['mpeg']['audio'])) { |
2406
|
|
|
unset($ThisFileInfo['mpeg']['audio']); |
2407
|
|
|
} |
2408
|
|
View Code Duplication |
if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { |
|
|
|
|
2409
|
|
|
unset($ThisFileInfo['mpeg']); |
2410
|
|
|
} |
2411
|
|
|
return false; |
2412
|
|
|
|
2413
|
|
|
} |
2414
|
|
|
} |
2415
|
|
|
|
2416
|
|
|
if (($SynchSeekOffset + 1) >= strlen($header)) { |
2417
|
|
|
$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file'; |
2418
|
|
|
return false; |
2419
|
|
|
} |
2420
|
|
|
|
2421
|
|
|
if (($header[$SynchSeekOffset] == CONST_FF) && ($header[($SynchSeekOffset + 1)] > CONST_E0)) { // synch detected |
2422
|
|
|
|
2423
|
|
View Code Duplication |
if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { |
|
|
|
|
2424
|
|
|
$FirstFrameThisfileInfo = $ThisFileInfo; |
2425
|
|
|
$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; |
2426
|
|
|
if (!decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { |
2427
|
|
|
// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's |
2428
|
|
|
// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below |
2429
|
|
|
unset($FirstFrameThisfileInfo); |
2430
|
|
|
} |
2431
|
|
|
} |
2432
|
|
|
$dummy = $ThisFileInfo; // only overwrite real data if valid header found |
2433
|
|
|
|
2434
|
|
|
if (decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { |
2435
|
|
|
|
2436
|
|
|
$ThisFileInfo = $dummy; |
2437
|
|
|
$ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; |
2438
|
|
View Code Duplication |
switch ($ThisFileInfo['fileformat']) { |
|
|
|
|
2439
|
|
|
case '': |
2440
|
|
|
case 'id3': |
2441
|
|
|
case 'ape': |
2442
|
|
|
case 'mp3': |
2443
|
|
|
$ThisFileInfo['fileformat'] = 'mp3'; |
2444
|
|
|
$ThisFileInfo['audio']['dataformat'] = 'mp3'; |
2445
|
|
|
} |
2446
|
|
|
if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { |
2447
|
|
|
if (!CloseMatch($ThisFileInfo['audio']['bitrate'], $FirstFrameThisfileInfo['audio']['bitrate'], 1)) { |
2448
|
|
|
// If there is garbage data between a valid VBR header frame and a sequence |
2449
|
|
|
// of valid MPEG-audio frames the VBR data is no longer discarded. |
2450
|
|
|
$ThisFileInfo = $FirstFrameThisfileInfo; |
2451
|
|
|
$ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; |
|
|
|
|
2452
|
|
|
$ThisFileInfo['fileformat'] = 'mp3'; |
2453
|
|
|
$ThisFileInfo['audio']['dataformat'] = 'mp3'; |
2454
|
|
|
$dummy = $ThisFileInfo; |
2455
|
|
|
unset($dummy['mpeg']['audio']); |
2456
|
|
|
$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; |
2457
|
|
|
$GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; |
2458
|
|
|
if (decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { |
2459
|
|
|
|
2460
|
|
|
$ThisFileInfo = $dummy; |
2461
|
|
|
$ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; |
2462
|
|
|
$ThisFileInfo['warning'] .= "\n".'apparently-valid VBR header not used because could not find '.MPEG_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; |
2463
|
|
|
|
2464
|
|
|
} else { |
2465
|
|
|
|
2466
|
|
|
$ThisFileInfo['warning'] .= "\n".'using data from VBR header even though could not find '.MPEG_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; |
2467
|
|
|
|
2468
|
|
|
} |
2469
|
|
|
} |
2470
|
|
|
} |
2471
|
|
|
|
2472
|
|
View Code Duplication |
if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { |
|
|
|
|
2473
|
|
|
// VBR file with no VBR header |
2474
|
|
|
$BitrateHistogram = true; |
2475
|
|
|
} |
2476
|
|
|
|
2477
|
|
|
if ($BitrateHistogram) { |
2478
|
|
|
|
2479
|
|
|
$ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); |
2480
|
|
|
$ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); |
2481
|
|
|
|
2482
|
|
View Code Duplication |
if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { |
|
|
|
|
2483
|
|
|
if ($ThisFileInfo['mpeg']['audio']['layer'] == 'III') { |
2484
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 40=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 320=>0); |
2485
|
|
|
} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'II') { |
2486
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 320=>0, 384=>0); |
2487
|
|
|
} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { |
2488
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 64=>0, 96=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 288=>0, 320=>0, 352=>0, 384=>0, 416=>0, 448=>0); |
2489
|
|
|
} |
2490
|
|
|
} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') { |
2491
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 144=>0, 160=>0, 176=>0, 192=>0, 224=>0, 256=>0); |
2492
|
|
|
} else { |
2493
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8=>0, 16=>0, 24=>0, 32=>0, 40=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 144=>0, 160=>0); |
2494
|
|
|
} |
2495
|
|
|
|
2496
|
|
|
$dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); |
2497
|
|
|
$synchstartoffset = $ThisFileInfo['avdataoffset']; |
2498
|
|
|
|
2499
|
|
|
$FastMode = false; |
2500
|
|
|
while (decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) { |
2501
|
|
|
$FastMode = true; |
2502
|
|
|
$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; |
2503
|
|
|
|
2504
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++; |
2505
|
|
|
$ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++; |
2506
|
|
|
$ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++; |
2507
|
|
|
if (empty($dummy['mpeg']['audio']['framelength'])) { |
2508
|
|
|
$ThisFileInfo['warning'] .= "\n".'Invalid/missing framelength in histogram analysis - aborting'; |
2509
|
|
|
$synchstartoffset += 4; |
2510
|
|
|
// return false; |
2511
|
|
|
} |
2512
|
|
|
$synchstartoffset += $dummy['mpeg']['audio']['framelength']; |
2513
|
|
|
} |
2514
|
|
|
|
2515
|
|
|
$bittotal = 0; |
2516
|
|
|
$framecounter = 0; |
2517
|
|
View Code Duplication |
foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { |
|
|
|
|
2518
|
|
|
$framecounter += $bitratecount; |
2519
|
|
|
if ($bitratevalue != 'free') { |
2520
|
|
|
$bittotal += ($bitratevalue * $bitratecount); |
2521
|
|
|
} |
2522
|
|
|
} |
2523
|
|
|
if ($framecounter == 0) { |
2524
|
|
|
$ThisFileInfo['error'] .= "\n".'Corrupt MP3 file: framecounter == zero'; |
2525
|
|
|
return false; |
2526
|
|
|
} |
2527
|
|
|
$ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter; |
2528
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate'] = 1000 * ($bittotal / $framecounter); |
2529
|
|
|
|
2530
|
|
|
$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; |
2531
|
|
|
|
2532
|
|
|
|
2533
|
|
|
// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently |
2534
|
|
|
$distinct_bitrates = 0; |
2535
|
|
View Code Duplication |
foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { |
|
|
|
|
2536
|
|
|
if ($bitrate_count > 0) { |
2537
|
|
|
$distinct_bitrates++; |
2538
|
|
|
} |
2539
|
|
|
} |
2540
|
|
|
if ($distinct_bitrates > 1) { |
2541
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; |
2542
|
|
|
} else { |
2543
|
|
|
$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; |
2544
|
|
|
} |
2545
|
|
|
$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; |
2546
|
|
|
|
2547
|
|
|
} |
2548
|
|
|
|
2549
|
|
|
break; // exit while() |
2550
|
|
|
} |
2551
|
|
|
} |
2552
|
|
|
|
2553
|
|
|
$SynchSeekOffset++; |
2554
|
|
|
if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) { |
2555
|
|
|
// end of file/data |
2556
|
|
|
|
2557
|
|
|
if (empty($ThisFileInfo['mpeg']['audio'])) { |
2558
|
|
|
|
2559
|
|
|
$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file'; |
2560
|
|
|
if (isset($ThisFileInfo['audio']['bitrate'])) { |
2561
|
|
|
unset($ThisFileInfo['audio']['bitrate']); |
2562
|
|
|
} |
2563
|
|
|
if (isset($ThisFileInfo['mpeg']['audio'])) { |
2564
|
|
|
unset($ThisFileInfo['mpeg']['audio']); |
2565
|
|
|
} |
2566
|
|
View Code Duplication |
if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) { |
|
|
|
|
2567
|
|
|
unset($ThisFileInfo['mpeg']); |
2568
|
|
|
} |
2569
|
|
|
return false; |
2570
|
|
|
|
2571
|
|
|
} |
2572
|
|
|
break; |
2573
|
|
|
} |
2574
|
|
|
|
2575
|
|
|
} |
2576
|
|
|
$ThisFileInfo['audio']['bits_per_sample'] = 16; |
2577
|
|
|
$ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; |
2578
|
|
|
$ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode']; |
2579
|
|
|
$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; |
2580
|
|
|
return true; |
2581
|
|
|
} |
2582
|
|
|
|
2583
|
|
|
|
2584
|
|
|
function MPEGaudioVersionArray() { |
2585
|
|
|
static $MPEGaudioVersion = array('2.5', false, '2', '1'); |
2586
|
|
|
return $MPEGaudioVersion; |
2587
|
|
|
} |
2588
|
|
|
|
2589
|
|
|
function MPEGaudioLayerArray() { |
2590
|
|
|
static $MPEGaudioLayer = array(false, 'III', 'II', 'I'); |
2591
|
|
|
return $MPEGaudioLayer; |
2592
|
|
|
} |
2593
|
|
|
|
2594
|
|
|
function MPEGaudioBitrateArray() { |
2595
|
|
|
static $MPEGaudioBitrate; |
2596
|
|
|
if (empty($MPEGaudioBitrate)) { |
2597
|
|
|
$MPEGaudioBitrate['1']['I'] = array('free', 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448); |
2598
|
|
|
$MPEGaudioBitrate['1']['II'] = array('free', 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384); |
2599
|
|
|
$MPEGaudioBitrate['1']['III'] = array('free', 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320); |
2600
|
|
|
$MPEGaudioBitrate['2']['I'] = array('free', 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256); |
2601
|
|
|
$MPEGaudioBitrate['2']['II'] = array('free', 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160); |
2602
|
|
|
$MPEGaudioBitrate['2']['III'] = $MPEGaudioBitrate['2']['II']; |
2603
|
|
|
$MPEGaudioBitrate['2.5']['I'] = $MPEGaudioBitrate['2']['I']; |
2604
|
|
|
$MPEGaudioBitrate['2.5']['II'] = $MPEGaudioBitrate['2']['II']; |
2605
|
|
|
$MPEGaudioBitrate['2.5']['III'] = $MPEGaudioBitrate['2']['III']; |
2606
|
|
|
} |
2607
|
|
|
return $MPEGaudioBitrate; |
2608
|
|
|
} |
2609
|
|
|
|
2610
|
|
|
function MPEGaudioFrequencyArray() { |
2611
|
|
|
static $MPEGaudioFrequency; |
2612
|
|
|
if (empty($MPEGaudioFrequency)) { |
2613
|
|
|
$MPEGaudioFrequency['1'] = array(44100, 48000, 32000); |
2614
|
|
|
$MPEGaudioFrequency['2'] = array(22050, 24000, 16000); |
2615
|
|
|
$MPEGaudioFrequency['2.5'] = array(11025, 12000, 8000); |
2616
|
|
|
} |
2617
|
|
|
return $MPEGaudioFrequency; |
2618
|
|
|
} |
2619
|
|
|
|
2620
|
|
|
function MPEGaudioChannelModeArray() { |
2621
|
|
|
static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); |
2622
|
|
|
return $MPEGaudioChannelMode; |
2623
|
|
|
} |
2624
|
|
|
|
2625
|
|
|
function MPEGaudioModeExtensionArray() { |
2626
|
|
|
static $MPEGaudioModeExtension; |
2627
|
|
|
if (empty($MPEGaudioModeExtension)) { |
2628
|
|
|
$MPEGaudioModeExtension['I'] = array('4-31', '8-31', '12-31', '16-31'); |
2629
|
|
|
$MPEGaudioModeExtension['II'] = array('4-31', '8-31', '12-31', '16-31'); |
2630
|
|
|
$MPEGaudioModeExtension['III'] = array('', 'IS', 'MS', 'IS+MS'); |
2631
|
|
|
} |
2632
|
|
|
return $MPEGaudioModeExtension; |
2633
|
|
|
} |
2634
|
|
|
|
2635
|
|
|
function MPEGaudioEmphasisArray() { |
2636
|
|
|
static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); |
2637
|
|
|
return $MPEGaudioEmphasis; |
2638
|
|
|
} |
2639
|
|
|
|
2640
|
|
|
|
2641
|
|
|
function MPEGaudioHeaderBytesValid($head4) { |
2642
|
|
|
return MPEGaudioHeaderValid(MPEGaudioHeaderDecode($head4)); |
2643
|
|
|
} |
2644
|
|
|
|
2645
|
|
|
function MPEGaudioHeaderValid($rawarray, $echoerrors=false) { |
2646
|
|
|
|
2647
|
|
|
if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { |
2648
|
|
|
return false; |
2649
|
|
|
} |
2650
|
|
|
|
2651
|
|
|
static $MPEGaudioVersionLookup; |
2652
|
|
|
static $MPEGaudioLayerLookup; |
2653
|
|
|
static $MPEGaudioBitrateLookup; |
2654
|
|
|
static $MPEGaudioFrequencyLookup; |
2655
|
|
|
static $MPEGaudioChannelModeLookup; |
2656
|
|
|
static $MPEGaudioModeExtensionLookup; |
2657
|
|
|
static $MPEGaudioEmphasisLookup; |
2658
|
|
View Code Duplication |
if (empty($MPEGaudioVersionLookup)) { |
|
|
|
|
2659
|
|
|
$MPEGaudioVersionLookup = MPEGaudioVersionArray(); |
2660
|
|
|
$MPEGaudioLayerLookup = MPEGaudioLayerArray(); |
2661
|
|
|
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray(); |
2662
|
|
|
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray(); |
2663
|
|
|
$MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray(); |
2664
|
|
|
$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray(); |
2665
|
|
|
$MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray(); |
2666
|
|
|
} |
2667
|
|
|
|
2668
|
|
|
if (isset($MPEGaudioVersionLookup[$rawarray['version']])) { |
2669
|
|
|
$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']]; |
2670
|
|
|
} else { |
2671
|
|
|
if ($echoerrors) { |
2672
|
|
|
echo "\n".'invalid Version ('.$rawarray['version'].')'; |
2673
|
|
|
} |
2674
|
|
|
return false; |
2675
|
|
|
} |
2676
|
|
|
if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) { |
2677
|
|
|
$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']]; |
2678
|
|
|
} else { |
2679
|
|
|
if ($echoerrors) { |
2680
|
|
|
echo "\n".'invalid Layer ('.$rawarray['layer'].')'; |
2681
|
|
|
} |
2682
|
|
|
return false; |
2683
|
|
|
} |
2684
|
|
View Code Duplication |
if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) { |
|
|
|
|
2685
|
|
|
if ($echoerrors) { |
2686
|
|
|
echo "\n".'invalid Bitrate ('.$rawarray['bitrate'].')'; |
2687
|
|
|
} |
2688
|
|
|
if ($rawarray['bitrate'] == 15) { |
2689
|
|
|
// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0 |
2690
|
|
|
// let it go through here otherwise file will not be identified |
2691
|
|
|
} else { |
2692
|
|
|
return false; |
2693
|
|
|
} |
2694
|
|
|
} |
2695
|
|
View Code Duplication |
if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) { |
|
|
|
|
2696
|
|
|
if ($echoerrors) { |
2697
|
|
|
echo "\n".'invalid Frequency ('.$rawarray['sample_rate'].')'; |
2698
|
|
|
} |
2699
|
|
|
return false; |
2700
|
|
|
} |
2701
|
|
View Code Duplication |
if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) { |
|
|
|
|
2702
|
|
|
if ($echoerrors) { |
2703
|
|
|
echo "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')'; |
2704
|
|
|
} |
2705
|
|
|
return false; |
2706
|
|
|
} |
2707
|
|
View Code Duplication |
if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) { |
|
|
|
|
2708
|
|
|
if ($echoerrors) { |
2709
|
|
|
echo "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')'; |
2710
|
|
|
} |
2711
|
|
|
return false; |
2712
|
|
|
} |
2713
|
|
View Code Duplication |
if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) { |
|
|
|
|
2714
|
|
|
if ($echoerrors) { |
2715
|
|
|
echo "\n".'invalid Emphasis ('.$rawarray['emphasis'].')'; |
2716
|
|
|
} |
2717
|
|
|
return false; |
2718
|
|
|
} |
2719
|
|
|
// These are just either set or not set, you can't mess that up :) |
2720
|
|
|
// $rawarray['protection']; |
2721
|
|
|
// $rawarray['padding']; |
2722
|
|
|
// $rawarray['private']; |
2723
|
|
|
// $rawarray['copyright']; |
2724
|
|
|
// $rawarray['original']; |
2725
|
|
|
|
2726
|
|
|
return true; |
2727
|
|
|
} |
2728
|
|
|
|
2729
|
|
View Code Duplication |
function MPEGaudioHeaderDecode($Header4Bytes) { |
|
|
|
|
2730
|
|
|
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM |
2731
|
|
|
// A - Frame sync (all bits set) |
2732
|
|
|
// B - MPEG Audio version ID |
2733
|
|
|
// C - Layer description |
2734
|
|
|
// D - Protection bit |
2735
|
|
|
// E - Bitrate index |
2736
|
|
|
// F - Sampling rate frequency index |
2737
|
|
|
// G - Padding bit |
2738
|
|
|
// H - Private bit |
2739
|
|
|
// I - Channel Mode |
2740
|
|
|
// J - Mode extension (Only if Joint stereo) |
2741
|
|
|
// K - Copyright |
2742
|
|
|
// L - Original |
2743
|
|
|
// M - Emphasis |
2744
|
|
|
|
2745
|
|
|
if (strlen($Header4Bytes) != 4) { |
2746
|
|
|
return false; |
2747
|
|
|
} |
2748
|
|
|
|
2749
|
|
|
$MPEGrawHeader['synch'] = (BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4; |
|
|
|
|
2750
|
|
|
$MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3; // BB |
2751
|
|
|
$MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x06) >> 1; // CC |
2752
|
|
|
$MPEGrawHeader['protection'] = (ord($Header4Bytes[1]) & 0x01); // D |
2753
|
|
|
$MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE |
2754
|
|
|
$MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0x0C) >> 2; // FF |
2755
|
|
|
$MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x02) >> 1; // G |
2756
|
|
|
$MPEGrawHeader['private'] = (ord($Header4Bytes[2]) & 0x01); // H |
2757
|
|
|
$MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II |
2758
|
|
|
$MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; // JJ |
2759
|
|
|
$MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x08) >> 3; // K |
2760
|
|
|
$MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x04) >> 2; // L |
2761
|
|
|
$MPEGrawHeader['emphasis'] = (ord($Header4Bytes[3]) & 0x03); // MM |
2762
|
|
|
|
2763
|
|
|
return $MPEGrawHeader; |
2764
|
|
|
} |
2765
|
|
|
|
2766
|
|
|
function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { |
2767
|
|
|
static $AudioFrameLengthCache = array(); |
2768
|
|
|
|
2769
|
|
|
if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) { |
2770
|
|
|
$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false; |
2771
|
|
View Code Duplication |
if ($bitrate != 'free') { |
|
|
|
|
2772
|
|
|
|
2773
|
|
|
if ($version == '1') { |
2774
|
|
|
|
2775
|
|
|
if ($layer == 'I') { |
2776
|
|
|
|
2777
|
|
|
// For Layer I slot is 32 bits long |
2778
|
|
|
$FrameLengthCoefficient = 48; |
2779
|
|
|
$SlotLength = 4; |
2780
|
|
|
|
2781
|
|
|
} else { // Layer II / III |
2782
|
|
|
|
2783
|
|
|
// for Layer II and Layer III slot is 8 bits long. |
2784
|
|
|
$FrameLengthCoefficient = 144; |
2785
|
|
|
$SlotLength = 1; |
2786
|
|
|
|
2787
|
|
|
} |
2788
|
|
|
|
2789
|
|
|
} else { // MPEG-2 / MPEG-2.5 |
2790
|
|
|
|
2791
|
|
|
if ($layer == 'I') { |
2792
|
|
|
|
2793
|
|
|
// For Layer I slot is 32 bits long |
2794
|
|
|
$FrameLengthCoefficient = 24; |
2795
|
|
|
$SlotLength = 4; |
2796
|
|
|
|
2797
|
|
|
} elseif ($layer == 'II') { |
2798
|
|
|
|
2799
|
|
|
// for Layer II and Layer III slot is 8 bits long. |
2800
|
|
|
$FrameLengthCoefficient = 144; |
2801
|
|
|
$SlotLength = 1; |
2802
|
|
|
|
2803
|
|
|
} else { // III |
2804
|
|
|
|
2805
|
|
|
// for Layer II and Layer III slot is 8 bits long. |
2806
|
|
|
$FrameLengthCoefficient = 72; |
2807
|
|
|
$SlotLength = 1; |
2808
|
|
|
|
2809
|
|
|
} |
2810
|
|
|
|
2811
|
|
|
} |
2812
|
|
|
|
2813
|
|
|
// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding |
2814
|
|
|
// http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068 |
2815
|
|
|
// -> [Finding the next frame synch] on www.r3mix.net forums if the above link goes dead |
2816
|
|
|
if ($samplerate > 0) { |
2817
|
|
|
$NewFramelength = ($FrameLengthCoefficient * $bitrate * 1000) / $samplerate; |
2818
|
|
|
$NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer II/III, 4 bytes for Layer I) |
2819
|
|
|
if ($padding) { |
2820
|
|
|
$NewFramelength += $SlotLength; |
2821
|
|
|
} |
2822
|
|
|
$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength; |
2823
|
|
|
} |
2824
|
|
|
} |
2825
|
|
|
} |
2826
|
|
|
return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; |
2827
|
|
|
} |
2828
|
|
|
|
2829
|
|
View Code Duplication |
function LAMEvbrMethodLookup($VBRmethodID) { |
|
|
|
|
2830
|
|
|
static $LAMEvbrMethodLookup = array(); |
2831
|
|
|
if (empty($LAMEvbrMethodLookup)) { |
2832
|
|
|
$LAMEvbrMethodLookup[0x00] = 'unknown'; |
2833
|
|
|
$LAMEvbrMethodLookup[0x01] = 'cbr'; |
2834
|
|
|
$LAMEvbrMethodLookup[0x02] = 'abr'; |
2835
|
|
|
$LAMEvbrMethodLookup[0x03] = 'vbr-old / vbr-rh'; |
2836
|
|
|
$LAMEvbrMethodLookup[0x04] = 'vbr-mtrh'; |
2837
|
|
|
$LAMEvbrMethodLookup[0x05] = 'vbr-new / vbr-mt'; |
2838
|
|
|
} |
2839
|
|
|
return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); |
2840
|
|
|
} |
2841
|
|
|
|
2842
|
|
|
function LAMEmiscStereoModeLookup($StereoModeID) { |
2843
|
|
|
static $LAMEmiscStereoModeLookup = array(); |
2844
|
|
|
if (empty($LAMEmiscStereoModeLookup)) { |
2845
|
|
|
$LAMEmiscStereoModeLookup[0] = 'mono'; |
2846
|
|
|
$LAMEmiscStereoModeLookup[1] = 'stereo'; |
2847
|
|
|
$LAMEmiscStereoModeLookup[2] = 'dual'; |
2848
|
|
|
$LAMEmiscStereoModeLookup[3] = 'joint'; |
2849
|
|
|
$LAMEmiscStereoModeLookup[4] = 'forced'; |
2850
|
|
|
$LAMEmiscStereoModeLookup[5] = 'auto'; |
2851
|
|
|
$LAMEmiscStereoModeLookup[6] = 'intensity'; |
2852
|
|
|
$LAMEmiscStereoModeLookup[7] = 'other'; |
2853
|
|
|
} |
2854
|
|
|
return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); |
2855
|
|
|
} |
2856
|
|
|
|
2857
|
|
|
function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { |
2858
|
|
|
static $LAMEmiscSourceSampleFrequencyLookup = array(); |
2859
|
|
|
if (empty($LAMEmiscSourceSampleFrequencyLookup)) { |
2860
|
|
|
$LAMEmiscSourceSampleFrequencyLookup[0] = '<= 32 kHz'; |
2861
|
|
|
$LAMEmiscSourceSampleFrequencyLookup[1] = '44.1 kHz'; |
2862
|
|
|
$LAMEmiscSourceSampleFrequencyLookup[2] = '48 kHz'; |
2863
|
|
|
$LAMEmiscSourceSampleFrequencyLookup[3] = '> 48kHz'; |
2864
|
|
|
} |
2865
|
|
|
return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); |
2866
|
|
|
} |
2867
|
|
|
|
2868
|
|
|
function LAMEsurroundInfoLookup($SurroundInfoID) { |
2869
|
|
|
static $LAMEsurroundInfoLookup = array(); |
2870
|
|
|
if (empty($LAMEsurroundInfoLookup)) { |
2871
|
|
|
$LAMEsurroundInfoLookup[0] = 'no surround info'; |
2872
|
|
|
$LAMEsurroundInfoLookup[1] = 'DPL encoding'; |
2873
|
|
|
$LAMEsurroundInfoLookup[2] = 'DPL2 encoding'; |
2874
|
|
|
$LAMEsurroundInfoLookup[3] = 'Ambisonic encoding'; |
2875
|
|
|
} |
2876
|
|
|
return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); |
2877
|
|
|
} |
2878
|
|
|
|
2879
|
|
|
for ($i = 0x00; $i <= 0xFF; $i++) { |
2880
|
|
|
$head4 = "\xFF\xFE".chr($i)."\x00"; |
2881
|
|
|
$isvalid = MPEGaudioHeaderBytesValid($head4); |
2882
|
|
|
echo '<div style="color: '.($isvalid ? 'green' : 'red').';">'.str_pad(strtoupper(dechex($i)), 2, '0', STR_PAD_LEFT).' = '.htmlentities(chr($i)).' = '.($isvalid ? 'valid' : 'INVALID').'</div>'; |
2883
|
|
|
} |
2884
|
|
|
|
This check looks for functions that have already been defined in other files.
Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the
@ignore
annotation.See also the PhpDoc documentation for @ignore.