PageRenderTime 10ms CodeModel.GetById 96ms app.highlight 96ms RepoModel.GetById 1ms app.codeStats 1ms

/demos/demo.mp3header.php

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