Skip to content

Instantly share code, notes, and snippets.

@zonble
Created April 16, 2012 17:22
Show Gist options
  • Save zonble/2400085 to your computer and use it in GitHub Desktop.
Save zonble/2400085 to your computer and use it in GitHub Desktop.
PHP MP3 Parser
<?php
/*
* A tiny parser which parses ID3 tags and MP3 frames.
*/
function parseMP3($mp3file) {
function _parseID3TagsV2($data) {
define("HEADER_LENGTH", 10);
define("HEADER_BODY_LENGTH_INFO_OFFSET", 6);
define("HEADER_BODY_LENGTH_INFO_LENGTH", 4);
define("FRAME_HEADER_LENGTH", 10);
define("FRAME_ID_LENGTH", 4);
define("FRAME_BODY_LENGTH_INFO_LENGTH", 4);
function MASK($bits) {
return (1 << ($bits)) - 1;
}
function _readUInt28($bytes) {
$BITSUSED = 7; $val = 0;
foreach(str_split($bytes) as $byte) {
$val = ($val << $BITSUSED) | (ord($byte) & MASK($BITSUSED));
}
return min($val, MASK($BITSUSED * 4));
}
$tags = array();
$ID3TagHeader = substr($data, 0, HEADER_LENGTH);
$ID3TagBodyInfo = substr($data, HEADER_BODY_LENGTH_INFO_OFFSET, HEADER_BODY_LENGTH_INFO_LENGTH);
$ID3TagBodyLength = _readUInt28($ID3TagBodyInfo);
$readHead = HEADER_LENGTH;
while ($readHead < $ID3TagBodyLength) {
$frameHeader = substr($data, $readHead, FRAME_HEADER_LENGTH);
$frameID = substr($frameHeader, 0, FRAME_ID_LENGTH);
$frameBodyLengthInfo = substr($frameHeader, FRAME_ID_LENGTH, FRAME_BODY_LENGTH_INFO_LENGTH);
$frameBodyLength = _readUInt28($frameBodyLengthInfo);
$frameBody = substr($data, $readHead + FRAME_HEADER_LENGTH, $frameBodyLength);
$textEncodingType = intval($frameBody[0]);
$frameContent = substr($frameBody, 1, strlen($frameBody) -1);
$tags[$frameID] = $frameContent;
$readHead += $FRAME_HEADER_LENGTH + $frameBodyLength;
}
return array($tags, $readHead);
}
function _parseMP3Data($data, $size, $inReadHead=0) {
// $data = str_split($data);
$readHead = $inReadHead;
$foundFirstFrame = False;
$MP3BitrateLookup = array(0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0);
$parsedFrameCount = 0;
$skippedByteCount = 0;
while(True) {
$remainingLength = $size - $readHead;
if ($remainingLength < 2) {
break;
}
$frameSync = (ord($data[$readHead]) << 8) | (ord($data[$readHead + 1]) & (0x80 | 0x40 | 0x20));
if ($frameSync != 0xffe0) {
print "skip $readHead\n";
$readHead += 1;
$skippedByteCount += 1;
continue;
}
if ($remainingLength < 6) {
break;
}
$audioVersion = (ord($data[$readHead + 1]) >> 3) & 0x03;
$layer = (ord($data[$readHead + 1]) >> 1) & 0x03;
$hasCRC = !(ord($data[$readHead + 1]) & 0x01);
$bitrateIndex = ord($data[$readHead + 2]) >> 4;
$sampleRateIndex = ord($data[$readHead + 2]) >> 2 & 0x03;
$bitrate = $MP3BitrateLookup[$bitrateIndex];
// print "audioVersion:$audioVersion, layer:$layer, sampleRateIndex:$sampleRateIndex, bitrate:$bitrate\n";
if (!($audioVersion == 0x03 and $layer == 0x01 and $sampleRateIndex == 0x00)) {
// Test
}
$hasPadding = (!(!((ord($data[$readHead + 2]) >> 1) & 0x01)));
$padding = 0; if ($hasPadding) { $padding = 1; };
$CRCPadding = 0; if ($hasCRC) { $CRCPadding = 2; };
$frameLength = intval((144 * $bitrate / 44100)) + $padding + $CRCPadding;
$readHead += $frameLength;
if ($remainingLength < $frameLength) {
break;
}
$parsedFrameCount++;
}
print "parsedFrameCount:".$parsedFrameCount."\n";
print "skippedByteCount:".$skippedByteCount."\n";
return True;
}
$handle = fopen($mp3file, "rb");
$size = filesize($mp3file);
$data = fread($handle, $size);
$readHead = 0;
$tags = NULL;
if ($data[0] == 'I' && $data[1] == 'D' && $data[2] == '3') {
$result = _parseID3TagsV2($data);
$tags = $result[0];
$readHead = $result[1];
}
return array(_parseMP3Data($data, $size, 0), $tags);
}
?>
Copy link

ghost commented Sep 5, 2014

zonble 大
line 42 should be:
$readHead += FRAME_HEADER_LENGTH + $frameBodyLength;

you have an additional '$' for FRAME_HEADER_LENGTH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment