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