2015-09-04 03:59:46 +02:00
< ? php
2021-02-14 03:39:15 +01:00
if ( ! function_exists ( 'PrintHexBytes' )) {
function PrintHexBytes ( $string )
{
$returnstring = '' ;
for ( $i = 0 ; $i < strlen ( $string ); $i ++ ) {
$returnstring .= str_pad ( dechex ( ord ( substr ( $string , $i , 1 ))), 2 , '0' , STR_PAD_LEFT ) . ' ' ;
}
return $returnstring ;
}
}
if ( ! function_exists ( 'PrintTextBytes' )) {
function PrintTextBytes ( $string )
{
$returnstring = '' ;
for ( $i = 0 ; $i < strlen ( $string ); $i ++ ) {
if ( ord ( substr ( $string , $i , 1 )) <= 31 ) {
$returnstring .= ' ' ;
} else {
$returnstring .= ' ' . substr ( $string , $i , 1 ) . ' ' ;
}
}
return $returnstring ;
}
}
if ( ! function_exists ( 'table_var_dump' )) {
function table_var_dump ( $variable )
{
$returnstring = '' ;
switch ( gettype ( $variable )) {
case 'array' :
$returnstring .= '<table border="1" cellspacing="0" cellpadding="2">' ;
foreach ( $variable as $key => $value ) {
$returnstring .= '<tr><td valign="top"><b>' . str_replace ( chr ( 0 ), ' ' , $key ) . '</b></td>' ;
$returnstring .= '<td valign="top">' . gettype ( $value );
if ( is_array ( $value )) {
$returnstring .= ' (' . count ( $value ) . ')' ;
} elseif ( is_string ( $value )) {
$returnstring .= ' (' . strlen ( $value ) . ')' ;
}
if (( $key == 'data' ) && isset ( $variable [ 'image_mime' ]) && isset ( $variable [ 'dataoffset' ])) {
require_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php' ;
$imageinfo = [];
if ( $imagechunkcheck = GetDataImageSize ( $value , $imageinfo )) {
$DumpedImageSRC = ( ! empty ( $_REQUEST [ 'filename' ]) ? $_REQUEST [ 'filename' ] : '.getid3' ) . '.' . $variable [ 'dataoffset' ] . '.' . ImageTypesLookup ( $imagechunkcheck [ 2 ]);
if ( $tempimagefile = fopen ( $DumpedImageSRC , 'wb' )) {
fwrite ( $tempimagefile , $value );
fclose ( $tempimagefile );
}
$returnstring .= '</td><td><img src="' . $DumpedImageSRC . '" width="' . $imagechunkcheck [ 0 ] . '" height="' . $imagechunkcheck [ 1 ] . '"></td></tr>' ;
} else {
$returnstring .= '</td><td><i>invalid image data</i></td></tr>' ;
}
} else {
$returnstring .= '</td><td>' . table_var_dump ( $value ) . '</td></tr>' ;
}
}
$returnstring .= '</table>' ;
break ;
case 'boolean' :
$returnstring .= ( $variable ? 'TRUE' : 'FALSE' );
break ;
case 'integer' :
case 'double' :
case 'float' :
$returnstring .= $variable ;
break ;
case 'object' :
case 'null' :
$returnstring .= string_var_dump ( $variable );
break ;
case 'string' :
$variable = str_replace ( chr ( 0 ), ' ' , $variable );
$varlen = strlen ( $variable );
for ( $i = 0 ; $i < $varlen ; $i ++ ) {
if ( preg_match ( '#[' . chr ( 0x0A ) . chr ( 0x0D ) . ' -;0-9A-Za-z]#' , $variable [ $i ])) {
$returnstring .= $variable [ $i ];
} else {
$returnstring .= '&#' . str_pad ( ord ( $variable [ $i ]), 3 , '0' , STR_PAD_LEFT ) . ';' ;
}
}
$returnstring = nl2br ( $returnstring );
break ;
default :
require_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php' ;
$imageinfo = [];
if (( $imagechunkcheck = GetDataImageSize ( substr ( $variable , 0 , 32768 ), $imageinfo )) && ( $imagechunkcheck [ 2 ] >= 1 ) && ( $imagechunkcheck [ 2 ] <= 3 )) {
$returnstring .= '<table border="1" cellspacing="0" cellpadding="2">' ;
$returnstring .= '<tr><td><b>type</b></td><td>' . ImageTypesLookup ( $imagechunkcheck [ 2 ]) . '</td></tr>' ;
$returnstring .= '<tr><td><b>width</b></td><td>' . number_format ( $imagechunkcheck [ 0 ]) . ' px</td></tr>' ;
$returnstring .= '<tr><td><b>height</b></td><td>' . number_format ( $imagechunkcheck [ 1 ]) . ' px</td></tr>' ;
$returnstring .= '<tr><td><b>size</b></td><td>' . number_format ( strlen ( $variable )) . ' bytes</td></tr></table>' ;
} else {
$returnstring .= nl2br ( htmlspecialchars ( str_replace ( chr ( 0 ), ' ' , $variable )));
}
break ;
}
return $returnstring ;
}
}
if ( ! function_exists ( 'string_var_dump' )) {
function string_var_dump ( $variable )
{
if ( version_compare ( PHP_VERSION , '4.3.0' , '>=' )) {
return print_r ( $variable , true );
}
ob_start ();
var_dump ( $variable );
$dumpedvariable = ob_get_contents ();
ob_end_clean ();
return $dumpedvariable ;
}
}
if ( ! function_exists ( 'fileextension' )) {
function fileextension ( $filename , $numextensions = 1 )
{
if ( strstr ( $filename , '.' )) {
$reversedfilename = strrev ( $filename );
$offset = 0 ;
for ( $i = 0 ; $i < $numextensions ; $i ++ ) {
$offset = strpos ( $reversedfilename , '.' , $offset + 1 );
if ( $offset === false ) {
return '' ;
}
}
return strrev ( substr ( $reversedfilename , 0 , $offset ));
}
return '' ;
}
}
if ( ! function_exists ( 'RemoveAccents' )) {
function RemoveAccents ( $string )
{
// return strtr($string, '¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ', 'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
// Revised version by marksteward@hotmail.com
return strtr ( strtr ( $string , 'ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ' , 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy' ), [ 'Þ' => 'TH' , 'þ' => 'th' , 'Ð' => 'DH' , 'ð' => 'dh' , 'ß' => 'ss' , ' ' => 'OE' , ' ' => 'oe' , 'Æ' => 'AE' , 'æ' => 'ae' , 'µ' => 'u' ]);
}
}
if ( ! function_exists ( 'MoreNaturalSort' )) {
function MoreNaturalSort ( $ar1 , $ar2 )
{
if ( $ar1 === $ar2 ) {
return 0 ;
}
$len1 = strlen ( $ar1 );
$len2 = strlen ( $ar2 );
$shortest = min ( $len1 , $len2 );
if ( substr ( $ar1 , 0 , $shortest ) === substr ( $ar2 , 0 , $shortest )) {
// the shorter argument is the beginning of the longer one, like "str" and "string"
if ( $len1 < $len2 ) {
return - 1 ;
} elseif ( $len1 > $len2 ) {
return 1 ;
}
return 0 ;
}
$ar1 = RemoveAccents ( strtolower ( trim ( $ar1 )));
$ar2 = RemoveAccents ( strtolower ( trim ( $ar2 )));
$translatearray = [ '\'' => '' , '"' => '' , '_' => ' ' , '(' => '' , ')' => '' , '-' => ' ' , ' ' => ' ' , '.' => '' , ',' => '' ];
foreach ( $translatearray as $key => $val ) {
$ar1 = str_replace ( $key , $val , $ar1 );
$ar2 = str_replace ( $key , $val , $ar2 );
}
if ( $ar1 < $ar2 ) {
return - 1 ;
} elseif ( $ar1 > $ar2 ) {
return 1 ;
}
return 0 ;
}
}
if ( ! function_exists ( 'trunc' )) {
function trunc ( $floatnumber )
{
// truncates a floating-point number at the decimal point
// returns int (if possible, otherwise float)
if ( $floatnumber >= 1 ) {
$truncatednumber = floor ( $floatnumber );
} elseif ( $floatnumber <= - 1 ) {
$truncatednumber = ceil ( $floatnumber );
} else {
$truncatednumber = 0 ;
}
if ( $truncatednumber <= pow ( 2 , 30 )) {
$truncatednumber = ( int ) $truncatednumber ;
}
return $truncatednumber ;
}
}
if ( ! function_exists ( 'CastAsInt' )) {
function CastAsInt ( $floatnum )
{
// convert to float if not already
$floatnum = ( float ) $floatnum ;
// convert a float to type int, only if possible
if ( trunc ( $floatnum ) == $floatnum ) {
// it's not floating point
if ( $floatnum <= pow ( 2 , 30 )) {
// it's within int range
$floatnum = ( int ) $floatnum ;
}
}
return $floatnum ;
}
}
if ( ! function_exists ( 'getmicrotime' )) {
function getmicrotime ()
{
list ( $usec , $sec ) = explode ( ' ' , microtime ());
return ( float ) $usec + ( float ) $sec ;
}
}
if ( ! function_exists ( 'DecimalBinary2Float' )) {
function DecimalBinary2Float ( $binarynumerator )
{
$numerator = Bin2Dec ( $binarynumerator );
$denominator = Bin2Dec ( str_repeat ( '1' , strlen ( $binarynumerator )));
return $numerator / $denominator ;
}
}
if ( ! function_exists ( 'NormalizeBinaryPoint' )) {
function NormalizeBinaryPoint ( $binarypointnumber , $maxbits = 52 )
{
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
if ( strpos ( $binarypointnumber , '.' ) === false ) {
$binarypointnumber = '0.' . $binarypointnumber ;
} elseif ( $binarypointnumber [ 0 ] == '.' ) {
$binarypointnumber = '0' . $binarypointnumber ;
}
$exponent = 0 ;
while (( $binarypointnumber [ 0 ] != '1' ) || ( substr ( $binarypointnumber , 1 , 1 ) != '.' )) {
if ( substr ( $binarypointnumber , 1 , 1 ) == '.' ) {
$exponent -- ;
$binarypointnumber = substr ( $binarypointnumber , 2 , 1 ) . '.' . substr ( $binarypointnumber , 3 );
} else {
$pointpos = strpos ( $binarypointnumber , '.' );
$exponent += ( $pointpos - 1 );
$binarypointnumber = str_replace ( '.' , '' , $binarypointnumber );
$binarypointnumber = $binarypointnumber [ 0 ] . '.' . substr ( $binarypointnumber , 1 );
}
}
$binarypointnumber = str_pad ( substr ( $binarypointnumber , 0 , $maxbits + 2 ), $maxbits + 2 , '0' , STR_PAD_RIGHT );
return [ 'normalized' => $binarypointnumber , 'exponent' => ( int ) $exponent ];
}
}
if ( ! function_exists ( 'Float2BinaryDecimal' )) {
function Float2BinaryDecimal ( $floatvalue )
{
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
$maxbits = 128 ; // to how many bits of precision should the calculations be taken?
$intpart = trunc ( $floatvalue );
$floatpart = abs ( $floatvalue - $intpart );
$pointbitstring = '' ;
while (( $floatpart != 0 ) && ( strlen ( $pointbitstring ) < $maxbits )) {
$floatpart *= 2 ;
$pointbitstring .= ( string ) trunc ( $floatpart );
$floatpart -= trunc ( $floatpart );
}
$binarypointnumber = decbin ( $intpart ) . '.' . $pointbitstring ;
return $binarypointnumber ;
}
}
if ( ! function_exists ( 'Float2String' )) {
function Float2String ( $floatvalue , $bits )
{
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
switch ( $bits ) {
case 32 :
$exponentbits = 8 ;
$fractionbits = 23 ;
break ;
case 64 :
$exponentbits = 11 ;
$fractionbits = 52 ;
break ;
default :
return false ;
break ;
}
if ( $floatvalue >= 0 ) {
$signbit = '0' ;
} else {
$signbit = '1' ;
}
$normalizedbinary = NormalizeBinaryPoint ( Float2BinaryDecimal ( $floatvalue ), $fractionbits );
$biasedexponent = pow ( 2 , $exponentbits - 1 ) - 1 + $normalizedbinary [ 'exponent' ]; // (127 or 1023) +/- exponent
$exponentbitstring = str_pad ( decbin ( $biasedexponent ), $exponentbits , '0' , STR_PAD_LEFT );
$fractionbitstring = str_pad ( substr ( $normalizedbinary [ 'normalized' ], 2 ), $fractionbits , '0' , STR_PAD_RIGHT );
return BigEndian2String ( Bin2Dec ( $signbit . $exponentbitstring . $fractionbitstring ), $bits % 8 , false );
}
}
if ( ! function_exists ( 'LittleEndian2Float' )) {
function LittleEndian2Float ( $byteword )
{
return BigEndian2Float ( strrev ( $byteword ));
}
}
if ( ! function_exists ( 'BigEndian2Float' )) {
function BigEndian2Float ( $byteword )
{
// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
// http://www.psc.edu/general/software/packages/ieee/ieee.html
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
$bitword = BigEndian2Bin ( $byteword );
$signbit = $bitword [ 0 ];
switch ( strlen ( $byteword ) * 8 ) {
case 32 :
$exponentbits = 8 ;
$fractionbits = 23 ;
break ;
case 64 :
$exponentbits = 11 ;
$fractionbits = 52 ;
break ;
case 80 :
$exponentbits = 16 ;
$fractionbits = 64 ;
break ;
default :
return false ;
break ;
}
$exponentstring = substr ( $bitword , 1 , $exponentbits - 1 );
$fractionstring = substr ( $bitword , $exponentbits , $fractionbits );
$exponent = Bin2Dec ( $exponentstring );
$fraction = Bin2Dec ( $fractionstring );
if (( $exponentbits == 16 ) && ( $fractionbits == 64 )) {
// 80-bit
// As used in Apple AIFF for sample_rate
// A bit of a hack, but it works ;)
return pow ( 2 , ( $exponent - 16382 )) * DecimalBinary2Float ( $fractionstring );
}
if (( $exponent == ( pow ( 2 , $exponentbits ) - 1 )) && ( $fraction != 0 )) {
// Not a Number
$floatvalue = false ;
} elseif (( $exponent == ( pow ( 2 , $exponentbits ) - 1 )) && ( $fraction == 0 )) {
if ( $signbit == '1' ) {
$floatvalue = '-infinity' ;
} else {
$floatvalue = '+infinity' ;
}
} elseif (( $exponent == 0 ) && ( $fraction == 0 )) {
if ( $signbit == '1' ) {
$floatvalue = - 0 ;
} else {
$floatvalue = 0 ;
}
$floatvalue = ( $signbit ? 0 : - 0 );
} elseif (( $exponent == 0 ) && ( $fraction != 0 )) {
// These are 'unnormalized' values
$floatvalue = pow ( 2 , ( - 1 * ( pow ( 2 , $exponentbits - 1 ) - 2 ))) * DecimalBinary2Float ( $fractionstring );
if ( $signbit == '1' ) {
$floatvalue *= - 1 ;
}
} elseif ( $exponent != 0 ) {
$floatvalue = pow ( 2 , ( $exponent - ( pow ( 2 , $exponentbits - 1 ) - 1 ))) * ( 1 + DecimalBinary2Float ( $fractionstring ));
if ( $signbit == '1' ) {
$floatvalue *= - 1 ;
}
}
return ( float ) $floatvalue ;
}
}
if ( ! function_exists ( 'BigEndian2Int' )) {
function BigEndian2Int ( $byteword , $synchsafe = false , $signed = false )
{
$intvalue = 0 ;
$bytewordlen = strlen ( $byteword );
for ( $i = 0 ; $i < $bytewordlen ; $i ++ ) {
if ( $synchsafe ) { // disregard MSB, effectively 7-bit bytes
$intvalue = $intvalue | ( ord ( $byteword [ $i ]) & 0x7F ) << (( $bytewordlen - 1 - $i ) * 7 );
} else {
$intvalue += ord ( $byteword [ $i ]) * pow ( 256 , ( $bytewordlen - 1 - $i ));
}
}
if ( $signed && ! $synchsafe ) {
// synchsafe ints are not allowed to be signed
switch ( $bytewordlen ) {
case 1 :
case 2 :
case 3 :
case 4 :
$signmaskbit = 0x80 << ( 8 * ( $bytewordlen - 1 ));
if ( $intvalue & $signmaskbit ) {
$intvalue = 0 - ( $intvalue & ( $signmaskbit - 1 ));
}
break ;
default :
die ( 'ERROR: Cannot have signed integers larger than 32-bits in BigEndian2Int()' );
break ;
}
}
return CastAsInt ( $intvalue );
}
}
if ( ! function_exists ( 'LittleEndian2Int' )) {
function LittleEndian2Int ( $byteword , $signed = false )
{
return BigEndian2Int ( strrev ( $byteword ), false , $signed );
}
}
if ( ! function_exists ( 'BigEndian2Bin' )) {
function BigEndian2Bin ( $byteword )
{
$binvalue = '' ;
$bytewordlen = strlen ( $byteword );
for ( $i = 0 ; $i < $bytewordlen ; $i ++ ) {
$binvalue .= str_pad ( decbin ( ord ( $byteword [ $i ])), 8 , '0' , STR_PAD_LEFT );
}
return $binvalue ;
}
}
if ( ! function_exists ( 'BigEndian2String' )) {
function BigEndian2String ( $number , $minbytes = 1 , $synchsafe = false , $signed = false )
{
if ( $number < 0 ) {
return false ;
}
$maskbyte = (( $synchsafe || $signed ) ? 0x7F : 0xFF );
$intstring = '' ;
if ( $signed ) {
if ( $minbytes > 4 ) {
die ( 'ERROR: Cannot have signed integers larger than 32-bits in BigEndian2String()' );
}
$number = $number & ( 0x80 << ( 8 * ( $minbytes - 1 )));
}
while ( $number != 0 ) {
$quotient = ( $number / ( $maskbyte + 1 ));
$intstring = chr ( ceil (( $quotient - floor ( $quotient )) * $maskbyte )) . $intstring ;
$number = floor ( $quotient );
}
return str_pad ( $intstring , $minbytes , chr ( 0 ), STR_PAD_LEFT );
}
}
if ( ! function_exists ( 'Dec2Bin' )) {
function Dec2Bin ( $number )
{
while ( $number >= 256 ) {
$bytes [] = (( $number / 256 ) - ( floor ( $number / 256 ))) * 256 ;
$number = floor ( $number / 256 );
}
$bytes [] = $number ;
$binstring = '' ;
for ( $i = 0 ; $i < count ( $bytes ); $i ++ ) {
$binstring = (( $i == count ( $bytes ) - 1 ) ? decbin ( $bytes [ $i ]) : str_pad ( decbin ( $bytes [ $i ]), 8 , '0' , STR_PAD_LEFT )) . $binstring ;
}
return $binstring ;
}
}
if ( ! function_exists ( 'Bin2Dec' )) {
function Bin2Dec ( $binstring )
{
$decvalue = 0 ;
for ( $i = 0 ; $i < strlen ( $binstring ); $i ++ ) {
$decvalue += (( int ) substr ( $binstring , strlen ( $binstring ) - $i - 1 , 1 )) * pow ( 2 , $i );
}
return CastAsInt ( $decvalue );
}
}
if ( ! function_exists ( 'Bin2String' )) {
function Bin2String ( $binstring )
{
// return 'hi' for input of '0110100001101001'
$string = '' ;
$binstringreversed = strrev ( $binstring );
for ( $i = 0 ; $i < strlen ( $binstringreversed ); $i += 8 ) {
$string = chr ( Bin2Dec ( strrev ( substr ( $binstringreversed , $i , 8 )))) . $string ;
}
return $string ;
}
}
if ( ! function_exists ( 'LittleEndian2String' )) {
function LittleEndian2String ( $number , $minbytes = 1 , $synchsafe = false )
{
$intstring = '' ;
while ( $number > 0 ) {
if ( $synchsafe ) {
$intstring = $intstring . chr ( $number & 127 );
$number >>= 7 ;
} else {
$intstring = $intstring . chr ( $number & 255 );
$number >>= 8 ;
}
}
return str_pad ( $intstring , $minbytes , chr ( 0 ), STR_PAD_RIGHT );
}
}
if ( ! function_exists ( 'Bool2IntString' )) {
function Bool2IntString ( $intvalue )
{
return $intvalue ? '1' : '0' ;
}
}
if ( ! function_exists ( 'IntString2Bool' )) {
function IntString2Bool ( $char )
{
if ( $char == '1' ) {
return true ;
} elseif ( $char == '0' ) {
return false ;
}
return null ;
}
}
if ( ! function_exists ( 'InverseBoolean' )) {
function InverseBoolean ( $value )
{
return $value ? false : true ;
}
}
if ( ! function_exists ( 'DeUnSynchronise' )) {
function DeUnSynchronise ( $data )
{
return str_replace ( chr ( 0xFF ) . chr ( 0x00 ), chr ( 0xFF ), $data );
}
}
if ( ! function_exists ( 'Unsynchronise' )) {
function Unsynchronise ( $data )
{
// Whenever a false synchronisation is found within the tag, one zeroed
// byte is inserted after the first false synchronisation byte. The
// format of a correct sync that should be altered by ID3 encoders is as
// follows:
// %11111111 111xxxxx
// And should be replaced with:
// %11111111 00000000 111xxxxx
// This has the side effect that all $FF 00 combinations have to be
// altered, so they won't be affected by the decoding process. Therefore
// all the $FF 00 combinations have to be replaced with the $FF 00 00
// combination during the unsynchronisation.
$data = str_replace ( chr ( 0xFF ) . chr ( 0x00 ), chr ( 0xFF ) . chr ( 0x00 ) . chr ( 0x00 ), $data );
$unsyncheddata = '' ;
for ( $i = 0 ; $i < strlen ( $data ); $i ++ ) {
$thischar = $data [ $i ];
$unsyncheddata .= $thischar ;
if ( $thischar == chr ( 255 )) {
$nextchar = ord ( substr ( $data , $i + 1 , 1 ));
if (( $nextchar | 0xE0 ) == 0xE0 ) {
// previous byte = 11111111, this byte = 111?????
$unsyncheddata .= chr ( 0 );
}
}
}
return $unsyncheddata ;
}
}
if ( ! function_exists ( 'is_hash' )) {
function is_hash ( $var )
{
// written by dev-null@christophe.vg
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if ( is_array ( $var )) {
$keys = array_keys ( $var );
$all_num = true ;
for ( $i = 0 ; $i < count ( $keys ); $i ++ ) {
if ( is_string ( $keys [ $i ])) {
return true ;
}
}
}
return false ;
}
}
if ( ! function_exists ( 'array_join_merge' )) {
function array_join_merge ( $arr1 , $arr2 )
{
// written by dev-null@christophe.vg
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if ( is_array ( $arr1 ) && is_array ( $arr2 )) {
// the same -> merge
$new_array = [];
if ( is_hash ( $arr1 ) && is_hash ( $arr2 )) {
// hashes -> merge based on keys
$keys = array_merge ( array_keys ( $arr1 ), array_keys ( $arr2 ));
foreach ( $keys as $key ) {
$arr1 [ $key ] = ( isset ( $arr1 [ $key ]) ? $arr1 [ $key ] : '' );
$arr2 [ $key ] = ( isset ( $arr2 [ $key ]) ? $arr2 [ $key ] : '' );
$new_array [ $key ] = array_join_merge ( $arr1 [ $key ], $arr2 [ $key ]);
}
} else {
// two real arrays -> merge
$new_array = array_reverse ( array_unique ( array_reverse ( array_merge ( $arr1 , $arr2 ))));
}
return $new_array ;
} else {
// not the same ... take new one if defined, else the old one stays
return $arr2 ? $arr2 : $arr1 ;
}
}
}
if ( ! function_exists ( 'array_merge_clobber' )) {
function array_merge_clobber ( $array1 , $array2 )
{
// written by kc@hireability.com
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if ( ! is_array ( $array1 ) || ! is_array ( $array2 )) {
return false ;
}
$newarray = $array1 ;
foreach ( $array2 as $key => $val ) {
if ( is_array ( $val ) && isset ( $newarray [ $key ]) && is_array ( $newarray [ $key ])) {
$newarray [ $key ] = array_merge_clobber ( $newarray [ $key ], $val );
} else {
$newarray [ $key ] = $val ;
}
}
return $newarray ;
}
}
if ( ! function_exists ( 'array_merge_noclobber' )) {
function array_merge_noclobber ( $array1 , $array2 )
{
if ( ! is_array ( $array1 ) || ! is_array ( $array2 )) {
return false ;
}
$newarray = $array1 ;
foreach ( $array2 as $key => $val ) {
if ( is_array ( $val ) && isset ( $newarray [ $key ]) && is_array ( $newarray [ $key ])) {
$newarray [ $key ] = array_merge_noclobber ( $newarray [ $key ], $val );
} elseif ( ! isset ( $newarray [ $key ])) {
$newarray [ $key ] = $val ;
}
}
return $newarray ;
}
}
if ( ! function_exists ( 'RoughTranslateUnicodeToASCII' )) {
function RoughTranslateUnicodeToASCII ( $rawdata , $frame_textencoding )
{
// rough translation of data for application that can't handle Unicode data
$tempstring = '' ;
switch ( $frame_textencoding ) {
case 0 : // ISO-8859-1. Terminated with $00.
$asciidata = $rawdata ;
break ;
case 1 : // UTF-16 encoded Unicode with BOM. Terminated with $00 00.
$asciidata = $rawdata ;
if ( substr ( $asciidata , 0 , 2 ) == chr ( 0xFF ) . chr ( 0xFE )) {
// remove BOM, only if present (it should be, but...)
$asciidata = substr ( $asciidata , 2 );
}
if ( substr ( $asciidata , strlen ( $asciidata ) - 2 , 2 ) == chr ( 0 ) . chr ( 0 )) {
$asciidata = substr ( $asciidata , 0 , strlen ( $asciidata ) - 2 ); // remove terminator, only if present (it should be, but...)
}
for ( $i = 0 ; $i < strlen ( $asciidata ); $i += 2 ) {
if (( ord ( $asciidata [ $i ]) <= 0x7F ) || ( ord ( $asciidata [ $i ]) >= 0xA0 )) {
$tempstring .= $asciidata [ $i ];
} else {
$tempstring .= '?' ;
}
}
$asciidata = $tempstring ;
break ;
case 2 : // UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
$asciidata = $rawdata ;
if ( substr ( $asciidata , strlen ( $asciidata ) - 2 , 2 ) == chr ( 0 ) . chr ( 0 )) {
$asciidata = substr ( $asciidata , 0 , strlen ( $asciidata ) - 2 ); // remove terminator, only if present (it should be, but...)
}
for ( $i = 0 ; $i < strlen ( $asciidata ); $i += 2 ) {
if (( ord ( $asciidata [ $i ]) <= 0x7F ) || ( ord ( $asciidata [ $i ]) >= 0xA0 )) {
$tempstring .= $asciidata [ $i ];
} else {
$tempstring .= '?' ;
}
}
$asciidata = $tempstring ;
break ;
case 3 : // UTF-8 encoded Unicode. Terminated with $00.
$asciidata = utf8_decode ( $rawdata );
break ;
case 255 : // Unicode, Big-Endian. Terminated with $00 00.
$asciidata = $rawdata ;
if ( substr ( $asciidata , strlen ( $asciidata ) - 2 , 2 ) == chr ( 0 ) . chr ( 0 )) {
$asciidata = substr ( $asciidata , 0 , strlen ( $asciidata ) - 2 ); // remove terminator, only if present (it should be, but...)
}
for ( $i = 0 ; ( $i + 1 ) < strlen ( $asciidata ); $i += 2 ) {
if (( ord ( $asciidata [( $i + 1 )]) <= 0x7F ) || ( ord ( $asciidata [( $i + 1 )]) >= 0xA0 )) {
$tempstring .= $asciidata [( $i + 1 )];
} else {
$tempstring .= '?' ;
}
}
$asciidata = $tempstring ;
break ;
default :
// shouldn't happen, but in case $frame_textencoding is not 1 <= $frame_textencoding <= 4
// just pass the data through unchanged.
$asciidata = $rawdata ;
break ;
}
if ( substr ( $asciidata , strlen ( $asciidata ) - 1 , 1 ) == chr ( 0 )) {
// remove null terminator, if present
$asciidata = NoNullString ( $asciidata );
}
return $asciidata ;
// return str_replace(chr(0), '', $asciidata); // just in case any nulls slipped through
}
}
if ( ! function_exists ( 'PlaytimeString' )) {
function PlaytimeString ( $playtimeseconds )
{
$contentseconds = round ((( $playtimeseconds / 60 ) - floor ( $playtimeseconds / 60 )) * 60 );
$contentminutes = floor ( $playtimeseconds / 60 );
if ( $contentseconds >= 60 ) {
$contentseconds -= 60 ;
$contentminutes ++ ;
}
return number_format ( $contentminutes ) . ':' . str_pad ( $contentseconds , 2 , 0 , STR_PAD_LEFT );
}
}
if ( ! function_exists ( 'CloseMatch' )) {
function CloseMatch ( $value1 , $value2 , $tolerance )
{
return abs ( $value1 - $value2 ) <= $tolerance ;
}
}
if ( ! function_exists ( 'ID3v1matchesID3v2' )) {
function ID3v1matchesID3v2 ( $id3v1 , $id3v2 )
{
$requiredindices = [ 'title' , 'artist' , 'album' , 'year' , 'genre' , 'comment' ];
foreach ( $requiredindices as $requiredindex ) {
if ( ! isset ( $id3v1 [ " $requiredindex " ])) {
$id3v1 [ " $requiredindex " ] = '' ;
}
if ( ! isset ( $id3v2 [ " $requiredindex " ])) {
$id3v2 [ " $requiredindex " ] = '' ;
}
}
if ( trim ( $id3v1 [ 'title' ]) != trim ( substr ( $id3v2 [ 'title' ], 0 , 30 ))) {
return false ;
}
if ( trim ( $id3v1 [ 'artist' ]) != trim ( substr ( $id3v2 [ 'artist' ], 0 , 30 ))) {
return false ;
}
if ( trim ( $id3v1 [ 'album' ]) != trim ( substr ( $id3v2 [ 'album' ], 0 , 30 ))) {
return false ;
}
if ( trim ( $id3v1 [ 'year' ]) != trim ( substr ( $id3v2 [ 'year' ], 0 , 4 ))) {
return false ;
}
if ( trim ( $id3v1 [ 'genre' ]) != trim ( $id3v2 [ 'genre' ])) {
return false ;
}
if ( isset ( $id3v1 [ 'track' ])) {
if ( ! isset ( $id3v1 [ 'track' ]) || ( trim ( $id3v1 [ 'track' ]) != trim ( $id3v2 [ 'track' ]))) {
return false ;
}
if ( trim ( $id3v1 [ 'comment' ]) != trim ( substr ( $id3v2 [ 'comment' ], 0 , 28 ))) {
return false ;
}
} else {
if ( trim ( $id3v1 [ 'comment' ]) != trim ( substr ( $id3v2 [ 'comment' ], 0 , 30 ))) {
return false ;
}
}
return true ;
}
}
if ( ! function_exists ( 'FILETIMEtoUNIXtime' )) {
function FILETIMEtoUNIXtime ( $FILETIME , $round = true )
{
// FILETIME is a 64-bit unsigned integer representing
// the number of 100-nanosecond intervals since January 1, 1601
// UNIX timestamp is number of seconds since January 1, 1970
// 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days
if ( $round ) {
return round (( $FILETIME - 116444736000000000 ) / 10000000 );
}
return ( $FILETIME - 116444736000000000 ) / 10000000 ;
}
}
if ( ! function_exists ( 'GUIDtoBytestring' )) {
function GUIDtoBytestring ( $GUIDstring )
{
// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
// first 4 bytes are in little-endian order
// next 2 bytes are appended in little-endian order
// next 2 bytes are appended in little-endian order
// next 2 bytes are appended in big-endian order
// next 6 bytes are appended in big-endian order
// AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string:
// $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp
$hexbytecharstring = chr ( hexdec ( substr ( $GUIDstring , 6 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 4 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 2 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 0 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 11 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 9 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 16 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 14 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 19 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 21 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 24 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 26 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 28 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 30 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 32 , 2 )));
$hexbytecharstring .= chr ( hexdec ( substr ( $GUIDstring , 34 , 2 )));
return $hexbytecharstring ;
}
}
if ( ! function_exists ( 'BytestringToGUID' )) {
function BytestringToGUID ( $Bytestring )
{
$GUIDstring = str_pad ( dechex ( ord ( $Bytestring [ 3 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 2 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 1 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 0 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= '-' ;
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 5 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 4 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= '-' ;
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 7 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 6 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= '-' ;
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 8 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 9 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= '-' ;
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 10 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 11 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 12 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 13 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 14 ])), 2 , '0' , STR_PAD_LEFT );
$GUIDstring .= str_pad ( dechex ( ord ( $Bytestring [ 15 ])), 2 , '0' , STR_PAD_LEFT );
return strtoupper ( $GUIDstring );
}
}
if ( ! function_exists ( 'BitrateColor' )) {
function BitrateColor ( $bitrate )
{
$bitrate /= 3 ; // scale from 1-768kbps to 1-256kbps
$bitrate -- ; // scale from 1-256kbps to 0-255kbps
$bitrate = max ( $bitrate , 0 );
$bitrate = min ( $bitrate , 255 );
//$bitrate = max($bitrate, 32);
//$bitrate = min($bitrate, 143);
//$bitrate = ($bitrate * 2) - 32;
$Rcomponent = max ( 255 - ( $bitrate * 2 ), 0 );
$Gcomponent = max (( $bitrate * 2 ) - 255 , 0 );
if ( $bitrate > 127 ) {
$Bcomponent = max (( 255 - $bitrate ) * 2 , 0 );
} else {
$Bcomponent = max ( $bitrate * 2 , 0 );
}
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 );
}
}
if ( ! function_exists ( 'BitrateText' )) {
function BitrateText ( $bitrate )
{
return '<SPAN STYLE="color: #' . BitrateColor ( $bitrate ) . '">' . round ( $bitrate ) . ' kbps</SPAN>' ;
}
}
if ( ! function_exists ( 'image_type_to_mime_type' )) {
function image_type_to_mime_type ( $imagetypeid )
{
// only available in PHP v4.3.0+
static $image_type_to_mime_type = [];
if ( empty ( $image_type_to_mime_type )) {
$image_type_to_mime_type [ 1 ] = 'image/gif' ; // GIF
$image_type_to_mime_type [ 2 ] = 'image/jpeg' ; // JPEG
$image_type_to_mime_type [ 3 ] = 'image/png' ; // PNG
$image_type_to_mime_type [ 4 ] = 'application/x-shockwave-flash' ; // Flash
$image_type_to_mime_type [ 5 ] = 'image/psd' ; // PSD
$image_type_to_mime_type [ 6 ] = 'image/bmp' ; // BMP
$image_type_to_mime_type [ 7 ] = 'image/tiff' ; // TIFF: little-endian (Intel)
$image_type_to_mime_type [ 8 ] = 'image/tiff' ; // TIFF: big-endian (Motorola)
//$image_type_to_mime_type[9] = 'image/jpc'; // JPC
//$image_type_to_mime_type[10] = 'image/jp2'; // JPC
//$image_type_to_mime_type[11] = 'image/jpx'; // JPC
//$image_type_to_mime_type[12] = 'image/jb2'; // JPC
$image_type_to_mime_type [ 13 ] = 'application/x-shockwave-flash' ; // Shockwave
$image_type_to_mime_type [ 14 ] = 'image/iff' ; // IFF
}
return isset ( $image_type_to_mime_type [ $imagetypeid ]) ? $image_type_to_mime_type [ $imagetypeid ] : 'application/octet-stream' ;
}
}
if ( ! function_exists ( 'utf8_decode' )) {
// PHP has this function built-in if it's configured with the --with-xml option
// This version of the function is only provided in case XML isn't installed
function utf8_decode ( $utf8text )
{
// http://www.php.net/manual/en/function.utf8-encode.php
// bytes bits representation
// 1 7 0bbbbbbb
// 2 11 110bbbbb 10bbbbbb
// 3 16 1110bbbb 10bbbbbb 10bbbbbb
// 4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
$utf8length = strlen ( $utf8text );
$decodedtext = '' ;
for ( $i = 0 ; $i < $utf8length ; $i ++ ) {
if (( ord ( $utf8text [ $i ]) & 0x80 ) == 0 ) {
$decodedtext .= $utf8text [ $i ];
} elseif (( ord ( $utf8text [ $i ]) & 0xF0 ) == 0xF0 ) {
$decodedtext .= '?' ;
$i += 3 ;
} elseif (( ord ( $utf8text [ $i ]) & 0xE0 ) == 0xE0 ) {
$decodedtext .= '?' ;
$i += 2 ;
} elseif (( ord ( $utf8text [ $i ]) & 0xC0 ) == 0xC0 ) {
// 2 11 110bbbbb 10bbbbbb
$decodedchar = Bin2Dec ( substr ( Dec2Bin ( ord ( $utf8text [ $i ])), 3 , 5 ) . substr ( Dec2Bin ( ord ( $utf8text [( $i + 1 )])), 2 , 6 ));
if ( $decodedchar <= 255 ) {
$decodedtext .= chr ( $decodedchar );
} else {
$decodedtext .= '?' ;
}
$i += 1 ;
}
}
return $decodedtext ;
}
}
if ( ! function_exists ( 'DateMac2Unix' )) {
function DateMac2Unix ( $macdate )
{
// Macintosh timestamp: seconds since 00:00h January 1, 1904
// UNIX timestamp: seconds since 00:00h January 1, 1970
return CastAsInt ( $macdate - 2082844800 );
}
}
if ( ! function_exists ( 'FixedPoint8_8' )) {
function FixedPoint8_8 ( $rawdata )
{
return BigEndian2Int ( substr ( $rawdata , 0 , 1 )) + ( float ) ( BigEndian2Int ( substr ( $rawdata , 1 , 1 )) / pow ( 2 , 8 ));
}
}
if ( ! function_exists ( 'FixedPoint16_16' )) {
function FixedPoint16_16 ( $rawdata )
{
return BigEndian2Int ( substr ( $rawdata , 0 , 2 )) + ( float ) ( BigEndian2Int ( substr ( $rawdata , 2 , 2 )) / pow ( 2 , 16 ));
}
}
if ( ! function_exists ( 'FixedPoint2_30' )) {
function FixedPoint2_30 ( $rawdata )
{
$binarystring = BigEndian2Bin ( $rawdata );
return Bin2Dec ( substr ( $binarystring , 0 , 2 )) + ( float ) ( Bin2Dec ( substr ( $binarystring , 2 , 30 )) / pow ( 2 , 30 ));
}
}
if ( ! function_exists ( 'Pascal2String' )) {
function Pascal2String ( $pascalstring )
{
// Pascal strings have 1 byte at the beginning saying how many chars are in the string
return substr ( $pascalstring , 1 );
}
}
if ( ! function_exists ( 'NoNullString' )) {
function NoNullString ( $nullterminatedstring )
{
// remove the single null terminator on null terminated strings
if ( substr ( $nullterminatedstring , strlen ( $nullterminatedstring ) - 1 , 1 ) === chr ( 0 )) {
return substr ( $nullterminatedstring , 0 , strlen ( $nullterminatedstring ) - 1 );
}
return $nullterminatedstring ;
}
}
if ( ! function_exists ( 'FileSizeNiceDisplay' )) {
function FileSizeNiceDisplay ( $filesize , $precision = 2 )
{
if ( $filesize < 1000 ) {
$sizeunit = 'bytes' ;
$precision = 0 ;
} else {
$filesize /= 1024 ;
$sizeunit = 'kB' ;
}
if ( $filesize >= 1000 ) {
$filesize /= 1024 ;
$sizeunit = 'MB' ;
}
if ( $filesize >= 1000 ) {
$filesize /= 1024 ;
$sizeunit = 'GB' ;
}
return number_format ( $filesize , $precision ) . ' ' . $sizeunit ;
}
}
if ( ! function_exists ( 'DOStime2UNIXtime' )) {
function DOStime2UNIXtime ( $DOSdate , $DOStime )
{
// wFatDate
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
// Bits Contents
// 0-4 Day of the month (1-31)
// 5-8 Month (1 = January, 2 = February, and so on)
// 9-15 Year offset from 1980 (add 1980 to get actual year)
$UNIXday = ( $DOSdate & 0x001F );
$UNIXmonth = (( $DOSdate & 0x01E0 ) >> 5 );
$UNIXyear = (( $DOSdate & 0xFE00 ) >> 9 ) + 1980 ;
// wFatTime
// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
// Bits Contents
// 0-4 Second divided by 2
// 5-10 Minute (0-59)
// 11-15 Hour (0-23 on a 24-hour clock)
$UNIXsecond = ( $DOStime & 0x001F ) * 2 ;
$UNIXminute = (( $DOStime & 0x07E0 ) >> 5 );
$UNIXhour = (( $DOStime & 0xF800 ) >> 11 );
return mktime ( $UNIXhour , $UNIXminute , $UNIXsecond , $UNIXmonth , $UNIXday , $UNIXyear );
}
}
if ( ! function_exists ( 'CreateDeepArray' )) {
function CreateDeepArray ( $ArrayPath , $Separator , $Value )
{
// assigns $Value to a nested array path:
// $foo = CreateDeepArray('/path/to/my', '/', 'file.txt')
// is the same as:
// $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
// or
// $foo['path']['to']['my'] = 'file.txt';
while ( $ArrayPath [ 0 ] == $Separator ) {
$ArrayPath = substr ( $ArrayPath , 1 );
}
if (( $pos = strpos ( $ArrayPath , $Separator )) !== false ) {
$ReturnedArray [ substr ( $ArrayPath , 0 , $pos )] = CreateDeepArray ( substr ( $ArrayPath , $pos + 1 ), $Separator , $Value );
} else {
$ReturnedArray [ " $ArrayPath " ] = $Value ;
}
return $ReturnedArray ;
}
}
if ( ! function_exists ( 'md5_file' )) {
// Allan Hansen <ah@artemis.dk>
// md5_file() exists in PHP 4.2.0.
// The following works under UNIX only, but dies on windows
function md5_file ( $file )
{
if ( substr ( php_uname (), 0 , 7 ) == 'Windows' ) {
die ( 'PHP 4.2.0 or newer required for md5_file()' );
}
$file = str_replace ( '`' , '\\`' , $file );
if ( preg_match ( " #^([0-9a-f] { 32})[ \t \n \r ]#i " , `md5sum "$file"` , $r )) {
return $r [ 1 ];
}
return false ;
}
}
if ( ! function_exists ( 'md5_data' )) {
// Allan Hansen <ah@artemis.dk>
// md5_data() - returns md5sum for a file from startuing position to absolute end position
function md5_data ( $file , $offset , $end , $invertsign = false )
{
// first try and create a temporary file in the same directory as the file being scanned
if (( $dataMD5filename = tempnam ( dirname ( $file ), preg_replace ( '#[^[:alnum:]]#i' , '' , basename ( $file )))) === false ) {
// if that fails, create a temporary file in the system temp directory
if (( $dataMD5filename = tempnam ( '/tmp' , 'getID3' )) === false ) {
// if that fails, create a temporary file in the current directory
if (( $dataMD5filename = tempnam ( '.' , preg_replace ( '#[^[:alnum:]]#i' , '' , basename ( $file )))) === false ) {
// can't find anywhere to create a temp file, just die
return false ;
}
}
}
$md5 = false ;
set_time_limit ( max ( filesize ( $file ) / 1000000 , 30 ));
// copy parts of file
ob_start ();
if ( $fp = fopen ( $file , 'rb' )) {
ob_end_clean ();
ob_start ();
if ( $MD5fp = fopen ( $dataMD5filename , 'wb' )) {
ob_end_clean ();
if ( $invertsign ) {
// Load conversion lookup strings for 8-bit unsigned->signed conversion below
$from = '' ;
$to = '' ;
for ( $i = 0 ; $i < 128 ; $i ++ ) {
$from .= chr ( $i );
$to .= chr ( $i + 128 );
}
for ( $i = 128 ; $i < 256 ; $i ++ ) {
$from .= chr ( $i );
$to .= chr ( $i - 128 );
}
}
fseek ( $fp , $offset , SEEK_SET );
$byteslefttowrite = $end - $offset ;
while (( $byteslefttowrite > 0 ) && ( $buffer = fread ( $fp , 32768 ))) {
if ( $invertsign ) {
// Possibly FLAC-specific (?)
// FLAC calculates the MD5sum of the source data of 8-bit files
// not on the actual byte values in the source file, but of those
// values converted from unsigned to signed, or more specifcally,
// with the MSB inverted. ex: 01 -> 81; F5 -> 75; etc
// Therefore, 8-bit WAV data has to be converted before getting the
// md5_data value so as to match the FLAC value
// Flip the MSB for each byte in the buffer before copying
$buffer = strtr ( $buffer , $from , $to );
}
$byteswritten = fwrite ( $MD5fp , $buffer , $byteslefttowrite );
$byteslefttowrite -= $byteswritten ;
}
fclose ( $MD5fp );
$md5 = md5_file ( $dataMD5filename );
} else {
$errormessage = ob_get_contents ();
ob_end_clean ();
}
fclose ( $fp );
} else {
$errormessage = ob_get_contents ();
ob_end_clean ();
}
unlink ( $dataMD5filename );
return $md5 ;
}
}
if ( ! function_exists ( 'TwosCompliment2Decimal' )) {
function TwosCompliment2Decimal ( $BinaryValue )
{
// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
// First check if the number is negative or positive by looking at the sign bit.
// If it is positive, simply convert it to decimal.
// If it is negative, make it positive by inverting the bits and adding one.
// Then, convert the result to decimal.
// The negative of this number is the value of the original binary.
if ( $BinaryValue & 0x80 ) {
// negative number
return 0 - (( ~ $BinaryValue & 0xFF ) + 1 );
} else {
// positive number
return $BinaryValue ;
}
}
}
if ( ! function_exists ( 'LastArrayElement' )) {
function LastArrayElement ( $MyArray )
{
if ( ! is_array ( $MyArray )) {
return false ;
}
if ( empty ( $MyArray )) {
return null ;
}
foreach ( $MyArray as $key => $value ) {
}
return $value ;
}
}
if ( ! function_exists ( 'safe_inc' )) {
function safe_inc ( & $variable , $increment = 1 )
{
if ( isset ( $variable )) {
$variable += $increment ;
} else {
$variable = $increment ;
}
return true ;
}
}
if ( ! function_exists ( 'CalculateCompressionRatioVideo' )) {
function CalculateCompressionRatioVideo ( & $ThisFileInfo )
{
if ( empty ( $ThisFileInfo [ 'video' ])) {
return false ;
}
if ( empty ( $ThisFileInfo [ 'video' ][ 'resolution_x' ]) || empty ( $ThisFileInfo [ 'video' ][ 'resolution_y' ])) {
return false ;
}
if ( empty ( $ThisFileInfo [ 'video' ][ 'bits_per_sample' ])) {
return false ;
}
switch ( $ThisFileInfo [ 'video' ][ 'dataformat' ]) {
case 'bmp' :
case 'gif' :
case 'jpeg' :
case 'jpg' :
case 'png' :
case 'tiff' :
$FrameRate = 1 ;
$PlaytimeSeconds = 1 ;
$BitrateCompressed = $ThisFileInfo [ 'filesize' ] * 8 ;
break ;
default :
if ( ! empty ( $ThisFileInfo [ 'video' ][ 'frame_rate' ])) {
$FrameRate = $ThisFileInfo [ 'video' ][ 'frame_rate' ];
} else {
return false ;
}
if ( ! empty ( $ThisFileInfo [ 'playtime_seconds' ])) {
$PlaytimeSeconds = $ThisFileInfo [ 'playtime_seconds' ];
} else {
return false ;
}
if ( ! empty ( $ThisFileInfo [ 'video' ][ 'bitrate' ])) {
$BitrateCompressed = $ThisFileInfo [ 'video' ][ 'bitrate' ];
} else {
return false ;
}
break ;
}
$BitrateUncompressed = $ThisFileInfo [ 'video' ][ 'resolution_x' ] * $ThisFileInfo [ 'video' ][ 'resolution_y' ] * $ThisFileInfo [ 'video' ][ 'bits_per_sample' ] * $FrameRate ;
$ThisFileInfo [ 'video' ][ 'compression_ratio' ] = $BitrateCompressed / $BitrateUncompressed ;
return true ;
}
}
if ( ! function_exists ( 'CalculateCompressionRatioAudio' )) {
function CalculateCompressionRatioAudio ( & $ThisFileInfo )
{
if ( empty ( $ThisFileInfo [ 'audio' ][ 'bitrate' ]) || empty ( $ThisFileInfo [ 'audio' ][ 'channels' ]) || empty ( $ThisFileInfo [ 'audio' ][ 'sample_rate' ]) || empty ( $ThisFileInfo [ 'audio' ][ 'bits_per_sample' ])) {
return false ;
}
$ThisFileInfo [ 'audio' ][ 'compression_ratio' ] = $ThisFileInfo [ 'audio' ][ 'bitrate' ] / ( $ThisFileInfo [ 'audio' ][ 'channels' ] * $ThisFileInfo [ 'audio' ][ 'sample_rate' ] * $ThisFileInfo [ 'audio' ][ 'bits_per_sample' ]);
return true ;
}
}
if ( ! function_exists ( 'IsValidMIMEstring' )) {
function IsValidMIMEstring ( $mimestring )
{
if (( strlen ( $mimestring ) >= 3 ) && ( strpos ( $mimestring , '/' ) > 0 ) && ( strpos ( $mimestring , '/' ) < ( strlen ( $mimestring ) - 1 ))) {
return true ;
}
return false ;
}
}
if ( ! function_exists ( 'IsWithinBitRange' )) {
function IsWithinBitRange ( $number , $maxbits , $signed = false )
{
if ( $signed ) {
if (( $number > ( 0 - pow ( 2 , $maxbits - 1 ))) && ( $number <= pow ( 2 , $maxbits - 1 ))) {
return true ;
}
} else {
if (( $number >= 0 ) && ( $number <= pow ( 2 , $maxbits ))) {
return true ;
}
}
return false ;
}
}
if ( ! function_exists ( 'safe_parse_url' )) {
function safe_parse_url ( $url )
{
ob_start ();
$parts = parse_url ( $url );
$errormessage = ob_get_contents ();
ob_end_clean ();
$parts [ 'scheme' ] = ( isset ( $parts [ 'scheme' ]) ? $parts [ 'scheme' ] : '' );
$parts [ 'host' ] = ( isset ( $parts [ 'host' ]) ? $parts [ 'host' ] : '' );
$parts [ 'user' ] = ( isset ( $parts [ 'user' ]) ? $parts [ 'user' ] : '' );
$parts [ 'pass' ] = ( isset ( $parts [ 'pass' ]) ? $parts [ 'pass' ] : '' );
$parts [ 'path' ] = ( isset ( $parts [ 'path' ]) ? $parts [ 'path' ] : '' );
$parts [ 'query' ] = ( isset ( $parts [ 'query' ]) ? $parts [ 'query' ] : '' );
return $parts ;
}
}
if ( ! function_exists ( 'IsValidURL' )) {
function IsValidURL ( $url , $allowUserPass = false )
{
if ( $url == '' ) {
return false ;
}
if ( $allowUserPass !== true ) {
if ( strstr ( $url , '@' )) {
// in the format http://user:pass@example.com or http://user@example.com
// but could easily be somebody incorrectly entering an email address in place of a URL
return false ;
}
}
if ( $parts = safe_parse_url ( $url )) {
if (( $parts [ 'scheme' ] != 'http' ) && ( $parts [ 'scheme' ] != 'https' ) && ( $parts [ 'scheme' ] != 'ftp' ) && ( $parts [ 'scheme' ] != 'gopher' )) {
return false ;
} 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' ])) {
return false ;
} elseif ( ! preg_match ( " #^([[:alnum:]-]|[ \ _])* $ #i " , $parts [ 'user' ], $regs )) {
return false ;
} elseif ( ! preg_match ( " #^([[:alnum:]-]|[ \ _])* $ #i " , $parts [ 'pass' ], $regs )) {
return false ;
} elseif ( ! preg_match ( " #^[[:alnum:]/_ \ .@~-]* $ #i " , $parts [ 'path' ], $regs )) {
return false ;
} elseif ( ! preg_match ( " #^[[:alnum:]?&=+:;_()%#/, \ .-]* $ #i " , $parts [ 'query' ], $regs )) {
return false ;
} else {
return true ;
}
}
return false ;
}
2015-09-04 03:59:46 +02:00
}
echo '<form action="' . htmlentities ( $_SERVER [ 'PHP_SELF' ], ENT_QUOTES ) . '" method="get">' ;
echo 'Enter 4 hex bytes of MPEG-audio header (ie <I>FF FA 92 44</I>)<BR>' ;
echo '<input type="text" name="HeaderHexBytes" value="' . htmlentities ( isset ( $_POST [ 'HeaderHexBytes' ]) ? strtoupper ( $_POST [ 'HeaderHexBytes' ]) : '' , ENT_QUOTES ) . '" size="11" maxlength="11">' ;
echo '<input type="submit" name="Analyze" value="Analyze"></form>' ;
echo '<hr>' ;
echo '<form action="' . htmlentities ( $_SERVER [ 'PHP_SELF' ], ENT_QUOTES ) . '" METHOD="get">' ;
echo 'Generate a MPEG-audio 4-byte header from these values:<BR>' ;
echo '<table border="0">' ;
2021-02-14 03:39:15 +01:00
$MPEGgenerateValues = [
'version' => [ '1' , '2' , '2.5' ],
'layer' => [ 'I' , 'II' , 'III' ],
'protection' => [ 'Y' , 'N' ],
'bitrate' => [ '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' ],
'frequency' => [ '8000' , '11025' , '12000' , '16000' , '22050' , '24000' , '32000' , '44100' , '48000' ],
'padding' => [ 'Y' , 'N' ],
'private' => [ 'Y' , 'N' ],
'channelmode' => [ 'stereo' , 'joint stereo' , 'dual channel' , 'mono' ],
'modeextension' => [ 'none' , 'IS' , 'MS' , 'IS+MS' , '4-31' , '8-31' , '12-31' , '16-31' ],
'copyright' => [ 'Y' , 'N' ],
'original' => [ 'Y' , 'N' ],
'emphasis' => [ 'none' , '50/15ms' , 'CCIT J.17' ],
];
2015-09-04 03:59:46 +02:00
foreach ( $MPEGgenerateValues as $name => $dataarray ) {
2021-02-14 03:39:15 +01:00
echo '<tr><th>' . $name . ':</th><td><select name="' . $name . '">' ;
foreach ( $dataarray as $key => $value ) {
echo '<option' . (( isset ( $_POST [ " $name " ]) && ( $_POST [ " $name " ] == $value )) ? ' SELECTED' : '' ) . '>' . $value . '</option>' ;
}
echo '</select></td></tr>' ;
2015-09-04 03:59:46 +02:00
}
if ( isset ( $_POST [ 'bitrate' ])) {
2021-02-14 03:39:15 +01:00
echo '<tr><th>Frame Length:</th><td>' . ( int ) MPEGaudioFrameLength ( $_POST [ 'bitrate' ], $_POST [ 'version' ], $_POST [ 'layer' ], (( $_POST [ 'padding' ] == 'Y' ) ? '1' : '0' ), $_POST [ 'frequency' ]) . '</td></tr>' ;
2015-09-04 03:59:46 +02:00
}
echo '</table>' ;
echo '<input type="submit" name="Generate" value="Generate"></form>' ;
echo '<hr>' ;
if ( isset ( $_POST [ 'Analyze' ]) && $_POST [ 'HeaderHexBytes' ]) {
2021-02-14 03:39:15 +01:00
$headerbytearray = explode ( ' ' , $_POST [ 'HeaderHexBytes' ]);
if ( count ( $headerbytearray ) != 4 ) {
die ( 'Invalid byte pattern' );
}
$headerstring = '' ;
foreach ( $headerbytearray as $textbyte ) {
$headerstring .= chr ( hexdec ( $textbyte ));
}
$MP3fileInfo [ 'error' ] = '' ;
$MPEGheaderRawArray = MPEGaudioHeaderDecode ( substr ( $headerstring , 0 , 4 ));
if ( MPEGaudioHeaderValid ( $MPEGheaderRawArray , true )) {
$MP3fileInfo [ 'raw' ] = $MPEGheaderRawArray ;
$MP3fileInfo [ 'version' ] = MPEGaudioVersionLookup ( $MP3fileInfo [ 'raw' ][ 'version' ]);
$MP3fileInfo [ 'layer' ] = MPEGaudioLayerLookup ( $MP3fileInfo [ 'raw' ][ 'layer' ]);
$MP3fileInfo [ 'protection' ] = MPEGaudioCRCLookup ( $MP3fileInfo [ 'raw' ][ 'protection' ]);
$MP3fileInfo [ 'bitrate' ] = MPEGaudioBitrateLookup ( $MP3fileInfo [ 'version' ], $MP3fileInfo [ 'layer' ], $MP3fileInfo [ 'raw' ][ 'bitrate' ]);
$MP3fileInfo [ 'frequency' ] = MPEGaudioFrequencyLookup ( $MP3fileInfo [ 'version' ], $MP3fileInfo [ 'raw' ][ 'sample_rate' ]);
$MP3fileInfo [ 'padding' ] = ( bool ) $MP3fileInfo [ 'raw' ][ 'padding' ];
$MP3fileInfo [ 'private' ] = ( bool ) $MP3fileInfo [ 'raw' ][ 'private' ];
$MP3fileInfo [ 'channelmode' ] = MPEGaudioChannelModeLookup ( $MP3fileInfo [ 'raw' ][ 'channelmode' ]);
$MP3fileInfo [ 'channels' ] = (( $MP3fileInfo [ 'channelmode' ] == 'mono' ) ? 1 : 2 );
$MP3fileInfo [ 'modeextension' ] = MPEGaudioModeExtensionLookup ( $MP3fileInfo [ 'layer' ], $MP3fileInfo [ 'raw' ][ 'modeextension' ]);
$MP3fileInfo [ 'copyright' ] = ( bool ) $MP3fileInfo [ 'raw' ][ 'copyright' ];
$MP3fileInfo [ 'original' ] = ( bool ) $MP3fileInfo [ 'raw' ][ 'original' ];
$MP3fileInfo [ 'emphasis' ] = MPEGaudioEmphasisLookup ( $MP3fileInfo [ 'raw' ][ 'emphasis' ]);
if ( $MP3fileInfo [ 'protection' ]) {
$MP3fileInfo [ 'crc' ] = BigEndian2Int ( substr ( $headerstring , 4 , 2 ));
}
if ( $MP3fileInfo [ 'frequency' ] > 0 ) {
$MP3fileInfo [ 'framelength' ] = MPEGaudioFrameLength ( $MP3fileInfo [ 'bitrate' ], $MP3fileInfo [ 'version' ], $MP3fileInfo [ 'layer' ], ( int ) $MP3fileInfo [ 'padding' ], $MP3fileInfo [ 'frequency' ]);
}
if ( $MP3fileInfo [ 'bitrate' ] != 'free' ) {
$MP3fileInfo [ 'bitrate' ] *= 1000 ;
}
} else {
$MP3fileInfo [ 'error' ] .= " \n " . 'Invalid MPEG audio header' ;
}
if ( ! $MP3fileInfo [ 'error' ]) {
unset ( $MP3fileInfo [ 'error' ]);
}
echo table_var_dump ( $MP3fileInfo );
} elseif ( isset ( $_POST [ 'Generate' ])) {
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream = '11111111111' ; // A - Frame sync (all bits set)
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$MPEGversionLookup = [ '2.5' => '00' , '2' => '10' , '1' => '11' ];
$headerbitstream .= $MPEGversionLookup [ $_POST [ 'version' ]]; // B - MPEG Audio version ID
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$MPEGlayerLookup = [ 'III' => '01' , 'II' => '10' , 'I' => '11' ];
$headerbitstream .= $MPEGlayerLookup [ $_POST [ 'layer' ]]; // C - Layer description
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream .= (( $_POST [ 'protection' ] == 'Y' ) ? '0' : '1' ); // D - Protection bit
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
$MPEGaudioBitrateLookup [ '1' ][ 'I' ] = [ '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' ];
$MPEGaudioBitrateLookup [ '1' ][ 'II' ] = [ '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' ];
$MPEGaudioBitrateLookup [ '1' ][ 'III' ] = [ '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' ];
$MPEGaudioBitrateLookup [ '2' ][ 'I' ] = [ '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' ];
$MPEGaudioBitrateLookup [ '2' ][ 'II' ] = [ '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' ];
$MPEGaudioBitrateLookup [ '2' ][ 'III' ] = $MPEGaudioBitrateLookup [ '2' ][ 'II' ];
$MPEGaudioBitrateLookup [ '2.5' ][ 'I' ] = $MPEGaudioBitrateLookup [ '2' ][ 'I' ];
$MPEGaudioBitrateLookup [ '2.5' ][ 'II' ] = $MPEGaudioBitrateLookup [ '2' ][ 'II' ];
$MPEGaudioBitrateLookup [ '2.5' ][ 'III' ] = $MPEGaudioBitrateLookup [ '2' ][ 'II' ];
if ( isset ( $MPEGaudioBitrateLookup [ $_POST [ 'version' ]][ $_POST [ 'layer' ]][ $_POST [ 'bitrate' ]])) {
$headerbitstream .= $MPEGaudioBitrateLookup [ $_POST [ 'version' ]][ $_POST [ 'layer' ]][ $_POST [ 'bitrate' ]]; // E - Bitrate index
} else {
die ( 'Invalid <B>Bitrate</B>' );
}
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$MPEGaudioFrequencyLookup [ '1' ] = [ '44100' => '00' , '48000' => '01' , '32000' => '10' ];
$MPEGaudioFrequencyLookup [ '2' ] = [ '22050' => '00' , '24000' => '01' , '16000' => '10' ];
$MPEGaudioFrequencyLookup [ '2.5' ] = [ '11025' => '00' , '12000' => '01' , '8000' => '10' ];
if ( isset ( $MPEGaudioFrequencyLookup [ $_POST [ 'version' ]][ $_POST [ 'frequency' ]])) {
$headerbitstream .= $MPEGaudioFrequencyLookup [ $_POST [ 'version' ]][ $_POST [ 'frequency' ]]; // F - Sampling rate frequency index
} else {
die ( 'Invalid <B>Frequency</B>' );
}
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream .= (( $_POST [ 'padding' ] == 'Y' ) ? '1' : '0' ); // G - Padding bit
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream .= (( $_POST [ 'private' ] == 'Y' ) ? '1' : '0' ); // H - Private bit
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
$MPEGaudioChannelModeLookup = [ 'stereo' => '00' , 'joint stereo' => '01' , 'dual channel' => '10' , 'mono' => '11' ];
$headerbitstream .= $MPEGaudioChannelModeLookup [ $_POST [ 'channelmode' ]]; // I - Channel Mode
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
$MPEGaudioModeExtensionLookup [ 'I' ] = [ '4-31' => '00' , '8-31' => '01' , '12-31' => '10' , '16-31' => '11' ];
$MPEGaudioModeExtensionLookup [ 'II' ] = $MPEGaudioModeExtensionLookup [ 'I' ];
$MPEGaudioModeExtensionLookup [ 'III' ] = [ 'none' => '00' , 'IS' => '01' , 'MS' => '10' , 'IS+MS' => '11' ];
if ( $_POST [ 'channelmode' ] != 'joint stereo' ) {
$headerbitstream .= '00' ;
} elseif ( isset ( $MPEGaudioModeExtensionLookup [ $_POST [ 'layer' ]][ $_POST [ 'modeextension' ]])) {
$headerbitstream .= $MPEGaudioModeExtensionLookup [ $_POST [ 'layer' ]][ $_POST [ 'modeextension' ]]; // J - Mode extension (Only if Joint stereo)
} else {
die ( 'Invalid <B>Mode Extension</B>' );
}
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream .= (( $_POST [ 'copyright' ] == 'Y' ) ? '1' : '0' ); // K - Copyright
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
$headerbitstream .= (( $_POST [ 'original' ] == 'Y' ) ? '1' : '0' ); // L - Original
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
$MPEGaudioEmphasisLookup = [ 'none' => '00' , '50/15ms' => '01' , 'CCIT J.17' => '11' ];
if ( isset ( $MPEGaudioEmphasisLookup [ $_POST [ 'emphasis' ]])) {
$headerbitstream .= $MPEGaudioEmphasisLookup [ $_POST [ 'emphasis' ]]; // M - Emphasis
} else {
die ( 'Invalid <B>Emphasis</B>' );
}
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
echo strtoupper ( str_pad ( dechex ( bindec ( substr ( $headerbitstream , 0 , 8 ))), 2 , '0' , STR_PAD_LEFT )) . ' ' ;
echo strtoupper ( str_pad ( dechex ( bindec ( substr ( $headerbitstream , 8 , 8 ))), 2 , '0' , STR_PAD_LEFT )) . ' ' ;
echo strtoupper ( str_pad ( dechex ( bindec ( substr ( $headerbitstream , 16 , 8 ))), 2 , '0' , STR_PAD_LEFT )) . ' ' ;
echo strtoupper ( str_pad ( dechex ( bindec ( substr ( $headerbitstream , 24 , 8 ))), 2 , '0' , STR_PAD_LEFT )) . '<BR>' ;
}
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
function MPEGaudioVersionLookup ( $rawversion )
{
$MPEGaudioVersionLookup = [ '2.5' , false , '2' , '1' ];
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioVersionLookup [ " $rawversion " ]) ? $MPEGaudioVersionLookup [ " $rawversion " ] : false ;
}
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
function MPEGaudioLayerLookup ( $rawlayer )
{
$MPEGaudioLayerLookup = [ false , 'III' , 'II' , 'I' ];
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioLayerLookup [ " $rawlayer " ]) ? $MPEGaudioLayerLookup [ " $rawlayer " ] : false ;
}
2017-05-15 20:37:10 +02:00
2021-02-14 03:39:15 +01:00
function MPEGaudioBitrateLookup ( $version , $layer , $rawbitrate )
{
static $MPEGaudioBitrateLookup ;
if ( empty ( $MPEGaudioBitrateLookup )) {
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray ();
}
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioBitrateLookup [ " $version " ][ " $layer " ][ " $rawbitrate " ]) ? $MPEGaudioBitrateLookup [ " $version " ][ " $layer " ][ " $rawbitrate " ] : false ;
2015-09-04 03:59:46 +02:00
}
2021-02-14 03:39:15 +01:00
function MPEGaudioFrequencyLookup ( $version , $rawfrequency )
{
static $MPEGaudioFrequencyLookup ;
if ( empty ( $MPEGaudioFrequencyLookup )) {
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray ();
}
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioFrequencyLookup [ " $version " ][ " $rawfrequency " ]) ? $MPEGaudioFrequencyLookup [ " $version " ][ " $rawfrequency " ] : false ;
2015-09-04 03:59:46 +02:00
}
2021-02-14 03:39:15 +01:00
function MPEGaudioChannelModeLookup ( $rawchannelmode )
{
$MPEGaudioChannelModeLookup = [ 'stereo' , 'joint stereo' , 'dual channel' , 'mono' ];
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioChannelModeLookup [ " $rawchannelmode " ]) ? $MPEGaudioChannelModeLookup [ " $rawchannelmode " ] : false ;
2015-09-04 03:59:46 +02:00
}
2021-02-14 03:39:15 +01:00
function MPEGaudioModeExtensionLookup ( $layer , $rawmodeextension )
{
$MPEGaudioModeExtensionLookup [ 'I' ] = [ '4-31' , '8-31' , '12-31' , '16-31' ];
$MPEGaudioModeExtensionLookup [ 'II' ] = [ '4-31' , '8-31' , '12-31' , '16-31' ];
$MPEGaudioModeExtensionLookup [ 'III' ] = [ '' , 'IS' , 'MS' , 'IS+MS' ];
2015-09-04 03:59:46 +02:00
2021-02-14 03:39:15 +01:00
return isset ( $MPEGaudioModeExtensionLookup [ " $layer " ][ " $rawmodeextension " ]) ? $MPEGaudioModeExtensionLookup [ " $layer " ][ " $rawmodeextension " ] : false ;
2017-05-15 20:37:10 +02:00
}
2021-02-14 03:39:15 +01:00
function MPEGaudioEmphasisLookup ( $rawemphasis )
{
$MPEGaudioEmphasisLookup = [ 'none' , '50/15ms' , false , 'CCIT J.17' ];
return isset ( $MPEGaudioEmphasisLookup [ " $rawemphasis " ]) ? $MPEGaudioEmphasisLookup [ " $rawemphasis " ] : false ;
2017-05-15 20:37:10 +02:00
}
2021-02-14 03:39:15 +01:00
function MPEGaudioCRCLookup ( $CRCbit )
{
// inverse boolean cast :)
if ( $CRCbit == '0' ) {
return true ;
} else {
return false ;
}
2015-09-04 03:59:46 +02:00
}
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net ///
// or http://www.getid3.org ///
/////////////////////////////////////////////////////////////////
// //
// getid3.mp3.php - part of getID3() //
// See getid3.readme.txt for more details //
// //
/////////////////////////////////////////////////////////////////
// number of frames to scan to determine if MPEG-audio sequence is valid
// Lower this number to 5-20 for faster scanning
// Increase this number to 50+ for most accurate detection of valid VBR/CBR
// mpeg-audio streams
define ( 'MPEG_VALID_CHECK_FRAMES' , 35 );
2021-02-14 03:39:15 +01:00
function getMP3headerFilepointer ( & $fd , & $ThisFileInfo )
{
getOnlyMPEGaudioInfo ( $fd , $ThisFileInfo , $ThisFileInfo [ 'avdataoffset' ]);
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ])) {
$ThisFileInfo [ 'audio' ][ 'bitrate_mode' ] = strtolower ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ]);
}
if ((( isset ( $ThisFileInfo [ 'id3v2' ]) && ( $ThisFileInfo [ 'avdataoffset' ] > $ThisFileInfo [ 'id3v2' ][ 'headerlength' ])) || ( ! isset ( $ThisFileInfo [ 'id3v2' ]) && ( $ThisFileInfo [ 'avdataoffset' ] > 0 )))) {
$ThisFileInfo [ 'warning' ] .= " \n " . 'Unknown data before synch ' ;
if ( isset ( $ThisFileInfo [ 'id3v2' ][ 'headerlength' ])) {
$ThisFileInfo [ 'warning' ] .= '(ID3v2 header ends at ' . $ThisFileInfo [ 'id3v2' ][ 'headerlength' ] . ', then ' . ( $ThisFileInfo [ 'avdataoffset' ] - $ThisFileInfo [ 'id3v2' ][ 'headerlength' ]) . ' bytes garbage, ' ;
} else {
$ThisFileInfo [ 'warning' ] .= '(should be at beginning of file, ' ;
}
$ThisFileInfo [ 'warning' ] .= 'synch detected at ' . $ThisFileInfo [ 'avdataoffset' ] . ')' ;
if ( $ThisFileInfo [ 'audio' ][ 'bitrate_mode' ] == 'cbr' ) {
if ( ! empty ( $ThisFileInfo [ 'id3v2' ][ 'headerlength' ]) && (( $ThisFileInfo [ 'avdataoffset' ] - $ThisFileInfo [ 'id3v2' ][ 'headerlength' ]) == $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ])) {
$ThisFileInfo [ 'warning' ] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.' ;
$ThisFileInfo [ 'audio' ][ 'codec' ] = 'LAME' ;
} elseif ( empty ( $ThisFileInfo [ 'id3v2' ][ 'headerlength' ]) && ( $ThisFileInfo [ 'avdataoffset' ] == $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ])) {
$ThisFileInfo [ 'warning' ] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.' ;
$ThisFileInfo [ 'audio' ][ 'codec' ] = 'LAME' ;
}
}
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ]) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'II' )) {
$ThisFileInfo [ 'audio' ][ 'dataformat' ] = 'mp2' ;
} elseif ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ]) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' )) {
$ThisFileInfo [ 'audio' ][ 'dataformat' ] = 'mp1' ;
}
if ( $ThisFileInfo [ 'fileformat' ] == 'mp3' ) {
switch ( $ThisFileInfo [ 'audio' ][ 'dataformat' ]) {
case 'mp1' :
case 'mp2' :
case 'mp3' :
$ThisFileInfo [ 'fileformat' ] = $ThisFileInfo [ 'audio' ][ 'dataformat' ];
break ;
default :
$ThisFileInfo [ 'warning' ] .= " \n " . 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "' . $ThisFileInfo [ 'audio' ][ 'dataformat' ] . '"' ;
break ;
}
}
if ( empty ( $ThisFileInfo [ 'fileformat' ])) {
$ThisFileInfo [ 'error' ] .= " \n " . 'Synch not found' ;
unset ( $ThisFileInfo [ 'fileformat' ]);
unset ( $ThisFileInfo [ 'audio' ][ 'bitrate_mode' ]);
unset ( $ThisFileInfo [ 'avdataoffset' ]);
unset ( $ThisFileInfo [ 'avdataend' ]);
return false ;
}
$ThisFileInfo [ 'mime_type' ] = 'audio/mpeg' ;
$ThisFileInfo [ 'audio' ][ 'lossless' ] = false ;
// Calculate playtime
if ( ! isset ( $ThisFileInfo [ 'playtime_seconds' ]) && isset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ]) && ( $ThisFileInfo [ 'audio' ][ 'bitrate' ] > 0 )) {
$ThisFileInfo [ 'playtime_seconds' ] = ( $ThisFileInfo [ 'avdataend' ] - $ThisFileInfo [ 'avdataoffset' ]) * 8 / $ThisFileInfo [ 'audio' ][ 'bitrate' ];
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ])) {
$ThisFileInfo [ 'audio' ][ 'codec' ] = 'LAME' ;
if ( ! empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ])) {
$ThisFileInfo [ 'audio' ][ 'encoder' ] = trim ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ]);
}
}
return true ;
}
function decodeMPEGaudioHeader ( $fd , $offset , & $ThisFileInfo , $recursivesearch = true , $ScanAsCBR = false , $FastMPEGheaderScan = false )
{
static $MPEGaudioVersionLookup ;
static $MPEGaudioLayerLookup ;
static $MPEGaudioBitrateLookup ;
static $MPEGaudioFrequencyLookup ;
static $MPEGaudioChannelModeLookup ;
static $MPEGaudioModeExtensionLookup ;
static $MPEGaudioEmphasisLookup ;
if ( empty ( $MPEGaudioVersionLookup )) {
$MPEGaudioVersionLookup = MPEGaudioVersionArray ();
$MPEGaudioLayerLookup = MPEGaudioLayerArray ();
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray ();
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray ();
$MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray ();
$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray ();
$MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray ();
}
if ( $offset >= $ThisFileInfo [ 'avdataend' ]) {
$ThisFileInfo [ 'error' ] .= " \n " . 'end of file encounter looking for MPEG synch' ;
return false ;
}
fseek ( $fd , $offset , SEEK_SET );
$headerstring = fread ( $fd , 1441 ); // worse-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
// MP3 audio frame structure:
// $aa $aa $aa $aa [$bb $bb] $cc...
// where $aa..$aa is the four-byte mpeg-audio header (below)
// $bb $bb is the optional 2-byte CRC
// and $cc... is the audio data
$head4 = substr ( $headerstring , 0 , 4 );
static $MPEGaudioHeaderDecodeCache = [];
if ( isset ( $MPEGaudioHeaderDecodeCache [ $head4 ])) {
$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache [ $head4 ];
} else {
$MPEGheaderRawArray = MPEGaudioHeaderDecode ( $head4 );
$MPEGaudioHeaderDecodeCache [ $head4 ] = $MPEGheaderRawArray ;
}
static $MPEGaudioHeaderValidCache = [];
// Not in cache
if ( ! isset ( $MPEGaudioHeaderValidCache [ $head4 ])) {
$MPEGaudioHeaderValidCache [ $head4 ] = MPEGaudioHeaderValid ( $MPEGheaderRawArray );
}
if ( $MPEGaudioHeaderValidCache [ $head4 ]) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ] = $MPEGheaderRawArray ;
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'Invalid MPEG audio header at offset ' . $offset ;
return false ;
}
if ( ! $FastMPEGheaderScan ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] = $MPEGaudioVersionLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'version' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] = $MPEGaudioLayerLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'layer' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] = $MPEGaudioChannelModeLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'channelmode' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channels' ] = (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] == 'mono' ) ? 1 : 2 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ] = $MPEGaudioFrequencyLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ]][ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'sample_rate' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'protection' ] = ! $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'protection' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'private' ] = ( bool ) $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'private' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'modeextension' ] = $MPEGaudioModeExtensionLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ]][ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'modeextension' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'copyright' ] = ( bool ) $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'copyright' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'original' ] = ( bool ) $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'original' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'emphasis' ] = $MPEGaudioEmphasisLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'emphasis' ]];
$ThisFileInfo [ 'audio' ][ 'channels' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channels' ];
$ThisFileInfo [ 'audio' ][ 'sample_rate' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ];
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'protection' ]) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'crc' ] = BigEndian2Int ( substr ( $headerstring , 4 , 2 ));
}
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'bitrate' ] == 15 ) {
// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
$ThisFileInfo [ 'warning' ] .= " \n " . 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'bitrate' ] = 0 ;
}
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'padding' ] = ( bool ) $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'padding' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] = $MPEGaudioBitrateLookup [ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ]][ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ]][ $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'bitrate' ]];
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 'free' ) && ( $offset == $ThisFileInfo [ 'avdataoffset' ])) {
// only skip multiple frame check if free-format bitstream found at beginning of file
// otherwise is quite possibly simply corrupted data
$recursivesearch = false ;
}
// For Layer II there are some combinations of bitrate and mode which are not allowed.
if ( ! $FastMPEGheaderScan && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'II' )) {
$ThisFileInfo [ 'audio' ][ 'dataformat' ] = 'mp2' ;
switch ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ]) {
case 'mono' :
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 'free' ) || ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] <= 192 )) {
// these are ok
} else {
$ThisFileInfo [ 'error' ] .= " \n " . $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] . 'kbps not allowed in Layer II, ' . $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] . '.' ;
return false ;
}
break ;
case 'stereo' :
case 'joint stereo' :
case 'dual channel' :
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 'free' ) || ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 64 ) || ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] >= 96 )) {
// these are ok
} else {
$ThisFileInfo [ 'error' ] .= " \n " . $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] . 'kbps not allowed in Layer II, ' . $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] . '.' ;
return false ;
}
break ;
}
}
if ( $ThisFileInfo [ 'audio' ][ 'sample_rate' ] > 0 ) {
$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' ]);
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] != 'free' ) {
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = 1000 * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ];
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ])) {
$nextframetestoffset = $offset + $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ];
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'Frame at offset(' . $offset . ') is has an invalid frame length.' ;
return false ;
}
}
$ExpectedNumberOfAudioBytes = 0 ;
////////////////////////////////////////////////////////////////////////////////////
// Variable-bitrate headers
if ( substr ( $headerstring , 4 + 32 , 4 ) == 'VBRI' ) {
// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'vbr' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_method' ] = 'Fraunhofer' ;
$ThisFileInfo [ 'audio' ][ 'codec' ] = 'Fraunhofer' ;
$SideInfoData = substr ( $headerstring , 4 + 2 , 32 );
$FraunhoferVBROffset = 36 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_encoder_version' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 4 , 2 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_encoder_delay' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 6 , 2 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_quality' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 8 , 2 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 10 , 4 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 14 , 4 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_seek_offsets' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 18 , 2 ));
//$ThisFileInfo['mpeg']['audio']['reserved'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 4)); // hardcoded $00 $01 $00 $02 - purpose unknown
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_seek_offsets_stride' ] = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset + 24 , 2 ));
$ExpectedNumberOfAudioBytes = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ];
$previousbyteoffset = $offset ;
for ( $i = 0 ; $i < $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_seek_offsets' ]; $i ++ ) {
$Fraunhofer_OffsetN = BigEndian2Int ( substr ( $headerstring , $FraunhoferVBROffset , 2 ));
$FraunhoferVBROffset += 2 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_offsets_relative' ][ $i ] = $Fraunhofer_OffsetN ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_offsets_absolute' ][ $i ] = $Fraunhofer_OffsetN + $previousbyteoffset ;
$previousbyteoffset += $Fraunhofer_OffsetN ;
}
} else {
// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
// depending on MPEG layer and number of channels
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] == '1' ) {
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] == 'mono' ) {
// MPEG-1 (mono)
$VBRidOffset = 4 + 17 ; // 0x15
$SideInfoData = substr ( $headerstring , 4 + 2 , 17 );
} else {
// MPEG-1 (stereo, joint-stereo, dual-channel)
$VBRidOffset = 4 + 32 ; // 0x24
$SideInfoData = substr ( $headerstring , 4 + 2 , 32 );
}
} else { // 2 or 2.5
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ] == 'mono' ) {
// MPEG-2, MPEG-2.5 (mono)
$VBRidOffset = 4 + 9 ; // 0x0D
$SideInfoData = substr ( $headerstring , 4 + 2 , 9 );
} else {
// MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
$VBRidOffset = 4 + 17 ; // 0x15
$SideInfoData = substr ( $headerstring , 4 + 2 , 17 );
}
}
if (( substr ( $headerstring , $VBRidOffset , strlen ( 'Xing' )) == 'Xing' ) || ( substr ( $headerstring , $VBRidOffset , strlen ( 'Info' )) == 'Info' )) {
// 'Xing' is traditional Xing VBR frame
// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'vbr' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_method' ] = 'Xing' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags_raw' ] = BigEndian2Int ( substr ( $headerstring , $VBRidOffset + 4 , 4 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'frames' ] = ( bool ) ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags_raw' ] & 0x00000001 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'bytes' ] = ( bool ) ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags_raw' ] & 0x00000002 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'toc' ] = ( bool ) ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags_raw' ] & 0x00000004 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'vbr_scale' ] = ( bool ) ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags_raw' ] & 0x00000008 );
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'frames' ]) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ] = BigEndian2Int ( substr ( $headerstring , $VBRidOffset + 8 , 4 ));
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'bytes' ]) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] = BigEndian2Int ( substr ( $headerstring , $VBRidOffset + 12 , 4 ));
}
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 'free' ) && ! empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ]) && ! empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ])) {
$framelengthfloat = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] / $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ];
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' ) {
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = ((( $framelengthfloat / 4 ) - intval ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'padding' ])) * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ]) / 12 ;
} else {
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = (( $framelengthfloat - intval ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'padding' ])) * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ]) / 144 ;
}
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ] = floor ( $framelengthfloat );
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'toc' ]) {
$LAMEtocData = substr ( $headerstring , $VBRidOffset + 16 , 100 );
for ( $i = 0 ; $i < 100 ; $i ++ ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'toc' ][ $i ] = ord ( $LAMEtocData [ $i ]);
}
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'xing_flags' ][ 'vbr_scale' ]) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_scale' ] = BigEndian2Int ( substr ( $headerstring , $VBRidOffset + 116 , 4 ));
}
// http://gabriel.mp3-tech.org/mp3infotag.html
if ( substr ( $headerstring , $VBRidOffset + 120 , 4 ) == 'LAME' ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ] = substr ( $headerstring , $VBRidOffset + 120 , 20 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'short_version' ] = substr ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ], 0 , 9 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ] = rtrim ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ], " \x55 \xAA " );
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'short_version' ] >= 'LAME3.90.' ) {
// It the LAME tag was only introduced in LAME v3.90
// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
// are assuming a 'Xing' identifier offset of 0x24, which is the case for
// MPEG-1 non-mono, but not for other combinations
$LAMEtagOffsetContant = $VBRidOffset - 0x24 ;
// byte $9B VBR Quality
// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
// Actually overwrites original Xing bytes
unset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_scale' ]);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'vbr_quality' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0x9B , 1 ));
// bytes $9C-$A4 Encoder short VersionString
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'short_version' ] = substr ( $headerstring , $LAMEtagOffsetContant + 0x9C , 9 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'long_version' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'short_version' ];
// byte $A5 Info Tag revision + VBR method
$LAMEtagRevisionVBRmethod = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xA5 , 1 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'tag_revision' ] = ( $LAMEtagRevisionVBRmethod & 0xF0 ) >> 4 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'vbr_method' ] = $LAMEtagRevisionVBRmethod & 0x0F ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'vbr_method' ] = LAMEvbrMethodLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'vbr_method' ]);
// byte $A6 Lowpass filter value
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'lowpass_frequency' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xA6 , 1 )) * 100 ;
// bytes $A7-$AE Replay Gain
// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ] = BigEndian2Float ( substr ( $headerstring , $LAMEtagOffsetContant + 0xA7 , 4 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xAB , 2 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xAD , 2 ));
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ] == 0 ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ] = false ;
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] != 0 ) {
require_once GETID3_INCLUDEPATH . 'getid3.rgad.php' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'name' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] & 0xE000 ) >> 13 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'originator' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] & 0x1C00 ) >> 10 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'sign_bit' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] & 0x0200 ) >> 9 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'gain_adjust' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_radio' ] & 0x01FF ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'name' ] = RGADnameLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'name' ]);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'originator' ] = RGADoriginatorLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'raw' ][ 'originator' ]);
$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' ]);
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ] !== false ) {
$ThisFileInfo [ 'replay_gain' ][ 'radio' ][ 'peak' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ];
}
$ThisFileInfo [ 'replay_gain' ][ 'radio' ][ 'originator' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'originator' ];
$ThisFileInfo [ 'replay_gain' ][ 'radio' ][ 'adjustment' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'radio' ][ 'gain_db' ];
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] != 0 ) {
require_once GETID3_INCLUDEPATH . 'getid3.rgad.php' ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'name' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] & 0xE000 ) >> 13 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'originator' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] & 0x1C00 ) >> 10 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'sign_bit' ] = ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] & 0x0200 ) >> 9 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'gain_adjust' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'RGAD_audiophile' ] & 0x01FF ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'name' ] = RGADnameLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'name' ]);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'originator' ] = RGADoriginatorLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'raw' ][ 'originator' ]);
$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' ]);
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ] !== false ) {
$ThisFileInfo [ 'replay_gain' ][ 'audiophile' ][ 'peak' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'peak_amplitude' ];
}
$ThisFileInfo [ 'replay_gain' ][ 'audiophile' ][ 'originator' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'originator' ];
$ThisFileInfo [ 'replay_gain' ][ 'audiophile' ][ 'adjustment' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'RGAD' ][ 'audiophile' ][ 'gain_db' ];
}
// byte $AF Encoding flags + ATH Type
$EncodingFlagsATHtype = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xAF , 1 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'encoding_flags' ][ 'nspsytune' ] = ( bool ) ( $EncodingFlagsATHtype & 0x10 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'encoding_flags' ][ 'nssafejoint' ] = ( bool ) ( $EncodingFlagsATHtype & 0x20 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'encoding_flags' ][ 'nogap_next' ] = ( bool ) ( $EncodingFlagsATHtype & 0x40 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'encoding_flags' ][ 'nogap_prev' ] = ( bool ) ( $EncodingFlagsATHtype & 0x80 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'ath_type' ] = $EncodingFlagsATHtype & 0x0F ;
// byte $B0 if ABR {specified bitrate} else {minimal bitrate}
$ABRbitrateMinBitrate = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB0 , 1 ));
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'vbr_method' ] == 2 ) { // Average BitRate (ABR)
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'bitrate_abr' ] = $ABRbitrateMinBitrate ;
} elseif ( $ABRbitrateMinBitrate > 0 ) { // Variable BitRate (VBR) - minimum bitrate
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'bitrate_min' ] = $ABRbitrateMinBitrate ;
}
// bytes $B1-$B3 Encoder delays
$EncoderDelays = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB1 , 3 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'encoder_delay' ] = ( $EncoderDelays & 0xFFF000 ) >> 12 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'end_padding' ] = $EncoderDelays & 0x000FFF ;
// byte $B4 Misc
$MiscByte = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB4 , 1 ));
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'noise_shaping' ] = ( $MiscByte & 0x03 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'stereo_mode' ] = ( $MiscByte & 0x1C ) >> 2 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'not_optimal_quality' ] = ( $MiscByte & 0x20 ) >> 5 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'source_sample_freq' ] = ( $MiscByte & 0xC0 ) >> 6 ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'noise_shaping' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'noise_shaping' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'stereo_mode' ] = LAMEmiscStereoModeLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'stereo_mode' ]);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'not_optimal_quality' ] = ( bool ) $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'not_optimal_quality' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'source_sample_freq' ] = LAMEmiscSourceSampleFrequencyLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'source_sample_freq' ]);
// byte $B5 MP3 Gain
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'mp3_gain' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB5 , 1 ), false , true );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'mp3_gain_db' ] = 1.5 * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'mp3_gain' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'mp3_gain_factor' ] = pow ( 2 , ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'mp3_gain_db' ] / 6 ));
// bytes $B6-$B7 Preset and surround info
$PresetSurroundBytes = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB6 , 2 ));
// Reserved = ($PresetSurroundBytes & 0xC000);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'surround_info' ] = ( $PresetSurroundBytes & 0x3800 );
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'surround_info' ] = LAMEsurroundInfoLookup ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'surround_info' ]);
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'preset_used_id' ] = ( $PresetSurroundBytes & 0x07FF );
// bytes $B8-$BB MusicLength
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'audio_bytes' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xB8 , 4 ));
$ExpectedNumberOfAudioBytes = (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'audio_bytes' ] > 0 ) ? $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'audio_bytes' ] : $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ]);
// bytes $BC-$BD MusicCRC
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'music_crc' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xBC , 2 ));
// bytes $BE-$BF CRC-16 of Info Tag
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'lame_tag_crc' ] = BigEndian2Int ( substr ( $headerstring , $LAMEtagOffsetContant + 0xBE , 2 ));
// LAME CBR
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'raw' ][ 'vbr_method' ] == 1 ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'cbr' ;
if ( empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ]) || ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'bitrate_min' ] != 255 )) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'LAME' ][ 'bitrate_min' ];
}
}
}
}
} else {
// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'cbr' ;
if ( $recursivesearch ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'vbr' ;
if ( RecursiveFrameScanning ( $fd , $ThisFileInfo , $offset , $nextframetestoffset , true )) {
$recursivesearch = false ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'cbr' ;
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] == 'vbr' ) {
$ThisFileInfo [ 'warning' ] .= " \n " . 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.' ;
}
}
}
}
if (( $ExpectedNumberOfAudioBytes > 0 ) && ( $ExpectedNumberOfAudioBytes != ( $ThisFileInfo [ 'avdataend' ] - $ThisFileInfo [ 'avdataoffset' ]))) {
if (( $ExpectedNumberOfAudioBytes - ( $ThisFileInfo [ 'avdataend' ] - $ThisFileInfo [ 'avdataoffset' ])) == 1 ) {
$ThisFileInfo [ 'warning' ] .= " \n " . 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)' ;
} elseif ( $ExpectedNumberOfAudioBytes > ( $ThisFileInfo [ 'avdataend' ] - $ThisFileInfo [ 'avdataoffset' ])) {
$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)' ;
} else {
$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)' ;
}
}
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] == 'free' ) && empty ( $ThisFileInfo [ 'audio' ][ 'bitrate' ])) {
if (( $offset == $ThisFileInfo [ 'avdataoffset' ]) && empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ])) {
$framebytelength = FreeFormatFrameLength ( $fd , $offset , $ThisFileInfo , true );
if ( $framebytelength > 0 ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ] = $framebytelength ;
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' ) {
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = ((( $framebytelength / 4 ) - intval ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'padding' ])) * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ]) / 12 ;
} else {
// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = (( $framebytelength - intval ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'padding' ])) * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ]) / 144 ;
}
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'Error calculating frame length of free-format MP3 without Xing/LAME header' ;
}
}
}
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] == 'vbr' ) && isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ]) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ] > 1 )) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ] -- ; // don't count the Xing / VBRI frame
if (( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] == '1' ) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' )) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ] = ((( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] / $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ]) * 8 ) * ( $ThisFileInfo [ 'audio' ][ 'sample_rate' ] / 384 )) / 1000 ;
} elseif ((( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] == '2' ) || ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] == '2.5' )) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'III' )) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ] = ((( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] / $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ]) * 8 ) * ( $ThisFileInfo [ 'audio' ][ 'sample_rate' ] / 576 )) / 1000 ;
} else {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ] = ((( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bytes' ] / $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_frames' ]) * 8 ) * ( $ThisFileInfo [ 'audio' ][ 'sample_rate' ] / 1152 )) / 1000 ;
}
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ] > 0 ) {
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = 1000 * $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_bitrate' ]; // to avoid confusion
}
}
// End variable-bitrate headers
////////////////////////////////////////////////////////////////////////////////////
if ( $recursivesearch ) {
if ( ! RecursiveFrameScanning ( $fd , $ThisFileInfo , $offset , $nextframetestoffset , $ScanAsCBR )) {
return false ;
}
}
//if (false) {
// // experimental side info parsing section - not returning anything useful yet
//
// $SideInfoBitstream = BigEndian2Bin($SideInfoData);
// $SideInfoOffset = 0;
//
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
// if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
// // MPEG-1 (mono)
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
// $SideInfoOffset += 9;
// $SideInfoOffset += 5;
// } else {
// // MPEG-1 (stereo, joint-stereo, dual-channel)
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
// $SideInfoOffset += 9;
// $SideInfoOffset += 3;
// }
// } else { // 2 or 2.5
// if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
// // MPEG-2, MPEG-2.5 (mono)
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
// $SideInfoOffset += 8;
// $SideInfoOffset += 1;
// } else {
// // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
// $ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
// $SideInfoOffset += 8;
// $SideInfoOffset += 2;
// }
// }
//
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
// for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
// for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
// $ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 2;
// }
// }
// }
// for ($granule = 0; $granule < (($ThisFileInfo['mpeg']['audio']['version'] == '1') ? 2 : 1); $granule++) {
// for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
// $ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
// $SideInfoOffset += 12;
// $ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
// $SideInfoOffset += 9;
// $ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
// $SideInfoOffset += 8;
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
// $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
// $SideInfoOffset += 4;
// } else {
// $ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
// $SideInfoOffset += 9;
// }
// $ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 1;
//
// if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') {
//
// $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
// $SideInfoOffset += 2;
// $ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 1;
//
// for ($region = 0; $region < 2; $region++) {
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
// $SideInfoOffset += 5;
// }
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0;
//
// for ($window = 0; $window < 3; $window++) {
// $ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
// $SideInfoOffset += 3;
// }
//
// } else {
//
// for ($region = 0; $region < 3; $region++) {
// $ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
// $SideInfoOffset += 5;
// }
//
// $ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
// $SideInfoOffset += 4;
// $ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
// $SideInfoOffset += 3;
// $ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0;
// }
//
// if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
// $ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 1;
// }
// $ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 1;
// $ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
// $SideInfoOffset += 1;
// }
// }
//}
return true ;
}
function RecursiveFrameScanning ( & $fd , & $ThisFileInfo , & $offset , & $nextframetestoffset , $ScanAsCBR )
{
for ( $i = 0 ; $i < MPEG_VALID_CHECK_FRAMES ; $i ++ ) {
// check next MPEG_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
if (( $nextframetestoffset + 4 ) >= $ThisFileInfo [ 'avdataend' ]) {
// end of file
return true ;
}
$nextframetestarray = [ 'error' => '' , 'warning' => '' , 'avdataend' => $ThisFileInfo [ 'avdataend' ], 'avdataoffset' => $ThisFileInfo [ 'avdataoffset' ]];
if ( decodeMPEGaudioHeader ( $fd , $nextframetestoffset , $nextframetestarray , false )) {
if ( $ScanAsCBR ) {
// force CBR mode, used for trying to pick out invalid audio streams with
// valid(?) VBR headers, or VBR streams with no VBR header
if ( ! isset ( $nextframetestarray [ 'mpeg' ][ 'audio' ][ 'bitrate' ]) || ! isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ]) || ( $nextframetestarray [ 'mpeg' ][ 'audio' ][ 'bitrate' ] != $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ])) {
return false ;
}
}
// next frame is OK, get ready to check the one after that
if ( isset ( $nextframetestarray [ 'mpeg' ][ 'audio' ][ 'framelength' ]) && ( $nextframetestarray [ 'mpeg' ][ 'audio' ][ 'framelength' ] > 0 )) {
$nextframetestoffset += $nextframetestarray [ 'mpeg' ][ 'audio' ][ 'framelength' ];
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'Frame at offset (' . $offset . ') is has an invalid frame length.' ;
return false ;
}
} else {
// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
$ThisFileInfo [ 'error' ] .= " \n " . 'Frame at offset (' . $offset . ') is valid, but the next one at (' . $nextframetestoffset . ') is not.' ;
return false ;
}
}
return true ;
}
function FreeFormatFrameLength ( $fd , $offset , & $ThisFileInfo , $deepscan = false )
{
fseek ( $fd , $offset , SEEK_SET );
$MPEGaudioData = fread ( $fd , 32768 );
$SyncPattern1 = substr ( $MPEGaudioData , 0 , 4 );
// may be different pattern due to padding
$SyncPattern2 = $SyncPattern1 [ 0 ] . $SyncPattern1 [ 1 ] . chr ( ord ( $SyncPattern1 [ 2 ]) | 0x02 ) . $SyncPattern1 [ 3 ];
if ( $SyncPattern2 === $SyncPattern1 ) {
$SyncPattern2 = $SyncPattern1 [ 0 ] . $SyncPattern1 [ 1 ] . chr ( ord ( $SyncPattern1 [ 2 ]) & 0xFD ) . $SyncPattern1 [ 3 ];
}
$framelength = false ;
$framelength1 = strpos ( $MPEGaudioData , $SyncPattern1 , 4 );
$framelength2 = strpos ( $MPEGaudioData , $SyncPattern2 , 4 );
if ( $framelength1 > 4 ) {
$framelength = $framelength1 ;
}
if (( $framelength2 > 4 ) && ( $framelength2 < $framelength1 )) {
$framelength = $framelength2 ;
}
if ( ! $framelength ) {
// LAME 3.88 has a different value for modeextension on the first frame vs the rest
$framelength1 = strpos ( $MPEGaudioData , substr ( $SyncPattern1 , 0 , 3 ), 4 );
$framelength2 = strpos ( $MPEGaudioData , substr ( $SyncPattern2 , 0 , 3 ), 4 );
if ( $framelength1 > 4 ) {
$framelength = $framelength1 ;
}
if (( $framelength2 > 4 ) && ( $framelength2 < $framelength1 )) {
$framelength = $framelength2 ;
}
if ( ! $framelength ) {
$ThisFileInfo [ 'error' ] .= " \n " . 'Cannot find next free-format synch pattern (' . PrintHexBytes ( $SyncPattern1 ) . ' or ' . PrintHexBytes ( $SyncPattern2 ) . ') after offset ' . $offset ;
return false ;
} else {
$ThisFileInfo [ 'warning' ] .= " \n " . 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)' ;
$ThisFileInfo [ 'audio' ][ 'codec' ] = 'LAME' ;
$ThisFileInfo [ 'audio' ][ 'encoder' ] = 'LAME3.88' ;
$SyncPattern1 = substr ( $SyncPattern1 , 0 , 3 );
$SyncPattern2 = substr ( $SyncPattern2 , 0 , 3 );
}
}
if ( $deepscan ) {
$ActualFrameLengthValues = [];
$nextoffset = $offset + $framelength ;
while ( $nextoffset < ( $ThisFileInfo [ 'avdataend' ] - 6 )) {
fseek ( $fd , $nextoffset - 1 , SEEK_SET );
$NextSyncPattern = fread ( $fd , 6 );
if (( substr ( $NextSyncPattern , 1 , strlen ( $SyncPattern1 )) == $SyncPattern1 ) || ( substr ( $NextSyncPattern , 1 , strlen ( $SyncPattern2 )) == $SyncPattern2 )) {
// good - found where expected
$ActualFrameLengthValues [] = $framelength ;
} elseif (( substr ( $NextSyncPattern , 0 , strlen ( $SyncPattern1 )) == $SyncPattern1 ) || ( substr ( $NextSyncPattern , 0 , strlen ( $SyncPattern2 )) == $SyncPattern2 )) {
// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
$ActualFrameLengthValues [] = ( $framelength - 1 );
$nextoffset -- ;
} elseif (( substr ( $NextSyncPattern , 2 , strlen ( $SyncPattern1 )) == $SyncPattern1 ) || ( substr ( $NextSyncPattern , 2 , strlen ( $SyncPattern2 )) == $SyncPattern2 )) {
// ok - found one byte later than expected (last frame was padded, first frame wasn't)
$ActualFrameLengthValues [] = ( $framelength + 1 );
$nextoffset ++ ;
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'Did not find expected free-format sync pattern at offset ' . $nextoffset ;
return false ;
}
$nextoffset += $framelength ;
}
if ( count ( $ActualFrameLengthValues ) > 0 ) {
$framelength = round ( array_sum ( $ActualFrameLengthValues ) / count ( $ActualFrameLengthValues ));
}
}
return $framelength ;
}
function getOnlyMPEGaudioInfo ( $fd , & $ThisFileInfo , $avdataoffset , $BitrateHistogram = false )
{
// looks for synch, decodes MPEG audio header
fseek ( $fd , $avdataoffset , SEEK_SET );
$header = '' ;
$SynchSeekOffset = 0 ;
if ( ! defined ( 'CONST_FF' )) {
define ( 'CONST_FF' , chr ( 0xFF ));
define ( 'CONST_E0' , chr ( 0xE0 ));
}
static $MPEGaudioVersionLookup ;
static $MPEGaudioLayerLookup ;
static $MPEGaudioBitrateLookup ;
if ( empty ( $MPEGaudioVersionLookup )) {
$MPEGaudioVersionLookup = MPEGaudioVersionArray ();
$MPEGaudioLayerLookup = MPEGaudioLayerArray ();
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray ();
}
$header_len = strlen ( $header ) - round ( 32768 / 2 );
while ( true ) {
if (( $SynchSeekOffset > $header_len ) && (( $avdataoffset + $SynchSeekOffset ) < $ThisFileInfo [ 'avdataend' ]) && ! feof ( $fd )) {
if ( $SynchSeekOffset > 131072 ) {
// if a synch's not found within the first 128k bytes, then give up
$ThisFileInfo [ 'error' ] .= " \n " . 'could not find valid MPEG synch within the first 131072 bytes' ;
if ( isset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ])) {
unset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ])) {
unset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ]) && ( ! is_array ( $ThisFileInfo [ 'mpeg' ]) || ( count ( $ThisFileInfo [ 'mpeg' ]) == 0 ))) {
unset ( $ThisFileInfo [ 'mpeg' ]);
}
return false ;
} elseif ( $header .= fread ( $fd , 32768 )) {
// great
$header_len = strlen ( $header ) - round ( 32768 / 2 );
} else {
$ThisFileInfo [ 'error' ] .= " \n " . 'could not find valid MPEG synch before end of file' ;
if ( isset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ])) {
unset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ])) {
unset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ]) && ( ! is_array ( $ThisFileInfo [ 'mpeg' ]) || ( count ( $ThisFileInfo [ 'mpeg' ]) == 0 ))) {
unset ( $ThisFileInfo [ 'mpeg' ]);
}
return false ;
}
}
if (( $SynchSeekOffset + 1 ) >= strlen ( $header )) {
$ThisFileInfo [ 'error' ] .= " \n " . 'could not find valid MPEG synch before end of file' ;
return false ;
}
if (( $header [ $SynchSeekOffset ] == CONST_FF ) && ( $header [( $SynchSeekOffset + 1 )] > CONST_E0 )) { // synch detected
if ( ! isset ( $FirstFrameThisfileInfo ) && ! isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ])) {
$FirstFrameThisfileInfo = $ThisFileInfo ;
$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset ;
if ( ! decodeMPEGaudioHeader ( $fd , $avdataoffset + $SynchSeekOffset , $FirstFrameThisfileInfo , false )) {
// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
unset ( $FirstFrameThisfileInfo );
}
}
$dummy = $ThisFileInfo ; // only overwrite real data if valid header found
if ( decodeMPEGaudioHeader ( $fd , $avdataoffset + $SynchSeekOffset , $dummy , true )) {
$ThisFileInfo = $dummy ;
$ThisFileInfo [ 'avdataoffset' ] = $avdataoffset + $SynchSeekOffset ;
switch ( $ThisFileInfo [ 'fileformat' ]) {
case '' :
case 'id3' :
case 'ape' :
case 'mp3' :
$ThisFileInfo [ 'fileformat' ] = 'mp3' ;
$ThisFileInfo [ 'audio' ][ 'dataformat' ] = 'mp3' ;
}
if ( isset ( $FirstFrameThisfileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ]) && ( $FirstFrameThisfileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] == 'vbr' )) {
if ( ! CloseMatch ( $ThisFileInfo [ 'audio' ][ 'bitrate' ], $FirstFrameThisfileInfo [ 'audio' ][ 'bitrate' ], 1 )) {
// If there is garbage data between a valid VBR header frame and a sequence
// of valid MPEG-audio frames the VBR data is no longer discarded.
$ThisFileInfo = $FirstFrameThisfileInfo ;
$ThisFileInfo [ 'avdataoffset' ] = $FirstFrameAVDataOffset ;
$ThisFileInfo [ 'fileformat' ] = 'mp3' ;
$ThisFileInfo [ 'audio' ][ 'dataformat' ] = 'mp3' ;
$dummy = $ThisFileInfo ;
unset ( $dummy [ 'mpeg' ][ 'audio' ]);
$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo [ 'mpeg' ][ 'audio' ][ 'framelength' ];
$GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset ;
if ( decodeMPEGaudioHeader ( $fd , $GarbageOffsetEnd , $dummy , true , true )) {
$ThisFileInfo = $dummy ;
$ThisFileInfo [ 'avdataoffset' ] = $GarbageOffsetEnd ;
$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 ;
} else {
$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 . ')' ;
}
}
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ]) && ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] == 'vbr' ) && ! isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'VBR_method' ])) {
// VBR file with no VBR header
$BitrateHistogram = true ;
}
if ( $BitrateHistogram ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'stereo_distribution' ] = [ 'stereo' => 0 , 'joint stereo' => 0 , 'dual channel' => 0 , 'mono' => 0 ];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version_distribution' ] = [ '1' => 0 , '2' => 0 , '2.5' => 0 ];
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version' ] == '1' ) {
if ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'III' ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] = [ '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 ];
} elseif ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'II' ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] = [ '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 ];
} elseif ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] = [ '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 ];
}
} elseif ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'layer' ] == 'I' ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] = [ '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 ];
} else {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] = [ '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 ];
}
$dummy = [ 'error' => $ThisFileInfo [ 'error' ], 'warning' => $ThisFileInfo [ 'warning' ], 'avdataend' => $ThisFileInfo [ 'avdataend' ], 'avdataoffset' => $ThisFileInfo [ 'avdataoffset' ]];
$synchstartoffset = $ThisFileInfo [ 'avdataoffset' ];
$FastMode = false ;
while ( decodeMPEGaudioHeader ( $fd , $synchstartoffset , $dummy , false , false , $FastMode )) {
$FastMode = true ;
$thisframebitrate = $MPEGaudioBitrateLookup [ $MPEGaudioVersionLookup [ $dummy [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'version' ]]][ $MPEGaudioLayerLookup [ $dummy [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'layer' ]]][ $dummy [ 'mpeg' ][ 'audio' ][ 'raw' ][ 'bitrate' ]];
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ][ $thisframebitrate ] ++ ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'stereo_distribution' ][ $dummy [ 'mpeg' ][ 'audio' ][ 'channelmode' ]] ++ ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'version_distribution' ][ $dummy [ 'mpeg' ][ 'audio' ][ 'version' ]] ++ ;
if ( empty ( $dummy [ 'mpeg' ][ 'audio' ][ 'framelength' ])) {
$ThisFileInfo [ 'warning' ] .= " \n " . 'Invalid/missing framelength in histogram analysis - aborting' ;
$synchstartoffset += 4 ;
// return false;
}
$synchstartoffset += $dummy [ 'mpeg' ][ 'audio' ][ 'framelength' ];
}
$bittotal = 0 ;
$framecounter = 0 ;
foreach ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] as $bitratevalue => $bitratecount ) {
$framecounter += $bitratecount ;
if ( $bitratevalue != 'free' ) {
$bittotal += ( $bitratevalue * $bitratecount );
}
}
if ( $framecounter == 0 ) {
$ThisFileInfo [ 'error' ] .= " \n " . 'Corrupt MP3 file: framecounter == zero' ;
return false ;
}
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'frame_count' ] = $framecounter ;
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ] = 1000 * ( $bittotal / $framecounter );
$ThisFileInfo [ 'audio' ][ 'bitrate' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate' ];
// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
$distinct_bitrates = 0 ;
foreach ( $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_distribution' ] as $bitrate_value => $bitrate_count ) {
if ( $bitrate_count > 0 ) {
$distinct_bitrates ++ ;
}
}
if ( $distinct_bitrates > 1 ) {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'vbr' ;
} else {
$ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ] = 'cbr' ;
}
$ThisFileInfo [ 'audio' ][ 'bitrate_mode' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'bitrate_mode' ];
}
break ; // exit while()
}
}
$SynchSeekOffset ++ ;
if (( $avdataoffset + $SynchSeekOffset ) >= $ThisFileInfo [ 'avdataend' ]) {
// end of file/data
if ( empty ( $ThisFileInfo [ 'mpeg' ][ 'audio' ])) {
$ThisFileInfo [ 'error' ] .= " \n " . 'could not find valid MPEG synch before end of file' ;
if ( isset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ])) {
unset ( $ThisFileInfo [ 'audio' ][ 'bitrate' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ])) {
unset ( $ThisFileInfo [ 'mpeg' ][ 'audio' ]);
}
if ( isset ( $ThisFileInfo [ 'mpeg' ]) && ( ! is_array ( $ThisFileInfo [ 'mpeg' ]) || empty ( $ThisFileInfo [ 'mpeg' ]))) {
unset ( $ThisFileInfo [ 'mpeg' ]);
}
return false ;
}
break ;
}
}
$ThisFileInfo [ 'audio' ][ 'bits_per_sample' ] = 16 ;
$ThisFileInfo [ 'audio' ][ 'channels' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channels' ];
$ThisFileInfo [ 'audio' ][ 'channelmode' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'channelmode' ];
$ThisFileInfo [ 'audio' ][ 'sample_rate' ] = $ThisFileInfo [ 'mpeg' ][ 'audio' ][ 'sample_rate' ];
return true ;
}
function MPEGaudioVersionArray ()
{
static $MPEGaudioVersion = [ '2.5' , false , '2' , '1' ];
return $MPEGaudioVersion ;
}
function MPEGaudioLayerArray ()
{
static $MPEGaudioLayer = [ false , 'III' , 'II' , 'I' ];
return $MPEGaudioLayer ;
}
function MPEGaudioBitrateArray ()
{
static $MPEGaudioBitrate ;
if ( empty ( $MPEGaudioBitrate )) {
$MPEGaudioBitrate [ '1' ][ 'I' ] = [ 'free' , 32 , 64 , 96 , 128 , 160 , 192 , 224 , 256 , 288 , 320 , 352 , 384 , 416 , 448 ];
$MPEGaudioBitrate [ '1' ][ 'II' ] = [ 'free' , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , 384 ];
$MPEGaudioBitrate [ '1' ][ 'III' ] = [ 'free' , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 ];
$MPEGaudioBitrate [ '2' ][ 'I' ] = [ 'free' , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 224 , 256 ];
$MPEGaudioBitrate [ '2' ][ 'II' ] = [ 'free' , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 ];
$MPEGaudioBitrate [ '2' ][ 'III' ] = $MPEGaudioBitrate [ '2' ][ 'II' ];
$MPEGaudioBitrate [ '2.5' ][ 'I' ] = $MPEGaudioBitrate [ '2' ][ 'I' ];
$MPEGaudioBitrate [ '2.5' ][ 'II' ] = $MPEGaudioBitrate [ '2' ][ 'II' ];
$MPEGaudioBitrate [ '2.5' ][ 'III' ] = $MPEGaudioBitrate [ '2' ][ 'III' ];
}
return $MPEGaudioBitrate ;
}
function MPEGaudioFrequencyArray ()
{
static $MPEGaudioFrequency ;
if ( empty ( $MPEGaudioFrequency )) {
$MPEGaudioFrequency [ '1' ] = [ 44100 , 48000 , 32000 ];
$MPEGaudioFrequency [ '2' ] = [ 22050 , 24000 , 16000 ];
$MPEGaudioFrequency [ '2.5' ] = [ 11025 , 12000 , 8000 ];
}
return $MPEGaudioFrequency ;
}
function MPEGaudioChannelModeArray ()
{
static $MPEGaudioChannelMode = [ 'stereo' , 'joint stereo' , 'dual channel' , 'mono' ];
return $MPEGaudioChannelMode ;
}
function MPEGaudioModeExtensionArray ()
{
static $MPEGaudioModeExtension ;
if ( empty ( $MPEGaudioModeExtension )) {
$MPEGaudioModeExtension [ 'I' ] = [ '4-31' , '8-31' , '12-31' , '16-31' ];
$MPEGaudioModeExtension [ 'II' ] = [ '4-31' , '8-31' , '12-31' , '16-31' ];
$MPEGaudioModeExtension [ 'III' ] = [ '' , 'IS' , 'MS' , 'IS+MS' ];
}
return $MPEGaudioModeExtension ;
}
function MPEGaudioEmphasisArray ()
{
static $MPEGaudioEmphasis = [ 'none' , '50/15ms' , false , 'CCIT J.17' ];
return $MPEGaudioEmphasis ;
}
function MPEGaudioHeaderBytesValid ( $head4 )
{
return MPEGaudioHeaderValid ( MPEGaudioHeaderDecode ( $head4 ));
}
function MPEGaudioHeaderValid ( $rawarray , $echoerrors = false )
{
if (( $rawarray [ 'synch' ] & 0x0FFE ) != 0x0FFE ) {
return false ;
}
static $MPEGaudioVersionLookup ;
static $MPEGaudioLayerLookup ;
static $MPEGaudioBitrateLookup ;
static $MPEGaudioFrequencyLookup ;
static $MPEGaudioChannelModeLookup ;
static $MPEGaudioModeExtensionLookup ;
static $MPEGaudioEmphasisLookup ;
if ( empty ( $MPEGaudioVersionLookup )) {
$MPEGaudioVersionLookup = MPEGaudioVersionArray ();
$MPEGaudioLayerLookup = MPEGaudioLayerArray ();
$MPEGaudioBitrateLookup = MPEGaudioBitrateArray ();
$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray ();
$MPEGaudioChannelModeLookup = MPEGaudioChannelModeArray ();
$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray ();
$MPEGaudioEmphasisLookup = MPEGaudioEmphasisArray ();
}
if ( isset ( $MPEGaudioVersionLookup [ $rawarray [ 'version' ]])) {
$decodedVersion = $MPEGaudioVersionLookup [ $rawarray [ 'version' ]];
} else {
if ( $echoerrors ) {
echo " \n " . 'invalid Version (' . $rawarray [ 'version' ] . ')' ;
}
return false ;
}
if ( isset ( $MPEGaudioLayerLookup [ $rawarray [ 'layer' ]])) {
$decodedLayer = $MPEGaudioLayerLookup [ $rawarray [ 'layer' ]];
} else {
if ( $echoerrors ) {
echo " \n " . 'invalid Layer (' . $rawarray [ 'layer' ] . ')' ;
}
return false ;
}
if ( ! isset ( $MPEGaudioBitrateLookup [ $decodedVersion ][ $decodedLayer ][ $rawarray [ 'bitrate' ]])) {
if ( $echoerrors ) {
echo " \n " . 'invalid Bitrate (' . $rawarray [ 'bitrate' ] . ')' ;
}
if ( $rawarray [ 'bitrate' ] == 15 ) {
// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
// let it go through here otherwise file will not be identified
} else {
return false ;
}
}
if ( ! isset ( $MPEGaudioFrequencyLookup [ $decodedVersion ][ $rawarray [ 'sample_rate' ]])) {
if ( $echoerrors ) {
echo " \n " . 'invalid Frequency (' . $rawarray [ 'sample_rate' ] . ')' ;
}
return false ;
}
if ( ! isset ( $MPEGaudioChannelModeLookup [ $rawarray [ 'channelmode' ]])) {
if ( $echoerrors ) {
echo " \n " . 'invalid ChannelMode (' . $rawarray [ 'channelmode' ] . ')' ;
}
return false ;
}
if ( ! isset ( $MPEGaudioModeExtensionLookup [ $decodedLayer ][ $rawarray [ 'modeextension' ]])) {
if ( $echoerrors ) {
echo " \n " . 'invalid Mode Extension (' . $rawarray [ 'modeextension' ] . ')' ;
}
return false ;
}
if ( ! isset ( $MPEGaudioEmphasisLookup [ $rawarray [ 'emphasis' ]])) {
if ( $echoerrors ) {
echo " \n " . 'invalid Emphasis (' . $rawarray [ 'emphasis' ] . ')' ;
}
return false ;
}
// These are just either set or not set, you can't mess that up :)
// $rawarray['protection'];
// $rawarray['padding'];
// $rawarray['private'];
// $rawarray['copyright'];
// $rawarray['original'];
return true ;
}
function MPEGaudioHeaderDecode ( $Header4Bytes )
{
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
// A - Frame sync (all bits set)
// B - MPEG Audio version ID
// C - Layer description
// D - Protection bit
// E - Bitrate index
// F - Sampling rate frequency index
// G - Padding bit
// H - Private bit
// I - Channel Mode
// J - Mode extension (Only if Joint stereo)
// K - Copyright
// L - Original
// M - Emphasis
if ( strlen ( $Header4Bytes ) != 4 ) {
return false ;
}
$MPEGrawHeader [ 'synch' ] = ( BigEndian2Int ( substr ( $Header4Bytes , 0 , 2 )) & 0xFFE0 ) >> 4 ;
$MPEGrawHeader [ 'version' ] = ( ord ( $Header4Bytes [ 1 ]) & 0x18 ) >> 3 ; // BB
$MPEGrawHeader [ 'layer' ] = ( ord ( $Header4Bytes [ 1 ]) & 0x06 ) >> 1 ; // CC
$MPEGrawHeader [ 'protection' ] = ( ord ( $Header4Bytes [ 1 ]) & 0x01 ); // D
$MPEGrawHeader [ 'bitrate' ] = ( ord ( $Header4Bytes [ 2 ]) & 0xF0 ) >> 4 ; // EEEE
$MPEGrawHeader [ 'sample_rate' ] = ( ord ( $Header4Bytes [ 2 ]) & 0x0C ) >> 2 ; // FF
$MPEGrawHeader [ 'padding' ] = ( ord ( $Header4Bytes [ 2 ]) & 0x02 ) >> 1 ; // G
$MPEGrawHeader [ 'private' ] = ( ord ( $Header4Bytes [ 2 ]) & 0x01 ); // H
$MPEGrawHeader [ 'channelmode' ] = ( ord ( $Header4Bytes [ 3 ]) & 0xC0 ) >> 6 ; // II
$MPEGrawHeader [ 'modeextension' ] = ( ord ( $Header4Bytes [ 3 ]) & 0x30 ) >> 4 ; // JJ
$MPEGrawHeader [ 'copyright' ] = ( ord ( $Header4Bytes [ 3 ]) & 0x08 ) >> 3 ; // K
$MPEGrawHeader [ 'original' ] = ( ord ( $Header4Bytes [ 3 ]) & 0x04 ) >> 2 ; // L
$MPEGrawHeader [ 'emphasis' ] = ( ord ( $Header4Bytes [ 3 ]) & 0x03 ); // MM
return $MPEGrawHeader ;
}
function MPEGaudioFrameLength ( & $bitrate , & $version , & $layer , $padding , & $samplerate )
{
static $AudioFrameLengthCache = [];
if ( ! isset ( $AudioFrameLengthCache [ $bitrate ][ $version ][ $layer ][ $padding ][ $samplerate ])) {
$AudioFrameLengthCache [ $bitrate ][ $version ][ $layer ][ $padding ][ $samplerate ] = false ;
if ( $bitrate != 'free' ) {
if ( $version == '1' ) {
if ( $layer == 'I' ) {
// For Layer I slot is 32 bits long
$FrameLengthCoefficient = 48 ;
$SlotLength = 4 ;
} else { // Layer II / III
// for Layer II and Layer III slot is 8 bits long.
$FrameLengthCoefficient = 144 ;
$SlotLength = 1 ;
}
} else { // MPEG-2 / MPEG-2.5
if ( $layer == 'I' ) {
// For Layer I slot is 32 bits long
$FrameLengthCoefficient = 24 ;
$SlotLength = 4 ;
} elseif ( $layer == 'II' ) {
// for Layer II and Layer III slot is 8 bits long.
$FrameLengthCoefficient = 144 ;
$SlotLength = 1 ;
} else { // III
// for Layer II and Layer III slot is 8 bits long.
$FrameLengthCoefficient = 72 ;
$SlotLength = 1 ;
}
}
// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
// http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068
// -> [Finding the next frame synch] on www.r3mix.net forums if the above link goes dead
if ( $samplerate > 0 ) {
$NewFramelength = ( $FrameLengthCoefficient * $bitrate * 1000 ) / $samplerate ;
$NewFramelength = floor ( $NewFramelength / $SlotLength ) * $SlotLength ; // round to next-lower multiple of SlotLength (1 byte for Layer II/III, 4 bytes for Layer I)
if ( $padding ) {
$NewFramelength += $SlotLength ;
}
$AudioFrameLengthCache [ $bitrate ][ $version ][ $layer ][ $padding ][ $samplerate ] = ( int ) $NewFramelength ;
}
}
}
return $AudioFrameLengthCache [ $bitrate ][ $version ][ $layer ][ $padding ][ $samplerate ];
}
function LAMEvbrMethodLookup ( $VBRmethodID )
{
static $LAMEvbrMethodLookup = [];
if ( empty ( $LAMEvbrMethodLookup )) {
$LAMEvbrMethodLookup [ 0x00 ] = 'unknown' ;
$LAMEvbrMethodLookup [ 0x01 ] = 'cbr' ;
$LAMEvbrMethodLookup [ 0x02 ] = 'abr' ;
$LAMEvbrMethodLookup [ 0x03 ] = 'vbr-old / vbr-rh' ;
$LAMEvbrMethodLookup [ 0x04 ] = 'vbr-mtrh' ;
$LAMEvbrMethodLookup [ 0x05 ] = 'vbr-new / vbr-mt' ;
}
return isset ( $LAMEvbrMethodLookup [ $VBRmethodID ]) ? $LAMEvbrMethodLookup [ $VBRmethodID ] : '' ;
}
function LAMEmiscStereoModeLookup ( $StereoModeID )
{
static $LAMEmiscStereoModeLookup = [];
if ( empty ( $LAMEmiscStereoModeLookup )) {
$LAMEmiscStereoModeLookup [ 0 ] = 'mono' ;
$LAMEmiscStereoModeLookup [ 1 ] = 'stereo' ;
$LAMEmiscStereoModeLookup [ 2 ] = 'dual' ;
$LAMEmiscStereoModeLookup [ 3 ] = 'joint' ;
$LAMEmiscStereoModeLookup [ 4 ] = 'forced' ;
$LAMEmiscStereoModeLookup [ 5 ] = 'auto' ;
$LAMEmiscStereoModeLookup [ 6 ] = 'intensity' ;
$LAMEmiscStereoModeLookup [ 7 ] = 'other' ;
}
return isset ( $LAMEmiscStereoModeLookup [ $StereoModeID ]) ? $LAMEmiscStereoModeLookup [ $StereoModeID ] : '' ;
}
function LAMEmiscSourceSampleFrequencyLookup ( $SourceSampleFrequencyID )
{
static $LAMEmiscSourceSampleFrequencyLookup = [];
if ( empty ( $LAMEmiscSourceSampleFrequencyLookup )) {
$LAMEmiscSourceSampleFrequencyLookup [ 0 ] = '<= 32 kHz' ;
$LAMEmiscSourceSampleFrequencyLookup [ 1 ] = '44.1 kHz' ;
$LAMEmiscSourceSampleFrequencyLookup [ 2 ] = '48 kHz' ;
$LAMEmiscSourceSampleFrequencyLookup [ 3 ] = '> 48kHz' ;
}
return isset ( $LAMEmiscSourceSampleFrequencyLookup [ $SourceSampleFrequencyID ]) ? $LAMEmiscSourceSampleFrequencyLookup [ $SourceSampleFrequencyID ] : '' ;
}
function LAMEsurroundInfoLookup ( $SurroundInfoID )
{
static $LAMEsurroundInfoLookup = [];
if ( empty ( $LAMEsurroundInfoLookup )) {
$LAMEsurroundInfoLookup [ 0 ] = 'no surround info' ;
$LAMEsurroundInfoLookup [ 1 ] = 'DPL encoding' ;
$LAMEsurroundInfoLookup [ 2 ] = 'DPL2 encoding' ;
$LAMEsurroundInfoLookup [ 3 ] = 'Ambisonic encoding' ;
}
return isset ( $LAMEsurroundInfoLookup [ $SurroundInfoID ]) ? $LAMEsurroundInfoLookup [ $SurroundInfoID ] : 'reserved' ;
2015-09-04 03:59:46 +02:00
}
for ( $i = 0x00 ; $i <= 0xFF ; $i ++ ) {
2021-02-14 03:39:15 +01:00
$head4 = " \xFF \xFE " . chr ( $i ) . " \x00 " ;
$isvalid = MPEGaudioHeaderBytesValid ( $head4 );
echo '<div style="color: ' . ( $isvalid ? 'green' : 'red' ) . ';">' . str_pad ( strtoupper ( dechex ( $i )), 2 , '0' , STR_PAD_LEFT ) . ' = ' . htmlentities ( chr ( $i )) . ' = ' . ( $isvalid ? 'valid' : 'INVALID' ) . '</div>' ;
2015-09-04 03:59:46 +02:00
}