Hi,
I'm not sure this is entirely on-topic, but I thought this might be helpful
for
me and others.
I've written a small function for PHP (server-side web programming language)
for extracting the bitrate etc. information from MP3 files. I use it for my
website
AudioPhilez (www.audiophilez.com). I have indexed over 60,000 files and used
this code for each and every one. It seems very accurate to me, but I was
hoping
you guys on the list might be interested in having a look at it. If you spot
any
errors or things which might cause a problem, it would be great to get some
feedback on it. I am just beginning to worry that if any of this code is
incorrect
it would be nice to spot the problem before it causes a problem on my
website.
I've posted it below. And by the way, it's totally free for non-commercial
use. Please
let me know if you use it.
If you don't know PHP, it should still be very straightforwared (PHP has
very similar
coding style to C, but without strict types etc.)
MPEGAudioHeaderDecode takes two parameters, a binary header of
arbitrary length (I usually give it 4,000 bytes incase sync is not right at
the
beginning), and the filesize (so it can calculate play length). It checks
for sync at two headers in a row.
/*
* MPEG AUDIO FRAME HEADER INFORMATION PLUG-IN
* VERSION 3.0.5-PHP
* Last Modified: February 2, 2000.
*
* Copyright (C) 1999-2000 Netherworld Media.
*/
Function MPEGAudioHeaderDecode($HEADER,$FS)
{
$LAYER1_1=Array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448);
$LAYER2_1=Array(0,32,48,56,64,80,96,112,128,160,192,224,256,320,384);
$LAYER3_1=Array(0,32,40,48,56,64,80,96,112,128,160,192,224,256,320);
$LAYER1_2=Array(0,32,48,56,64,80,96,112,128,144,160,176,192,224,256);
$LAYER2_2=Array(0,8,16,24,32,40,48,56,64,80,96,112,128,144,160);
$SAM_LIST=Array(44100,48000,32000);
$EMPHASIS_TYPE=Array("None","50/15ms","Reserved","CCITT j.17");
$VALID_MPEG=0;
for ($I=0;$I<strlen($HEADER)-1;$I++)
{
if ((ord($HEADER[$I])==0xFF) && ((ord($HEADER[$I+1]) & 0xE0)==0xE0))
{
switch((ord($HEADER[$I+1]) & 0x18) >> 0x03)
{
case 0x03: $RESULT["MPEG_VERSION"]="1"; break;
case 0x02: $RESULT["MPEG_VERSION"]="2"; break;
case 0x00: $RESULT["MPEG_VERSION"]="2.5"; break;
default: $RESULT["MPEG_VERSION"]=""; break;
}
switch((ord($HEADER[$I+1]) & 0x06) >> 0x01)
{
case 0x01: $RESULT["MPEG_LAYER"]="3"; break;
case 0x02: $RESULT["MPEG_LAYER"]="2"; break;
case 0x03: $RESULT["MPEG_LAYER"]="1"; break;
default: $RESULT["MPEG_LAYER"]=""; break;
}
$BITRATE_INDEX=(ord($HEADER[$I+2]) & 0xF0) >> 0x04;
if (($BITRATE_INDEX > 14) || ($BITRATE_INDEX==0))
$RESULT["BITRATE"]="";
else
{
switch($RESULT["MPEG_VERSION"])
{
case "1":
switch($RESULT["MPEG_LAYER"])
{
case "3": $RESULT["BITRATE"]=$LAYER3_1[$BITRATE_INDEX];
break;
case "2": $RESULT["BITRATE"]=$LAYER2_1[$BITRATE_INDEX];
break;
case "1": $RESULT["BITRATE"]=$LAYER1_1[$BITRATE_INDEX];
break;
default: $RESULT["BITRATE"]=""; break;
}
break;
case "2":
case "2.5":
switch($RESULT["MPEG_LAYER"])
{
case "3":
case "2": $RESULT["BITRATE"]=$LAYER2_2[$BITRATE_INDEX];
break;
case "1": $RESULT["BITRATE"]=$LAYER1_2[$BITRATE_INDEX];
break;
default: $RESULT["BITRATE"]=""; break;
}
break;
default: $RESULT["BITRATE"]=""; break;
}
}
$SAMPLE_INDEX=(ord($HEADER[$I+2]) & 0x0C) >> 0x02;
if ($SAMPLE_INDEX > 2)
$RESULT["SAMPLING_RATE"]="";
else
{
switch($RESULT["MPEG_VERSION"])
{
case "1": $RESULT["SAMPLING_RATE"]=$SAM_LIST[$SAMPLE_INDEX];
break;
case "2": $RESULT["SAMPLING_RATE"]=$SAM_LIST[$SAMPLE_INDEX] / 2;
break;
case "2.5": $RESULT["SAMPLING_RATE"]=$SAM_LIST[$SAMPLE_INDEX] /
4; break;
default: $RESULT["SAMPLING_RATE"]="";
}
}
switch((ord($HEADER[$I+2]) & 0x02) >> 0x01)
{
case 0x01: $RESULT["PADDING"]="TRUE"; break;
case 0x00: $RESULT["PADDING"]="FALSE"; break;
}
if (($RESULT["BITRATE"]=="") || ($RESULT["SAMPLING_RATE"]==""))
{
$I++; continue;
}
switch($RESULT["MPEG_LAYER"])
{
case "3":
case "2":
$RESULT["FRAME_LENGTH"]=(int)
(($RESULT["BITRATE"]*144000)/$RESULT["SAMPLING_RATE"]);
if ($RESULT["PADDING"]=="TRUE")
$RESULT["FRAME_LENGTH"]+=1;
break;
case "1":
$RESULT["FRAME_LENGTH"]=(int)
(($RESULT["BITRATE"]*12000)/$RESULT["SAMPLING_RATE"]);
if ($RESULT["PADDING"]=="TRUE")
$RESULT["FRAME_LENGTH"]+=4;
break;
default: $RESULT["FRAME_LENGTH"]=""; break;
}
if (($I+$RESULT["FRAME_LENGTH"]) > strlen($HEADER))
{
unset($RESULT);
$RESULT["ERROR"]=101;
$RESULT["ERROR_STRING"]="Bad frame length (exceeds limit).";
return $RESULT;
}
if ((ord($HEADER[$I+$RESULT["FRAME_LENGTH"]])!=0xFF) ||
((ord($HEADER[$I+$RESULT["FRAME_LENGTH"]+1]) & 0xE0)!=0xE0))
{
$I++; continue;
}
$VALID_MPEG=1;
break;
}
}
if ($VALID_MPEG==0)
{
unset($RESULT);
$RESULT["ERROR"]=100;
$RESULT["ERROR_STRING"]="Not a valid MPEG audio file.";
return $RESULT;
}
$FS-=$I;
$VBR=0;
$XING_HEADER=substr($HEADER,$I,48);
$HEADER=substr($HEADER,$I,4);
if (substr($XING_HEADER,36,4)=="Xing")
{
$XING_HEADER=substr($XING_HEADER,36);
$VBR=1;
}
elseif (substr($XING_HEADER,21,4)=="Xing")
{
$XING_HEADER=substr($XING_HEADER,21);
$VBR=1;
}
elseif (substr($XING_HEADER,13,4)=="Xing")
{
$XING_HEADER=substr($XING_HEADER,13);
$VBR=1;
}
if ($VBR==1)
{
$RESULT["BITRATE"]="Variable";
$RESULT["FRAME_LENGTH"]="";
$RESULT["FRAME_COUNT"]=(ord($XING_HEADER[8]) <<
24)+(ord($XING_HEADER[9]) << 16)+(ord($XING_HEADER[10]) <<
8)+(ord($XING_HEADER[11]));
$PLAYTIME_SEC=(1152 / $RESULT["SAMPLING_RATE"]) *
$RESULT["FRAME_COUNT"];
$RESULT["AVG_BITRATE"]=round((($FS / $PLAYTIME_SEC) * 8)/1000);
}
else
{
if ($RESULT["FRAME_LENGTH"]=="")
$RESULT["FRAME_COUNT"]="";
else
$RESULT["FRAME_COUNT"]=(int) ($FS/$RESULT["FRAME_LENGTH"]);
$PLAYTIME_SEC=$FS/($RESULT["BITRATE"] * 125);
}
/* CHANNEL MODE */
switch((ord($HEADER[3]) & 0xC0) >> 0x06)
{
case 0x00: $RESULT["CHANNEL_MODE"]="Stereo"; break;
case 0x01: $RESULT["CHANNEL_MODE"]="Joint Stereo"; break;
case 0x02: $RESULT["CHANNEL_MODE"]="Dual Stereo"; break;
case 0x03: $RESULT["CHANNEL_MODE"]="Mono"; break;
default: $RESULT["CHANNEL_MODE"]=""; break;
}
/* PROTECTION BIT */
switch(ord($HEADER[1]) & 0x01)
{
case 0x01: $RESULT["PROTECTION_BIT"]="FALSE"; break;
case 0x00: $RESULT["PROTECTION_BIT"]="TRUE"; break;
}
/* PRIVATE BIT */
switch(ord($HEADER[2]) & 0x01)
{
case 0x01: $RESULT["PRIVATE_BIT"]="TRUE"; break;
case 0x00: $RESULT["PRIVATE_BIT"]="FALSE"; break;
}
/* COPYRIGHT BIT */
switch((ord($HEADER[3]) & 0x08) >> 0x03)
{
case 0x01: $RESULT["COPYRIGHT_BIT"]="TRUE"; break;
case 0x00: $RESULT["COPYRIGHT_BIT"]="FALSE"; break;
}
/* ORIGINAL BIT */
switch((ord($HEADER[3]) & 0x04) >> 0x02)
{
case 0x01: $RESULT["ORIGINAL_BIT"]="TRUE"; break;
case 0x00: $RESULT["ORIGINAL_BIT"]="FALSE"; break;
}
/* EMPHASIS */
$EMPHASIS_INDEX=ord($HEADER[3]) & 0x03;
if ($EMPHASIS_INDEX > 3)
$RESULT["EMPHASIS"]="";
else
$RESULT["EMPHASIS"]=$EMPHASIS_TYPE[$EMPHASIS_INDEX];
/* PLAYTIME */
if ($PLAYTIME_SEC >= 3600)
$RESULT["PLAYTIME"]=(int) ($PLAYTIME_SEC / 3600) . "h ";
if ($PLAYTIME_SEC >= 60)
$RESULT["PLAYTIME"].=(int) (($PLAYTIME_SEC % 3600) / 60) . "m ";
else
$RESULT["PLAYTIME"].="0m ";
$RESULT["PLAYTIME"].=(int) ($PLAYTIME_SEC % 60) . "s";
$MODE_EXT_INDEX=(ord($HEADER[3]) & 0x30) >> 4;
if (($MODE_EXT_INDEX > 3) || ($RESULT["MPEG_LAYER"]=="") ||
($RESULT["CHANNEL_MODE"]!="Joint Stereo"))
$RESULT["MODE_EXTENSION"]="";
else
{
switch($MODE_EXT_INDEX)
{
case 0x00:
if ($RESULT["MPEG_LAYER"]=="3")
$RESULT["MODE_EXTENSION"]="None";
elseif (($RESULT["MPEG_LAYER"]=="1") ||
($RESULT["MPEG_LAYER"]=="2"))
$RESULT["MODE_EXTENSION"]="Bands 4-31";
else
$RESULT["MODE_EXTENSION"]="";
break;
case 0x01:
if ($RESULT["MPEG_LAYER"]=="3")
$RESULT["MODE_EXTENSION"]="Intensity Stereo";
elseif (($RESULT["MPEG_LAYER"]=="1") ||
($RESULT["MPEG_LAYER"]=="2"))
$RESULT["MODE_EXTENSION"]="Bands 8-31";
else
$RESULT["MODE_EXTENSION"]="";
break;
case 0x02:
if ($RESULT["MPEG_LAYER"]=="3")
$RESULT["MODE_EXTENSION"]="Mid/Side Stereo";
elseif (($RESULT["MPEG_LAYER"]=="1") ||
($RESULT["MPEG_LAYER"]=="2"))
$RESULT["MODE_EXTENSION"]="Bands 12-31";
else
$RESULT["MODE_EXTENSION"]="";
break;
case 0x03:
if ($RESULT["MPEG_LAYER"]=="3")
$RESULT["MODE_EXTENSION"]="IS & M/S Stereo";
elseif (($RESULT["MPEG_LAYER"]=="1") ||
($RESULT["MPEG_LAYER"]=="2"))
$RESULT["MODE_EXTENSION"]="Bands 16-31";
else
$RESULT["MODE_EXTENSION"]="";
break;
}
}
$RESULT["ERROR"]=0;
$RESULT["ERROR_STRING"]="";
return $RESULT;
}
?>
Regards,
Adam Whitehead
CEO - Netherworld Media
E-mail: [EMAIL PROTECTED]
Phone: +61 (889) 279 898
FAX: +61 (889) 273 889
AudioPhilez (http://www.audiophilez.com)
--
MP3 ENCODER mailing list ( http://geek.rcc.se/mp3encoder/ )