if (isset($_POST['Analyze']) && $_POST['HeaderHexBytes']) { $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']);
function decodeMPEGaudioHeader($fd, $offset, &$MP3fileInfo, $recursivesearch = TRUE) { if ($offset >= $MP3fileInfo['filesize']) { $MP3fileInfo['error'] .= "\n" . 'end of file encounter looking for MPEG synch'; return FALSE; } fseek($fd, $offset, SEEK_SET); $headerstring = fread($fd, FREAD_BUFFER_SIZE); // 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 // AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM // 01234567 01234567 01234567 01234567 // 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 $byte1 = BigEndian2Bin(substr($headerstring, 0, 1)); $byte2 = BigEndian2Bin(substr($headerstring, 1, 1)); $byte3 = BigEndian2Bin(substr($headerstring, 2, 1)); $byte4 = BigEndian2Bin(substr($headerstring, 3, 1)); if (substr(BigEndian2Bin(substr($headerstring, 0, 2)), 0, 11) == '11111111111') { // synch detected (11 set bits in a row) } else { $MP3fileInfo['error'] .= "\n" . 'MPEG-audio synch not found where expected (at offset ' . $offset . ')'; return FALSE; } $MP3fileInfo['mpeg']['audio']['raw']['version'] = bindec(substr($byte2, 3, 2)); $MP3fileInfo['mpeg']['audio']['raw']['layer'] = bindec(substr($byte2, 5, 2)); $MP3fileInfo['mpeg']['audio']['raw']['protection'] = bindec(substr($byte2, 7, 1)); $MP3fileInfo['mpeg']['audio']['raw']['bitrate'] = bindec(substr($byte3, 0, 4)); $MP3fileInfo['mpeg']['audio']['raw']['frequency'] = bindec(substr($byte3, 4, 2)); $MP3fileInfo['mpeg']['audio']['raw']['padding'] = bindec(substr($byte3, 6, 1)); $MP3fileInfo['mpeg']['audio']['raw']['private'] = bindec(substr($byte3, 7, 1)); $MP3fileInfo['mpeg']['audio']['raw']['channelmode'] = bindec(substr($byte4, 0, 2)); $MP3fileInfo['mpeg']['audio']['raw']['modeextension'] = bindec(substr($byte4, 2, 2)); $MP3fileInfo['mpeg']['audio']['raw']['copyright'] = bindec(substr($byte4, 4, 1)); $MP3fileInfo['mpeg']['audio']['raw']['original'] = bindec(substr($byte4, 5, 1)); $MP3fileInfo['mpeg']['audio']['raw']['emphasis'] = bindec(substr($byte4, 6, 2)); if (!MPEGaudioHeaderValid($MP3fileInfo['mpeg']['audio']['raw'])) { $MP3fileInfo['error'] .= "\n" . 'invalid MPEG audio header at offset ' . $offset; return FALSE; } $MP3fileInfo['mpeg']['audio']['version'] = MPEGaudioVersionLookup($MP3fileInfo['mpeg']['audio']['raw']['version']); $MP3fileInfo['mpeg']['audio']['layer'] = MPEGaudioLayerLookup($MP3fileInfo['mpeg']['audio']['raw']['layer']); $MP3fileInfo['mpeg']['audio']['protection'] = MPEGaudioCRCLookup($MP3fileInfo['mpeg']['audio']['raw']['protection']); $MP3fileInfo['mpeg']['audio']['bitrate'] = MPEGaudioBitrateLookup($MP3fileInfo['mpeg']['audio']['version'], $MP3fileInfo['mpeg']['audio']['layer'], $MP3fileInfo['mpeg']['audio']['raw']['bitrate']); $MP3fileInfo['mpeg']['audio']['frequency'] = MPEGaudioFrequencyLookup($MP3fileInfo['mpeg']['audio']['version'], $MP3fileInfo['mpeg']['audio']['raw']['frequency']); $MP3fileInfo['mpeg']['audio']['padding'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['padding']; $MP3fileInfo['mpeg']['audio']['private'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['private']; $MP3fileInfo['mpeg']['audio']['channelmode'] = MPEGaudioChannelModeLookup($MP3fileInfo['mpeg']['audio']['raw']['channelmode']); $MP3fileInfo['mpeg']['audio']['channels'] = $MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono' ? 1 : 2; $MP3fileInfo['mpeg']['audio']['modeextension'] = MPEGaudioModeExtensionLookup($MP3fileInfo['mpeg']['audio']['layer'], $MP3fileInfo['mpeg']['audio']['raw']['modeextension']); $MP3fileInfo['mpeg']['audio']['copyright'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['copyright']; $MP3fileInfo['mpeg']['audio']['original'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['original']; $MP3fileInfo['mpeg']['audio']['emphasis'] = MPEGaudioEmphasisLookup($MP3fileInfo['mpeg']['audio']['raw']['emphasis']); if ($MP3fileInfo['mpeg']['audio']['protection']) { $MP3fileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2)); } if ($MP3fileInfo['mpeg']['audio']['bitrate'] != 'free') { if ($MP3fileInfo['mpeg']['audio']['version'] == '1') { if ($MP3fileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 48; $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 4 : 0; // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long." } else { // Layer II / III $FrameLengthCoefficient = 144; $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 1 : 0; // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long." } } else { // MPEG-2 / MPEG-2.5 if ($MP3fileInfo['mpeg']['audio']['layer'] == 'I') { $FrameLengthCoefficient = 24; $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 4 : 0; // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long." } else { // Layer II / III $FrameLengthCoefficient = 72; $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 1 : 0; // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long." } } // 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 $MP3fileInfo['mpeg']['audio']['framelength'] = (int) floor($FrameLengthCoefficient * 1000 * $MP3fileInfo['mpeg']['audio']['bitrate'] / $MP3fileInfo['mpeg']['audio']['frequency']) + $FrameLengthPadding; } $MP3fileInfo['bitrate'] = 1000 * $MP3fileInfo['mpeg']['audio']['bitrate']; $nextframetestarray = array('error' => '', 'filesize' => $MP3fileInfo['filesize']); if (isset($MP3fileInfo['mpeg']['audio']['framelength'])) { $nextframetestoffset = $offset + $MP3fileInfo['mpeg']['audio']['framelength']; } else { $nextframetestoffset = $MP3fileInfo['filesize']; } if ($recursivesearch && isset($MP3fileInfo['mpeg']['audio']['framelength']) && $MP3fileInfo['mpeg']['audio']['framelength']) { for ($i = 0; $i < 5; $i++) { // check next 5 frames for validity, to make sure we haven't run across a false synch if ($nextframetestoffset >= $MP3fileInfo['filesize']) { // end of file break; } if (decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, FALSE)) { // next frame is OK, get ready to check the one after that $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; } else { // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence $MP3fileInfo['error'] .= "\n" . 'Frame at offset(' . $offset . ') is valid, but the next one at (' . $nextframetestoffset . ') is not.'; return FALSE; } } } // For Layer II there are some combinations of bitrate and mode which are not allowed. if ($MP3fileInfo['mpeg']['audio']['layer'] == 'II') { switch ($MP3fileInfo['mpeg']['audio']['channelmode']) { case 'mono': if ($MP3fileInfo['mpeg']['audio']['bitrate'] == 'free' || $MP3fileInfo['mpeg']['audio']['bitrate'] <= 192) { // these are ok } else { $MP3fileInfo['error'] .= "\n" . $MP3fileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $MP3fileInfo['mpeg']['audio']['channelmode'] . '.'; } break; case 'stereo': case 'joint stereo': case 'dual channel': if ($MP3fileInfo['mpeg']['audio']['bitrate'] == 'free' || $MP3fileInfo['mpeg']['audio']['bitrate'] == 64 || $MP3fileInfo['mpeg']['audio']['bitrate'] >= 96) { // these are ok } else { $MP3fileInfo['error'] .= "\n" . $MP3fileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $MP3fileInfo['mpeg']['audio']['channelmode'] . '.'; } break; } } //////////////////////////////////////////////////////////////////////////////////// // Variable-bitrate headers if ($MP3fileInfo['mpeg']['audio']['version'] == '1') { if ($MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono') { $VBRidOffset = 17 + 4; // 21 bytes } else { $VBRidOffset = 32 + 4; // 36 bytes } } else { // 2 or 2.5 if ($MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono') { $VBRidOffset = 9 + 4; // 13 bytes } else { $VBRidOffset = 17 + 4; // 21 bytes } } $VBRid = substr($headerstring, $VBRidOffset, 4); if ($VBRid == 'Xing') { $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'VBR'; $MP3fileInfo['mpeg']['audio']['VBR_method'] = 'Xing'; } else { if ($VBRid == 'VBRI') { $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'VBR'; $MP3fileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer'; } else { $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'CBR'; } } if ($MP3fileInfo['mpeg']['audio']['bitratemode'] == 'VBR') { if ($MP3fileInfo['mpeg']['audio']['VBR_method'] == 'Xing') { $XingVBROffset = $VBRidOffset + 4; $XingHeader_Flags = substr($headerstring, $XingVBROffset, 4); $XingVBROffset += 4; $XingHeader_byte4 = BigEndian2Bin(substr($XingHeader_Flags, 3, 1)); $XingHeader_flags['frames'] = substr($XingHeader_byte4, 4, 1); $XingHeader_flags['bytes'] = substr($XingHeader_byte4, 5, 1); $XingHeader_flags['toc'] = substr($XingHeader_byte4, 6, 1); $XingHeader_flags['vbr_scale'] = substr($XingHeader_byte4, 7, 1); if ($XingHeader_flags['frames'] == '1') { $XingHeader_Frames = substr($headerstring, $XingVBROffset, 4); $XingVBROffset += 4; $MP3fileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($XingHeader_Frames); } if ($XingHeader_flags['bytes'] == '1') { $XingHeader_Bytes = substr($headerstring, $XingVBROffset, 4); $XingVBROffset += 4; $MP3fileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($XingHeader_Bytes); } } else { if ($MP3fileInfo['mpeg']['audio']['VBR_method'] == 'Fraunhofer') { // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html $FraunhoferVBROffset = $VBRidOffset + 4; $Fraunhofer_version = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $Fraunhofer_quality = substr($headerstring, $FraunhoferVBROffset, 2); $FraunhoferVBROffset += 2; $MP3fileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int($Fraunhofer_quality); $Fraunhofer_Bytes = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $MP3fileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($Fraunhofer_Bytes); $Fraunhofer_Frames = substr($headerstring, $FraunhoferVBROffset, 4); $FraunhoferVBROffset += 4; $MP3fileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($Fraunhofer_Frames); } } $MP3fileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame if ($MP3fileInfo['mpeg']['audio']['version'] == '1' && $MP3fileInfo['mpeg']['audio']['layer'] == 'I') { $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 384) / 1000; } else { if (($MP3fileInfo['mpeg']['audio']['version'] == '2' || $MP3fileInfo['mpeg']['audio']['version'] == '2.5') && $MP3fileInfo['mpeg']['audio']['layer'] == 'III') { $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 576) / 1000; } else { $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 1152) / 1000; } } if ($MP3fileInfo['mpeg']['audio']['VBR_bitrate'] > 0) { $MP3fileInfo['bitrate'] = 1000 * $MP3fileInfo['mpeg']['audio']['VBR_bitrate']; unset($MP3fileInfo['mpeg']['audio']['bitrate']); // to avoid confusion } } return TRUE; }