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