/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at https://github.com/JamesHeinrich/getID3 //
// or https://www.getid3.org //
// or http://getid3.sourceforge.net //
// getid3.lib.php - part of getID3() //
// see readme.txt for more details //
/////////////////////////////////////////////////////////////////
* @param string|bool $htmlencoding
public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
for ($i = 0; $i < strlen($string); $i++) {
$returnstring .= str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT);
$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string[$i]) ? $string[$i] : 'ยค');
if (!empty($htmlencoding)) {
if ($htmlencoding === true) {
$htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
$returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
* Truncates a floating-point number at the decimal point.
* @param float $floatnumber
* @return float|int returns int (if possible, otherwise float)
public static function trunc($floatnumber) {
$truncatednumber = floor($floatnumber);
} elseif ($floatnumber <= -1) {
$truncatednumber = ceil($floatnumber);
if (self::intValueSupported($truncatednumber)) {
$truncatednumber = (int) $truncatednumber;
* @param int|null $variable
public static function safe_inc(&$variable, $increment=1) {
* @param int|float $floatnum
public static function CastAsInt($floatnum) {
// convert to float if not already
$floatnum = (float) $floatnum;
// convert a float to type int, only if possible
if (self::trunc($floatnum) == $floatnum) {
// it's not floating point
if (self::intValueSupported($floatnum)) {
$floatnum = (int) $floatnum;
public static function intValueSupported($num) {
// check if integers are 64-bit
if ($hasINT64 === null) { // 10x faster than is_null()
$hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
if (!$hasINT64 && !defined('PHP_INT_MIN')) {
define('PHP_INT_MIN', ~PHP_INT_MAX);
// if integers are 64-bit - no other check required
if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) { // phpcs:ignore PHPCompatibility.Constants.NewConstants.php_int_minFound
* @param string $fraction
public static function DecimalizeFraction($fraction) {
list($numerator, $denominator) = explode('/', $fraction);
return $numerator / ($denominator ? $denominator : 1);
* @param string $binarynumerator
public static function DecimalBinary2Float($binarynumerator) {
$numerator = self::Bin2Dec($binarynumerator);
$denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
return ($numerator / $denominator);
* @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
* @param string $binarypointnumber
public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
if (strpos($binarypointnumber, '.') === false) {
$binarypointnumber = '0.'.$binarypointnumber;
} elseif ($binarypointnumber[0] == '.') {
$binarypointnumber = '0'.$binarypointnumber;
while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
if (substr($binarypointnumber, 1, 1) == '.') {
$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
$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 array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
* @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
* @param float $floatvalue
public static function Float2BinaryDecimal($floatvalue) {
$maxbits = 128; // to how many bits of precision should the calculations be taken?
$intpart = self::trunc($floatvalue);
$floatpart = abs($floatvalue - $intpart);
while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
$pointbitstring .= (string) self::trunc($floatpart);
$floatpart -= self::trunc($floatpart);
$binarypointnumber = decbin($intpart).'.'.$pointbitstring;
return $binarypointnumber;
* @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
* @param float $floatvalue
public static function Float2String($floatvalue, $bits) {
$normalizedbinary = self::NormalizeBinaryPoint(self::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 self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
* @param string $byteword
public static function LittleEndian2Float($byteword) {
return self::BigEndian2Float(strrev($byteword));
* ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
* @link http://www.psc.edu/general/software/packages/ieee/ieee.html
* @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
* @param string $byteword
public static function BigEndian2Float($byteword) {
$bitword = self::BigEndian2Bin($byteword);
switch (strlen($byteword) * 8) {
// 80-bit Apple SANE format
// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
$exponentstring = substr($bitword, 1, 15);
$isnormalized = intval($bitword[16]);
$fractionstring = substr($bitword, 17, 63);
$exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
$fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
$floatvalue = $exponent * $fraction;
$exponentstring = substr($bitword, 1, $exponentbits);
$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
$exponent = self::Bin2Dec($exponentstring);
$fraction = self::Bin2Dec($fractionstring);
if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
$floatvalue = '-infinity';
$floatvalue = '+infinity';
} elseif (($exponent == 0) && ($fraction == 0)) {
$floatvalue = ($signbit ? 0 : -0);
} elseif (($exponent == 0) && ($fraction != 0)) {
// These are 'unnormalized' values
$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
} elseif ($exponent != 0) {
$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
return (float) $floatvalue;
* @param string $byteword
* @return int|float|false
public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
$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); // faster, but runs into problems past 2^31 on 32-bit systems
$intvalue += (ord($byteword[$i]) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
$intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i));
if ($signed && !$synchsafe) {
// synchsafe ints are not allowed to be signed
if ($bytewordlen <= PHP_INT_SIZE) {
$signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
if ($intvalue & $signMaskBit) {
$intvalue = 0 - ($intvalue & ($signMaskBit - 1));
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
return self::CastAsInt($intvalue);
* @param string $byteword
* @return int|float|false
public static function LittleEndian2Int($byteword, $signed=false) {
return self::BigEndian2Int(strrev($byteword), false, $signed);
* @param string $byteword
public static function LittleEndian2Bin($byteword) {
return self::BigEndian2Bin(strrev($byteword));
* @param string $byteword
public static function BigEndian2Bin($byteword) {
$bytewordlen = strlen($byteword);
for ($i = 0; $i < $bytewordlen; $i++) {
$binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT);
public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
if ($minbytes > PHP_INT_SIZE) {
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
$number = $number & (0x80 << (8 * ($minbytes - 1)));
$quotient = ($number / ($maskbyte + 1));
$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
$number = floor($quotient);
return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
public static function Dec2Bin($number) {
$bytes[] = (($number / 256) - (floor($number / 256))) * 256;
$number = floor($number / 256);
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;
* @param string $binstring
public static function Bin2Dec($binstring, $signed=false) {
if ($binstring[0] == '1') {
$binstring = substr($binstring, 1);
for ($i = 0; $i < strlen($binstring); $i++) {
$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
return self::CastAsInt($decvalue * $signmult);
* @param string $binstring
public static function Bin2String($binstring) {
// return 'hi' for input of '0110100001101001'
$binstringreversed = strrev($binstring);
for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
$string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
$intstring = $intstring.chr($number & 127);
$intstring = $intstring.chr($number & 255);
return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);