PageRenderTime 14ms CodeModel.GetById 20ms app.highlight 106ms RepoModel.GetById 1ms app.codeStats 2ms

/demos/demo.mp3header.php

https://bitbucket.org/holyfield/getid3
PHP | 2889 lines | 2146 code | 385 blank | 358 comment | 648 complexity | 9c2611812973585277a5e7eb0e8b02ca MD5 | raw file

Large files files are truncated, but you can click here to view the full 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'

Large files files are truncated, but you can click here to view the full file