function getAUheaderFilepointer(&$fd, &$ThisFileInfo) { fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $AUheader = fread($fd, 8); if (substr($AUheader, 0, 4) != '.snd') { $ThisFileInfo['error'] .= "\n" . 'Expecting ".snd" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . substr($AUheader, 0, 4) . '"'; return false; } $ThisFileInfo['fileformat'] = 'au'; $ThisFileInfo['audio']['dataformat'] = 'au'; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['au']['header_length'] = BigEndian2Int(substr($AUheader, 4, 4)); $AUheader .= fread($fd, $ThisFileInfo['au']['header_length'] - 8); $ThisFileInfo['avdataoffset'] += $ThisFileInfo['au']['header_length']; $ThisFileInfo['au']['data_size'] = BigEndian2Int(substr($AUheader, 8, 4)); $ThisFileInfo['au']['data_format_id'] = BigEndian2Int(substr($AUheader, 12, 4)); $ThisFileInfo['au']['sample_rate'] = BigEndian2Int(substr($AUheader, 16, 4)); $ThisFileInfo['au']['channels'] = BigEndian2Int(substr($AUheader, 20, 4)); $ThisFileInfo['au']['comment'] = trim(substr($AUheader, 24)); $ThisFileInfo['au']['data_format'] = AUdataFormatNameLookup($ThisFileInfo['au']['data_format_id']); $ThisFileInfo['au']['used_bits_per_sample'] = AUdataFormatUsedBitsPerSampleLookup($ThisFileInfo['au']['data_format_id']); if ($ThisFileInfo['au']['bits_per_sample'] = AUdataFormatBitsPerSampleLookup($ThisFileInfo['au']['data_format_id'])) { $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['au']['bits_per_sample']; } else { unset($ThisFileInfo['au']['bits_per_sample']); } $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['au']['sample_rate']; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['au']['channels']; if (!empty($ThisFileInfo['au']['comment'])) { $ThisFileInfo['comments']['comment'][] = $ThisFileInfo['au']['comment']; } if ($ThisFileInfo['avdataoffset'] + $ThisFileInfo['au']['data_size'] > $ThisFileInfo['avdataend']) { $ThisFileInfo['warning'] .= "\n" . 'Possible truncated file - expecting "' . $ThisFileInfo['au']['data_size'] . '" bytes of audio data, only found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) . ' bytes"'; } $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['au']['data_size'] / ($ThisFileInfo['au']['sample_rate'] * $ThisFileInfo['au']['channels'] * ($ThisFileInfo['au']['used_bits_per_sample'] / 8)); $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['au']['data_size'] * 8 / $ThisFileInfo['playtime_seconds']; return true; }
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; }
$frame_size = BigEndian2Int(substr($frame_header, 3, 3), 0); $frame_flags = ''; // not used for anything, just to avoid E_NOTICEs } else { if ($id3info['id3']['id3v2']['majorversion'] > 2) { $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header $framedata = substr($framedata, 10); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 4); if ($id3info['id3']['id3v2']['majorversion'] == 3) { $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } else { // ID3v2.4+ $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $id3info['id3']['id3v2']['majorversion'])) { $id3info['id3']['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $id3info); if (isset($id3info['id3']['id3v2']['APIC'][0]['data']) && strlen($id3info['id3']['id3v2']['APIC'][0]['data']) > 0) { if (isset($rawdata)) { echo $id3info['id3']['id3v2']['APIC'][0]['data']; } else { include_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php'; $imagechunkcheck = GetDataImageSize($id3info['id3']['id3v2']['APIC'][0]['data']);
function getMIDIHeaderFilepointer(&$fd, &$MP3fileInfo, $scanwholefile = TRUE) { if (!$fd) { $MP3fileInfo['error'] .= "\n" . 'Could not open file'; return FALSE; } else { rewind($fd); $MIDIdata = fread($fd, FREAD_BUFFER_SIZE); $offset = 0; $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' $offset += 4; $MP3fileInfo['midi']['raw']['headersize'] = BigEndian2Int(substr($MIDIdata, $offset, 4)); $offset += 4; $MP3fileInfo['midi']['raw']['fileformat'] = BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; $MP3fileInfo['midi']['raw']['tracks'] = BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; $MP3fileInfo['midi']['raw']['ticksperqnote'] = BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; for ($i = 0; $i < $MP3fileInfo['midi']['raw']['tracks']; $i++) { if (strlen($MIDIdata) - $offset < 8) { $MIDIdata .= fread($fd, FREAD_BUFFER_SIZE); } $trackID = substr($MIDIdata, $offset, 4); $offset += 4; if ($trackID == 'MTrk') { $tracksize = BigEndian2Int(substr($MIDIdata, $offset, 4)); $offset += 4; // $MP3fileInfo['midi']['tracks']["$i"]['size'] = $tracksize; $trackdataarray["{$i}"] = substr($MIDIdata, $offset, $tracksize); $offset += $tracksize; } else { $MP3fileInfo['error'] .= "\n" . 'Expecting "MTrk" at ' . $offset . ', found ' . $trackID . ' instead'; return FALSE; } } if (!isset($trackdataarray) || !is_array($trackdataarray)) { $MP3fileInfo['error'] .= "\n" . 'Cannot find MIDI track information'; unset($MP3fileInfo['midi']); unset($MP3fileInfo['fileformat']); return FALSE; } if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important $MP3fileInfo['midi']['totalticks'] = 0; $MP3fileInfo['playtime_seconds'] = 0; $CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat $CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat foreach ($trackdataarray as $tracknumber => $trackdata) { $eventsoffset = 0; $LastIssuedMIDIcommand = 0; $LastIssuedMIDIchannel = 0; $CumulativeDeltaTime = 0; $TicksAtCurrentBPM = 0; while ($eventsoffset < strlen($trackdata)) { $eventid = 0; if (isset($MIDIevents["{$tracknumber}"]) && is_array($MIDIevents["{$tracknumber}"])) { $eventid = count($MIDIevents["{$tracknumber}"]); } $deltatime = 0; for ($i = 0; $i < 4; $i++) { $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1)); $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7f); if ($deltatimebyte & 0x80) { // another byte follows } else { break; } } $CumulativeDeltaTime += $deltatime; $TicksAtCurrentBPM += $deltatime; $MIDIevents["{$tracknumber}"]["{$eventid}"]['deltatime'] = $deltatime; $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1)); if ($MIDI_event_channel & 0x80) { // OK, normal event - MIDI command has MSB set $LastIssuedMIDIcommand = $MIDI_event_channel >> 4; $LastIssuedMIDIchannel = $MIDI_event_channel & 0xf; } else { // running event - assume last command $eventsoffset--; } $MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] = $LastIssuedMIDIcommand; $MIDIevents["{$tracknumber}"]["{$eventid}"]['channel'] = $LastIssuedMIDIchannel; if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0x8) { // Note off (key is released) $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0x9) { // Note on (key is pressed) $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xa) { // Key after-touch $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xb) { // Control Change $controllernum = ord(substr($trackdata, $eventsoffset++, 1)); $newvalue = ord(substr($trackdata, $eventsoffset++, 1)); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xc) { // Program (patch) change $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1)); $MP3fileInfo['midi']['raw']['track']["{$tracknumber}"]['instrumentid'] = $newprogramnum; if ($tracknumber == 10) { $MP3fileInfo['midi']['raw']['track']["{$tracknumber}"]['instrument'] = GeneralMIDIpercussionLookup($newprogramnum); } else { $MP3fileInfo['midi']['raw']['track']["{$tracknumber}"]['instrument'] = GeneralMIDIinstrumentLookup($newprogramnum); } } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xd) { // Channel after-touch $channelnumber = ord(substr($trackdata, $eventsoffset++, 1)); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xe) { // Pitch wheel change (2000H is normal or no change) $changeLSB = ord(substr($trackdata, $eventsoffset++, 1)); $changeMSB = ord(substr($trackdata, $eventsoffset++, 1)); $pitchwheelchange = ($changeMSB & 0x7f) << 7 & ($changeLSB & 0x7f); } else { if ($MIDIevents["{$tracknumber}"]["{$eventid}"]['eventid'] == 0xf && $MIDIevents["{$tracknumber}"]["{$eventid}"]['channel'] == 0xf) { $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1)); $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1)); $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength); $eventsoffset += $METAeventLength; switch ($METAeventCommand) { case 0x0: // Set track sequence number $track_sequence_number = BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['seqno'] = $track_sequence_number; break; case 0x1: // Text: generic $text_generic = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['text'] = $text_generic; if (!isset($MP3fileInfo['midi']['comment'])) { $MP3fileInfo['midi']['comment'] = ''; } $MP3fileInfo['midi']['comment'] .= $text_generic . "\n"; break; case 0x2: // Text: copyright $text_copyright = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['copyright'] = $text_copyright; if (!isset($MP3fileInfo['midi']['copyright'])) { $MP3fileInfo['midi']['copyright'] = ''; } $MP3fileInfo['midi']['copyright'] = $text_copyright . "\n"; break; case 0x3: // Text: track name $text_trackname = substr($METAeventData, 0, $METAeventLength); $MP3fileInfo['midi']['raw']['track']["{$tracknumber}"]['name'] = $text_trackname; break; case 0x4: // Text: track instrument name $text_instrument = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['instrument'] = $text_instrument; break; case 0x5: // Text: lyric $text_lyric = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['lyric'] = $text_lyric; if (!isset($MP3fileInfo['midi']['lyric'])) { $MP3fileInfo['midi']['lyric'] = ''; } $MP3fileInfo['midi']['lyric'] .= $text_lyric . "\n"; break; case 0x6: // Text: marker $text_marker = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['marker'] = $text_marker; break; case 0x7: // Text: cue point $text_cuepoint = substr($METAeventData, 0, $METAeventLength); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['cuepoint'] = $text_cuepoint; break; case 0x2f: // End Of Track //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['EOT'] = $CumulativeDeltaTime; break; case 0x51: // Tempo: microseconds / quarter note $tempo_usperqnote = BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); // $MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['us_qnote'] = $tempo_usperqnote; $MP3fileInfo['midi']['raw']['events']["{$tracknumber}"]["{$CumulativeDeltaTime}"]['us_qnote'] = $tempo_usperqnote; //$MP3fileInfo['playtime_seconds'] += ($TicksAtCurrentBPM / $MP3fileInfo['midi']['raw']['ticksperqnote']) * ($CurrentMicroSecondsPerBeat / 1000000); $CurrentMicroSecondsPerBeat = $tempo_usperqnote; $CurrentBeatsPerMinute = 1000000 / $CurrentMicroSecondsPerBeat * 60; $MicroSecondsPerQuarterNoteAfter["{$CumulativeDeltaTime}"] = $CurrentMicroSecondsPerBeat; $TicksAtCurrentBPM = 0; break; case 0x58: // Time signature $timesig_numerator = BigEndian2Int(substr($METAeventData, 0, 1)); $timesig_denominator = pow(2, BigEndian2Int(substr($METAeventData, 1, 1))); // $02 -> x/4, $03 -> x/8, etc $timesig_32inqnote = BigEndian2Int(substr($METAeventData, 2, 1)); // number of 32nd notes to the quarter note //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['timesig_32inqnote'] = $timesig_32inqnote; //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['timesig_numerator'] = $timesig_numerator; //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['timesig_denominator'] = $timesig_denominator; //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator; $MP3fileInfo['midi']['timesignature'][] = $timesig_numerator . '/' . $timesig_denominator; break; case 0x59: // Keysignature $keysig_sharpsflats = BigEndian2Int(substr($METAeventData, 0, 1)) & 0x7f; // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps) $keysig_sfsign = BigEndian2Int(substr($METAeventData, 0, 1)) & 0x80; if ($keysig_sfsign == 1) { $keysig_sharpsflats = 0 - $keysig_sharpsflats; } $keysig_majorminor = BigEndian2Int(substr($METAeventData, 1, 1)); // 0 -> major, 1 -> minor $keysigs = array(-7 => 'Cb', -6 => 'Gb', -5 => 'Db', -4 => 'Ab', -3 => 'Eb', -2 => 'Bb', -1 => 'F', 0 => 'C', 1 => 'G', 2 => 'D', 3 => 'A', 4 => 'E', 5 => 'B', 6 => 'F#', 7 => 'C#'); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0); //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['keysig_minor'] = (bool) $keysig_majorminor; //$MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['keysig_text'] = $keysigs["$keysig_sharpsflats"].' '.($MP3fileInfo['midi']['raw']['events']["$tracknumber"]["$eventid"]['keysig_minor'] ? 'minor' : 'major'); $MP3fileInfo['midi']['keysignature'][] = $keysigs["{$keysig_sharpsflats}"] . ' ' . ((bool) $keysig_majorminor ? 'minor' : 'major'); break; case 0x7f: // Sequencer specific information $custom_data = substr($METAeventData, 0, $METAeventLength); break; default: break; } } else { // unknown MIDI event? } } } } } } } } } if ($tracknumber > 0) { $MP3fileInfo['midi']['totalticks'] = max($MP3fileInfo['midi']['totalticks'], $CumulativeDeltaTime); } } $previoustickoffset = 0; foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) { if ($MP3fileInfo['midi']['totalticks'] > $tickoffset) { $MP3fileInfo['playtime_seconds'] += ($tickoffset - $previoustickoffset) / $MP3fileInfo['midi']['raw']['ticksperqnote'] * ($microsecondsperbeat / 1000000); $previoustickoffset = $tickoffset; } } if ($MP3fileInfo['midi']['totalticks'] > $previoustickoffset) { $MP3fileInfo['playtime_seconds'] += ($MP3fileInfo['midi']['totalticks'] - $previoustickoffset) / $MP3fileInfo['midi']['raw']['ticksperqnote'] * ($microsecondsperbeat / 1000000); } } return TRUE; } }
function ID3v2FrameProcessing($frame_name, $frame_flags, &$MP3fileInfo) { // define $frame_arrayindex once here (used for many frames), override or ignore as neccesary $frame_arrayindex = count($MP3fileInfo['id3']['id3v2']["{$frame_name}"]); // 'data', 'datalength' if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags'])) { $frame_arrayindex--; } if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'])) { $frame_arrayindex--; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { // frame flags are not part of the ID3v2.2 standard if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { // Frame Header Flags // %abc00000 %ijk00000 $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['TagAlterPreservation'] = (bool) substr($frame_flags, 0, 1); // a - Tag alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['FileAlterPreservation'] = (bool) substr($frame_flags, 1, 1); // b - File alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['ReadOnly'] = (bool) substr($frame_flags, 2, 1); // c - Read only $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'] = (bool) substr($frame_flags, 8, 1); // i - Compression $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Encryption'] = (bool) substr($frame_flags, 9, 1); // j - Encryption $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['GroupingIdentity'] = (bool) substr($frame_flags, 10, 1); // k - Grouping identity } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4) { // Frame Header Flags // %0abc0000 %0h00kmnp $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['TagAlterPreservation'] = (bool) substr($frame_flags, 1, 1); // a - Tag alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['FileAlterPreservation'] = (bool) substr($frame_flags, 2, 1); // b - File alter preservation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['ReadOnly'] = (bool) substr($frame_flags, 3, 1); // c - Read only $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['GroupingIdentity'] = (bool) substr($frame_flags, 9, 1); // h - Grouping identity $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'] = (bool) substr($frame_flags, 12, 1); // k - Compression $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Encryption'] = (bool) substr($frame_flags, 13, 1); // m - Encryption $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Unsynchronisation'] = (bool) substr($frame_flags, 14, 1); // n - Unsynchronisation $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['DataLengthIndicator'] = (bool) substr($frame_flags, 15, 1); // p - Data length indicator } } // Frame-level de-unsynchronization - ID3v2.4 if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['Unsynchronisation'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = DeUnSynchronise($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } // Frame-level de-compression if (isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression'])) { // it's on the wishlist :) } } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'UFID' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'UFI') { // 4.1 UFI Unique file identifier // There may be more than one 'UFID' frame in a tag, // but only one with the same 'Owner identifier'. // <Header for 'Unique file identifier', ID: 'UFID'> // Owner identifier <text string> $00 // Identifier <up to 64 bytes binary data> $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0)); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_idstring; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'TXXX' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'TXX') { // 4.2.2 TXX User defined text information frame // There may be more than one 'TXXX' frame in each tag, // but only one with the same description. // <Header for 'User defined text information frame', ID: 'TXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // Value <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'], $frame_textencoding); } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($frame_name[0] == 'T') { // 4.2. T??[?] Text information frame // There may only be one text information frame of its kind in an tag. // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', // excluding 'TXXX' described in 4.2.6.> // Text encoding $xx // Information <text string(s) according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); // $MP3fileInfo['id3']['id3v2']["$frame_name"]['data'] = substr($MP3fileInfo['id3']['id3v2']["$frame_name"]['data'], $frame_offset); // this one-line method should work, but as a safeguard against null-padded data, do it the safe way $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); } else { // no null bytes following data, just use all data $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression']) || !$MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['compression']) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'WXXX' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'WXX') { // 4.3.2 WXX User defined URL link frame // There may be more than one 'WXXX' frame in each tag, // but only one with the same description // <Header for 'User defined URL link frame', ID: 'WXXX'> // Text encoding $xx // Description <text string according to encoding> $00 (00) // URL <text string> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding)); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } if ($frame_terminatorpos) { // there are null bytes after the data - this is not according to spec // only use data up to first null byte $frame_urldata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); } else { // no null bytes following data, just use all data $frame_urldata = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = $frame_urldata; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($frame_name[0] == 'W') { // 4.3. W??? URL link frames // There may only be one URL link frame of its kind in a tag, // except when stated otherwise in the frame description // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' // described in 4.3.2.> // URL <text string> $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = trim($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'IPLS' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'IPL') { // 4.4 IPL Involved people list (ID3v2.2 only) // There may only be one 'IPL' frame in each tag // <Header for 'User defined URL link frame', ID: 'IPL'> // Text encoding $xx // People list strings <textstrings> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'MCDI' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'MCI') { // 4.5 MCI Music CD identifier // There may only be one 'MCDI' frame in each tag // <Header for 'Music CD identifier', ID: 'MCDI'> // CD TOC <binary data> $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); // no other special processing needed } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'ETCO' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'ETC') { // 4.6 ETC Event timing codes // There may only be one 'ETCO' frame in each tag // <Header for 'Event timing codes', ID: 'ETCO'> // Time stamp format $xx // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Followed by a list of key events in the following format: // Type of event $xx // Time stamp $xx (xx ...) // The 'Time stamp' is set to zero if directly at the beginning of the sound // or after the previous event. All events MUST be sorted in chronological order. $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['typeid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['type'] = ETCOEventLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['typeid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestamp'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'MLLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'MLL') { // 4.7 MLL MPEG location lookup table // There may only be one 'MLLT' frame in each tag // <Header for 'Location lookup table', ID: 'MLLT'> // MPEG frames between reference $xx xx // Bytes between reference $xx xx xx // Milliseconds between reference $xx xx xx // Bits for bytes deviation $xx // Bits for milliseconds dev. $xx // Then for every reference the following data is included; // Deviation in bytes %xxx.... // Deviation in milliseconds %xxx.... $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framesbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, 2)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bytesbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 2, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['msbetweenreferences'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 5, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 8, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 9, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 10); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $deviationbitstream .= BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); } while (strlen($deviationbitstream)) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'])); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['msdeviation'] = bindec(substr($deviationbitstream, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation'])); $deviationbitstream = substr($deviationbitstream, $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsforbytesdeviation'] + $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsformsdeviation']); $frame_arrayindex++; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'SYTC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'STC') { // 4.8 STC Synchronised tempo codes // There may only be one 'SYTC' frame in each tag // <Header for 'Synchronised tempo codes', ID: 'SYTC'> // Time stamp format $xx // Tempo data <binary data> // Where time stamp format is: // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); while ($frame_offset < strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] == 255) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['tempo'] += ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestamp'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $frame_arrayindex++; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'USLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'ULT') { // 4.9 ULT Unsynchronised lyric/text transcription // There may be more than one 'Unsynchronised lyrics/text transcription' frame // in each tag, but only one with the same language and content descriptor. // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> // Text encoding $xx // Language $xx xx xx // Content descriptor <text string according to encoding> $00 (00) // Lyrics/text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'], $frame_textencoding); } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'SYLT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'SLT') { // 4.10 SLT Synchronised lyric/text // There may be more than one 'SYLT' frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> // Text encoding $xx // Language $xx xx xx // Time stamp format $xx // $01 (32-bit value) MPEG frames from beginning of file // $02 (32-bit value) milliseconds from beginning of file // Content type $xx // Content descriptor <text string according to encoding> $00 (00) // Terminated text to be synced (typically a syllable) // Sync identifier (terminator to above string) $00 (00) // Time stamp $xx (xx ...) $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttypeid'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttype'] = SYTLContentTypeLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contenttypeid']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $timestampindex = 0; $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_terminatorpos = strpos($frame_remainingdata, TextEncodingLookup('terminator', $frame_textencoding)); if ($frame_terminatorpos === FALSE) { $frame_remainingdata = ''; } else { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['data'], $frame_textencoding); } $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) { // timestamp probably omitted for first data item } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$timestampindex}"]['timestamp'] = BigEndian2Int(substr($frame_remainingdata, 0, 4)); $frame_remainingdata = substr($frame_remainingdata, 4); } $timestampindex++; } } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'COMM' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'COM') { // 4.11 COM Comments // There may be more than one comment frame in each tag, // but only one with the same language and content descriptor. // <Header for 'Comment', ID: 'COMM'> // Text encoding $xx // Language $xx xx xx // Short content descrip. <text string according to encoding> $00 (00) // The actual text <full text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_text = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = $frame_text; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidata'] = RoughTranslateUnicodeToASCII($frame_text, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'RVA2') { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) // There may be more than one 'RVA2' frame in each tag, // but only one with the same identification string // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> // Identification <text string> $00 // The 'identification' string is used to identify the situation and/or // device where this adjustment should apply. The following is then // repeated for every channel: // Type of channel $xx // Volume adjustment $xx xx // Bits representing peak $xx // Peak volume $xx (xx ...) $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0)); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], 0, $frame_terminatorpos); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_idstring; while (strlen($frame_remainingdata)) { $frame_offset = 0; $frame_channeltypeid = substr($frame_remainingdata, $frame_offset++, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['channeltypeid'] = $frame_channeltypeid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['channeltype'] = RVA2ChannelTypeLookup($frame_channeltypeid); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['volumeadjust'] = BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2)) - 0x7fff; // 16-bit signed $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); $frame_bytespeakvolume = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_channeltypeid}"]['bitspeakvolume'] / 8); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['peakvolume'] = BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]["{$frame_channeltypeid}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'RVAD' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'RVA') { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) // There may only be one 'RVA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'RVA'> // ID3v2.2 => Increment/decrement %000000ba // ID3v2.3 => Increment/decrement %00fedcba // Bits used for volume descr. $xx // Relative volume change, right $xx xx (xx ...) // a // Relative volume change, left $xx xx (xx ...) // b // Peak volume right $xx xx (xx ...) // Peak volume left $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, right back $xx xx (xx ...) // c // Relative volume change, left back $xx xx (xx ...) // d // Peak volume right back $xx xx (xx ...) // Peak volume left back $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, center $xx xx (xx ...) // e // Peak volume center $xx xx (xx ...) // ID3v2.3 only, optional (not present in ID3v2.2): // Relative volume change, bass $xx xx (xx ...) // f // Peak volume bass $xx xx (xx ...) $frame_offset = 0; $frame_incrdecrflags = BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsvolume'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_bytesvolume = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsvolume'] / 8); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['right'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['right'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['left'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['left'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['rightrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['rightrear'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['rightrear'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['leftrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['leftrear'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['leftrear'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['rightrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['leftrear'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['center'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['center'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['center'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['center'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']) > 0) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['bass'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['incdec']['bass'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['volumechange']['bass'] *= -1; } $frame_offset += $frame_bytesvolume; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakvolume']['bass'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesvolume)); $frame_offset += $frame_bytesvolume; } } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'EQU2') { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) // There may be more than one 'EQU2' frame in each tag, // but only one with the same identification string // <Header of 'Equalisation (2)', ID: 'EQU2'> // Interpolation method $xx // $00 Band // $01 Linear // Identification <text string> $00 // The following is then repeated for every adjustment point // Frequency $xx xx // Volume adjustment $xx xx $frame_offset = 0; $frame_interpolationmethod = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_idstring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_idstring) === 0) { $frame_idstring = ''; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_idstring; $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(chr(0))); while (strlen($frame_remainingdata)) { $frame_frequency = BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']["{$frame_frequency}"] = BigEndian2Int(substr($frame_remainingdata, 2, 2)); $frame_remainingdata = substr($frame_remainingdata, 4); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['interpolationmethod'] = $frame_interpolationmethod; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3 && $frame_name == 'EQUA' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'EQU') { // 4.13 EQU Equalisation (ID3v2.2 only) // There may only be one 'EQUA' frame in each tag // <Header for 'Relative volume adjustment', ID: 'EQU'> // Adjustment bits $xx // This is followed by 2 bytes + ('adjustment bits' rounded up to the // nearest byte) for every equalisation band in the following format, // giving a frequency range of 0 - 32767Hz: // Increment/decrement %x (MSB of the Frequency) // Frequency (lower 15 bits) // Adjustment $xx (xx ...) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['adjustmentbits'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1); $frame_adjustmentbytes = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['adjustmentbits'] / 8); $frame_remainingdata = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); while (strlen($frame_remainingdata)) { $frame_frequencystr = BigEndian2Bin(substr($frame_remainingdata, 0, 2)); $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['incdec'] = $frame_incdec; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['adjustment'] = BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); if ($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['incdec'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_frequency}"]['adjustment'] *= -1; } $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RVRB' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'REV') { // 4.14 REV Reverb // There may only be one 'RVRB' frame in each tag. // <Header for 'Reverb', ID: 'RVRB'> // Reverb left (ms) $xx xx // Reverb right (ms) $xx xx // Reverb bounces, left $xx // Reverb bounces, right $xx // Reverb feedback, left to left $xx // Reverb feedback, left to right $xx // Reverb feedback, right to right $xx // Reverb feedback, right to left $xx // Premix left to right $xx // Premix right to left $xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['left'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['right'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bouncesL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bouncesR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackLL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackLR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackRR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['feedbackRL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['premixLR'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['premixRL'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'APIC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'PIC') { // 4.15 PIC Attached picture // There may be several pictures attached to one file, // each in their individual 'APIC' frame, but only one // with the same content descriptor // <Header for 'Attached picture', ID: 'APIC'> // Text encoding $xx // ID3v2.3+ => MIME type <text string> $00 // ID3v2.2 => Image format $xx xx xx // Picture type $xx // Description <text string according to encoding> $00 (00) // Picture data <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $frame_imagetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); if (strtolower($frame_imagetype) == 'ima') { // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoff@pacbell.net) $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); if ($frame_imagetype == 'JPEG') { $frame_imagetype = 'JPG'; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); } else { $frame_offset += 3; } } if ($MP3fileInfo['id3']['id3v2']['majorversion'] > 2) { $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); } $frame_picturetype = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['imagetype'] = $frame_imagetype; } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['picturetypeid'] = $frame_picturetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['picturetype'] = APICPictureTypeLookup($frame_picturetype); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding))); include_once GETID3_INCLUDEPATH . 'getid3.getimagesize.php'; $imagechunkcheck = GetDataImageSize($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']); if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) { $imagetypes = array(1 => 'image/gif', 2 => 'image/jpeg', 3 => 'image/png'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_mime'] = $imagetypes["{$imagechunkcheck[2]}"]; if ($imagechunkcheck[0]) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_width'] = $imagechunkcheck[0]; } if ($imagechunkcheck[1]) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_height'] = $imagechunkcheck[1]; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['image_bytes'] = strlen($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data']); //$MP3fileInfo['id3']['id3v2']["$frame_name"]["$frame_arrayindex"]['image_offset'] = $MP3fileInfo['id3']['id3v2']["$frame_name"]['dataoffset'] + $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)) + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'GEOB' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'GEO') { // 4.16 GEO General encapsulated object // There may be more than one 'GEOB' frame in each tag, // but only one with the same content descriptor // <Header for 'General encapsulated object', ID: 'GEOB'> // Text encoding $xx // MIME type <text string> $00 // Filename <text string according to encoding> $00 (00) // Content description <text string according to encoding> $00 (00) // Encapsulated object <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_mimetype) === 0) { $frame_mimetype = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_filename = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_filename) === 0) { $frame_filename = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['objectdata'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['filename'] = $frame_filename; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; if (!isset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression']) || $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags']['compression'] === FALSE) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); } unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'PCNT' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CNT') { // 4.17 CNT Play counter // There may only be one 'PCNT' frame in each tag. // When the counter reaches all one's, one byte is inserted in // front of the counter thus making the counter eight bits bigger // <Header for 'Play counter', ID: 'PCNT'> // Counter $xx xx xx xx (xx ...) $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'POPM' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'POP') { // 4.18 POP Popularimeter // There may be more than one 'POPM' frame in each tag, // but only one with the same email address // <Header for 'Popularimeter', ID: 'POPM'> // Email to user <text string> $00 // Rating $xx // Counter $xx xx xx xx (xx ...) $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_emailaddress = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_emailaddress) === 0) { $frame_emailaddress = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_rating = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['email'] = $frame_emailaddress; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['rating'] = $frame_rating; if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RBUF' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'BUF') { // 4.19 BUF Recommended buffer size // There may only be one 'RBUF' frame in each tag // <Header for 'Recommended buffer size', ID: 'RBUF'> // Buffer size $xx xx xx // Embedded info flag %0000000x // Offset to next tag $xx xx xx xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['buffersize'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3)); $frame_offset += 3; $frame_embeddedinfoflags = BigEndian2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['nexttagoffset'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CRM') { // 4.20 Encrypted meta frame (ID3v2.2 only) // There may be more than one 'CRM' frame in a tag, // but only one with the same 'owner identifier' // <Header for 'Encrypted meta frame', ID: 'CRM'> // Owner identifier <textstring> $00 (00) // Content/explanation <textstring> $00 (00) // Encrypted datablock <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = count($MP3fileInfo['id3']['id3v2']["{$frame_name}"]) - 1; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'AENC' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'CRA') { // 4.21 CRA Audio encryption // There may be more than one 'AENC' frames in a tag, // but only one with the same 'Owner identifier' // <Header for 'Audio encryption', ID: 'AENC'> // Owner identifier <text string> $00 // Preview start $xx xx // Preview length $xx xx // Encryption info <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid == ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['previewstart'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['previewlength'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encryptioninfo'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_ownerid}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'LINK' || $MP3fileInfo['id3']['id3v2']['majorversion'] == 2 && $frame_name == 'LNK') { // 4.22 LNK Linked information // There may be more than one 'LINK' frame in a tag, // but only one with the same contents // <Header for 'Linked information', ID: 'LINK'> // ID3v2.3+ => Frame identifier $xx xx xx xx // ID3v2.2 => Frame identifier $xx xx xx // URL <text string> $00 // ID and additional data <text string(s)> $frame_offset = 0; if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['frameid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; } else { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['frameid'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4); $frame_offset += 4; } $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_url = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_url) === 0) { $frame_url = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['url'] = $frame_url; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['additionaldata'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'POSS') { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) // There may only be one 'POSS' frame in each tag // <Head for 'Position synchronisation', ID: 'POSS'> // Time stamp format $xx // Position $xx (xx ...) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['timestampformat'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['position'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'USER') { // 4.22 USER Terms of use (ID3v2.3+ only) // There may be more than one 'Terms of use' frame in a tag, // but only one with the same 'Language' // <Header for 'Terms of use frame', ID: 'USER'> // Text encoding $xx // Language $xx xx xx // The actual text <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_language = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 3); $frame_offset += 3; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['language'] = $frame_language; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['languagename'] = LanguageLookup($frame_language, FALSE); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); if (!$MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['flags']['compression']) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['asciidata'] = RoughTranslateUnicodeToASCII($MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['data'], $frame_textencoding); } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_language}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'OWNE') { // 4.23 OWNE Ownership frame (ID3v2.3+ only) // There may only be one 'OWNE' frame in a tag // <Header for 'Ownership frame', ID: 'OWNE'> // Text encoding $xx // Price paid <text string> $00 // Date of purch. <text string> // Seller <text string according to encoding> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_pricepaid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currency'] = LookupCurrency($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['currencyid'], 'units'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['pricepaid']['value'] = substr($frame_pricepaid, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 8); if (!IsValidDateStampString($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedateunix'] = mktime(0, 0, 0, substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 4, 2), substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 6, 2), substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['purchasedate'], 0, 4)); } $frame_offset += 8; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['seller'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'COMR') { // 4.24 COMR Commercial frame (ID3v2.3+ only) // There may be more than one 'commercial frame' in a tag, // but no two may be identical // <Header for 'Commercial frame', ID: 'COMR'> // Text encoding $xx // Price string <text string> $00 // Valid until <text string> // Contact URL <text string> $00 // Received as $xx // Name of seller <text string according to encoding> $00 (00) // Description <text string according to encoding> $00 (00) // Picture MIME type <string> $00 // Seller logo <binary data> $frame_offset = 0; $frame_textencoding = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_pricestring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_rawpricearray = explode('/', $frame_pricestring); foreach ($frame_rawpricearray as $key => $val) { $frame_currencyid = substr($val, 0, 3); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['price']["{$frame_currencyid}"]['currency'] = LookupCurrency($frame_currencyid, 'units'); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['price']["{$frame_currencyid}"]['value'] = substr($val, 3); } $frame_datestring = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 8); $frame_offset += 8; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_contacturl = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_receivedasid = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_sellername = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_sellername) === 0) { $frame_sellername = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], TextEncodingLookup('terminator', $frame_textencoding), $frame_offset); if (ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)), 1)) === 0) { $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 } $frame_description = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_description) === 0) { $frame_description = ''; } $frame_offset = $frame_terminatorpos + strlen(TextEncodingLookup('terminator', $frame_textencoding)); $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_mimetype = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_offset = $frame_terminatorpos + strlen(chr(0)); $frame_sellerlogo = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encodingid'] = $frame_textencoding; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['encoding'] = TextEncodingLookup('encoding', $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['pricevaliduntil'] = $frame_datestring; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['contacturl'] = $frame_contacturl; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['receivedasid'] = $frame_receivedasid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['receivedas'] = COMRReceivedAsLookup($frame_receivedasid); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['sellername'] = $frame_sellername; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciisellername'] = RoughTranslateUnicodeToASCII($frame_sellername, $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['description'] = $frame_description; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['asciidescription'] = RoughTranslateUnicodeToASCII($frame_description, $frame_textencoding); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['mime'] = $frame_mimetype; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['logo'] = $frame_sellerlogo; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'ENCR') { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) // There may be several 'ENCR' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Encryption method registration', ID: 'ENCR'> // Owner identifier <text string> $00 // Method symbol $xx // Encryption data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['methodsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'GRID') { // 4.26 GRID Group identification registration (ID3v2.3+ only) // There may be several 'GRID' frames in a tag, // but only one containing the same symbol // and only one containing the same owner identifier // <Header for 'Group ID registration', ID: 'GRID'> // Owner identifier <text string> $00 // Group symbol $xx // Group dependent data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['groupsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'PRIV') { // 4.27 PRIV Private frame (ID3v2.3+ only) // The tag may contain more than one 'PRIV' frame // but only with different contents // <Header for 'Private frame', ID: 'PRIV'> // Owner identifier <text string> $00 // The private data <binary data> $frame_offset = 0; $frame_terminatorpos = strpos($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], chr(0), $frame_offset); $frame_ownerid = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_terminatorpos - $frame_offset); if (ord($frame_ownerid) === 0) { $frame_ownerid = ''; } $frame_offset = $frame_terminatorpos + strlen(chr(0)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['ownerid'] = $frame_ownerid; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'SIGN') { // 4.28 SIGN Signature frame (ID3v2.4+ only) // There may be more than one 'signature frame' in a tag, // but no two may be identical // <Header for 'Signature frame', ID: 'SIGN'> // Group symbol $xx // Signature <binary data> $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['groupsymbol'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['data'] = substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['flags'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['flags']); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['datalength'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]["{$frame_arrayindex}"]['dataoffset'] = $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']; unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'SEEK') { // 4.29 SEEK Seek frame (ID3v2.4+ only) // There may only be one 'seek frame' in a tag // <Header for 'Seek frame', ID: 'SEEK'> // Minimum offset to next tag $xx xx xx xx $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 4 && $frame_name == 'ASPI') { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) // There may only be one 'audio seek point index' frame in a tag // <Header for 'Seek Point Index', ID: 'ASPI'> // Indexed data start (S) $xx xx xx xx // Indexed data length (L) $xx xx xx xx // Number of index points (N) $xx xx // Bits per index point (b) $xx // Then for every index point the following data is included: // Fraction at index (Fi) $xx (xx) $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datastart'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexeddatalength'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexpoints'] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsperpoint'] = ord(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset++, 1)); $frame_bytesperpoint = ceil($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['bitsperpoint'] / 8); for ($i = 0; $i < $frame_indexpoints; $i++) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['indexes']["{$i}"] = BigEndian2Int(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, $frame_bytesperpoint)); $frame_offset += $frame_bytesperpoint; } $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] >= 3 && $frame_name == 'RGAD') { // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html // There may only be one 'RGAD' frame in a tag // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> // Peak Amplitude $xx $xx $xx $xx // Radio Replay Gain Adjustment %aaabbbcd %dddddddd // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd // a - name code // b - originator code // c - sign bit // d - replay gain adjustment $frame_offset = 0; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['peakamplitude'] = BigEndian2Float(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 4)); $frame_offset += 4; $radioadjustment = Dec2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $audiophileadjustment = Dec2Bin(substr($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'], $frame_offset, 2)); $frame_offset += 2; $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['name'] = Bin2Dec(substr($radioadjustment, 0, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['originator'] = Bin2Dec(substr($radioadjustment, 3, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['signbit'] = Bin2Dec(substr($radioadjustment, 6, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['adjustment'] = Bin2Dec(substr($radioadjustment, 7, 9)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['name'] = Bin2Dec(substr($audiophileadjustment, 0, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['originator'] = Bin2Dec(substr($audiophileadjustment, 3, 3)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['signbit'] = Bin2Dec(substr($audiophileadjustment, 6, 1)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['adjustment'] = Bin2Dec(substr($audiophileadjustment, 7, 9)); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['name'] = RGADnameLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['name']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['originator'] = RGADoriginatorLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['originator']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['radio']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['adjustment'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['radio']['signbit']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['name'] = RGADnameLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['name']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['originator'] = RGADoriginatorLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['originator']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['audiophile']['adjustment'] = RGADadjustmentLookup($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['adjustment'], $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['raw']['audiophile']['signbit']); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['framenamelong'] = FrameNameLongLookup($frame_name); unset($MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data']); } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } return TRUE; }
function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed = false) { if ($ThisFileInfo['fileformat'] == 'riff') { return LittleEndian2Int($byteword, $signed); } return BigEndian2Int($byteword, false, $signed); }
function getVQFHeaderFilepointer(&$fd, &$ThisFileInfo) { // based loosely on code from TTwinVQ by Jurgen Faul // jfaul@gmx.de http://jfaul.de/atl $ThisFileInfo['fileformat'] = 'vqf'; $ThisFileInfo['audio']['dataformat'] = 'vqf'; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['lossless'] = false; $HasVQFTags = false; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $VQFheaderData = fread($fd, 16); $offset = 0; $ThisFileInfo['vqf']['raw']['header_tag'] = substr($VQFheaderData, $offset, 4); $offset += 4; $ThisFileInfo['vqf']['raw']['version'] = substr($VQFheaderData, $offset, 8); $offset += 8; $ThisFileInfo['vqf']['raw']['size'] = BigEndian2Int(substr($VQFheaderData, $offset, 4)); $offset += 4; while (ftell($fd) < $ThisFileInfo['avdataend']) { $ChunkBaseOffset = ftell($fd); $chunkoffset = 0; $ChunkData = fread($fd, 8); $ChunkName = substr($ChunkData, $chunkoffset, 4); if ($ChunkName == 'DATA') { $ThisFileInfo['avdataoffset'] = $ChunkBaseOffset; break; } $chunkoffset += 4; $ChunkSize = BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; if ($ChunkSize > $ThisFileInfo['avdataend'] - ftell($fd)) { $ThisFileInfo['error'] .= "\n" . 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } $ChunkData .= fread($fd, $ChunkSize); switch ($ChunkName) { case 'COMM': $ThisFileInfo['vqf']["{$ChunkName}"]['channel_mode'] = BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $ThisFileInfo['vqf']["{$ChunkName}"]['bitrate'] = BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $ThisFileInfo['vqf']["{$ChunkName}"]['sample_rate'] = BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $ThisFileInfo['vqf']["{$ChunkName}"]['security_level'] = BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['vqf']["{$ChunkName}"]['channel_mode'] + 1; $ThisFileInfo['audio']['sample_rate'] = VQFchannelFrequencyLookup($ThisFileInfo['vqf']["{$ChunkName}"]['sample_rate']); $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['vqf']["{$ChunkName}"]['bitrate'] * 1000; if ($ThisFileInfo['audio']['bitrate'] == 0) { $ThisFileInfo['error'] .= 'Corrupt VQF file: bitrate_audio == zero'; return false; } break; case 'NAME': case 'AUTH': case '(c) ': case 'FILE': case 'COMT': case 'ALBM': $HasVQFTags = true; $ThisFileInfo['vqf']['comments'][VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); break; case 'DSIZ': $ThisFileInfo['vqf']['DSIZ'] = BigEndian2Int(substr($ChunkData, 8, 4)); break; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled chunk type "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } } $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; if (isset($ThisFileInfo['vqf']['DSIZ']) && $ThisFileInfo['vqf']['DSIZ'] != $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')) { switch ($ThisFileInfo['vqf']['DSIZ']) { case 0: case 1: $ThisFileInfo['warning'] .= "\n" . 'Invalid DSIZ value "' . $ThisFileInfo['vqf']['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($ThisFileInfo['vqf']['DSIZ'] + 1) . '.0'; $ThisFileInfo['audio']['encoder'] = 'Ahead Nero'; break; default: $ThisFileInfo['warning'] .= "\n" . 'Probable corrupted file - should be ' . $ThisFileInfo['vqf']['DSIZ'] . ' bytes, actually ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')); break; } } // Any VQF tags present? if ($HasVQFTags) { // add tag to array of tags $ThisFileInfo['tags'][] = 'vqf'; // Yank other comments - VQF highest preference CopyFormatCommentsToRootComments($ThisFileInfo['vqf']['comments'], $ThisFileInfo, true, true, true); } return true; }
function QuicktimeParseContainerAtom($atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy) { $atomstructure = false; $subatomoffset = 0; $subatomcounter = 0; if (strlen($atomdata) == 4 && BigEndian2Int($atomdata) == 0x0) { return false; } while ($subatomoffset < strlen($atomdata)) { $subatomsize = BigEndian2Int(substr($atomdata, $subatomoffset + 0, 4)); $subatomname = substr($atomdata, $subatomoffset + 4, 4); $subatomdata = substr($atomdata, $subatomoffset + 8, $subatomsize - 8); if ($subatomsize == 0) { // Furthermore, for historical reasons the list of atoms is optionally // terminated by a 32-bit integer set to 0. If you are writing a program // to read user data atoms, you should allow for the terminating 0. return $atomstructure; } $atomstructure[$subatomcounter] = QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $ThisFileInfo, $baseoffset + $subatomoffset, $atomHierarchy); $subatomoffset += $subatomsize; $subatomcounter++; } return $atomstructure; }
function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) { $offset = 0; $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), ""); $offset += 128; $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80); $offset += 1; $offset += 258; // reserved $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) { $TrackSampleOffset = BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; $TrackNumber = BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12); $offset += 12; $TrackFlagsRaw = BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); $offset += 13; // reserved $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = BigEndian2Int(substr($METAdataBlockData, $offset, 1)); $offset += 1; for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { $IndexSampleOffset = BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 8; $IndexNumber = BigEndian2Int(substr($METAdataBlockData, $offset, 8)); $offset += 1; $offset += 3; // reserved $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; } } return true; }
function getMPEGHeaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'mpeg'; // Start code 32 bits // horizontal frame size 12 bits // vertical frame size 12 bits // pixel aspect ratio 4 bits // frame rate 4 bits // bitrate 18 bits // marker bit 1 bit // VBV buffer size 10 bits // constrained parameter flag 1 bit // intra quant. matrix flag 1 bit // intra quant. matrix values 512 bits (present if matrix flag == 1) // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $MPEGvideoHeader = fread($fd, FREAD_BUFFER_SIZE); $offset = 0; // MPEG video information is found as $00 $00 $01 $B3 $matching_pattern = chr(0x0) . chr(0x0) . chr(0x1) . chr(0xb3); while (substr($MPEGvideoHeader, $offset++, 4) !== $matching_pattern) { if ($offset >= strlen($MPEGvideoHeader) - 12) { $MPEGvideoHeader .= fread($fd, FREAD_BUFFER_SIZE); $MPEGvideoHeader = substr($MPEGvideoHeader, $offset); $offset = 0; if (strlen($MPEGvideoHeader) < 12) { $ThisFileInfo['error'] .= "\n" . 'Could not find start of video block before end of file'; unset($ThisFileInfo['fileformat']); return false; } elseif (ftell($fd) >= 100000) { $ThisFileInfo['error'] .= "\n" . 'Could not find start of video block in the first 100,000 bytes (this might not be an MPEG-video file?)'; unset($ThisFileInfo['fileformat']); return false; } } } $ThisFileInfo['video']['dataformat'] = 'mpeg'; $offset += strlen($matching_pattern) - 1; $FrameSizeAspectRatioFrameRateDWORD = BigEndian2Int(substr($MPEGvideoHeader, $offset, 4)); $offset += 4; $assortedinformation = BigEndian2Int(substr($MPEGvideoHeader, $offset, 4)); $offset += 4; $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeAspectRatioFrameRateDWORD & 4293918720.0) >> 20; // 12 bits for horizontal frame size $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeAspectRatioFrameRateDWORD & 0xfff00) >> 8; // 12 bits for vertical frame size $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($FrameSizeAspectRatioFrameRateDWORD & 0xf0) >> 4; $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = $FrameSizeAspectRatioFrameRateDWORD & 0xf; $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['frame_rate'] = MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = ($assortedinformation & 4294950912.0) >> 14; $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = ($assortedinformation & 0x2000) >> 13; $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = ($assortedinformation & 0x1ff8) >> 3; $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = ($assortedinformation & 0x4) >> 2; $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = ($assortedinformation & 0x2) >> 1; if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3ffff) { // 18 set bits $ThisFileInfo['mpeg']['video']['bitrate_type'] = 'variable'; $ThisFileInfo['bitrate_mode'] = 'vbr'; } else { $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; } $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; return true; }
function read_int($fp, $offset, $filelen, $path) { return BigEndian2Int(read_chunk($fp, $offset, $filelen, $path, 4)); }
function getID3v2Filepointer(&$fd, &$ThisFileInfo) { // Overall tag structure: // +-----------------------------+ // | Header (10 bytes) | // +-----------------------------+ // | Extended Header | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Frames (variable length) | // +-----------------------------+ // | Padding | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Footer (10 bytes, OPTIONAL) | // +-----------------------------+ // Header // ID3v2/file identifier "ID3" // ID3v2 version $04 00 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) // ID3v2 size 4 * %0xxxxxxx rewind($fd); $header = fread($fd, 10); if (substr($header, 0, 3) == 'ID3') { $ThisFileInfo['id3v2']['header'] = true; $ThisFileInfo['id3v2']['majorversion'] = ord($header[3]); $ThisFileInfo['id3v2']['minorversion'] = ord($header[4]); } else { return false; } if ($ThisFileInfo['id3v2']['majorversion'] > 4) { // this script probably won't correctly parse ID3v2.5.x and above. $ThisFileInfo['error'] .= "\n" . 'this script only parses up to ID3v2.4.x - this tag is ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . '.' . $ThisFileInfo['id3v2']['minorversion']; return false; } $id3_flags = BigEndian2Bin($header[5]); switch ($ThisFileInfo['id3v2']['majorversion']) { case 2: // %ab000000 in v2.2 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['compression'] = $id3_flags[1]; // b - Compression break; case 3: // %abc00000 in v2.3 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $ThisFileInfo['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator break; case 4: // %abcd0000 in v2.4 $ThisFileInfo['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $ThisFileInfo['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $ThisFileInfo['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator $ThisFileInfo['id3v2']['flags']['isfooter'] = $id3_flags[3]; // d - Footer present break; } $ThisFileInfo['id3v2']['headerlength'] = BigEndian2Int(substr($header, 6, 4), 1) + ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); // Extended Header if (isset($ThisFileInfo['id3v2']['flags']['exthead']) && $ThisFileInfo['id3v2']['flags']['exthead']) { // Extended header size 4 * %0xxxxxxx // Number of flag bytes $01 // Extended Flags $xx // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. $extheader = fread($fd, 4); $ThisFileInfo['id3v2']['extheaderlength'] = BigEndian2Int($extheader, 1); // The extended flags field, with its size described by 'number of flag bytes', is defined as: // %0bcd0000 // b - Tag is an update // Flag data length $00 // c - CRC data present // Flag data length $05 // Total frame CRC 5 * %0xxxxxxx // d - Tag restrictions // Flag data length $01 $extheaderflagbytes = fread($fd, 1); $extheaderflags = fread($fd, $extheaderflagbytes); $id3_exthead_flags = BigEndian2Bin(substr($header, 5, 1)); $ThisFileInfo['id3v2']['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); $ThisFileInfo['id3v2']['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); if ($ThisFileInfo['id3v2']['exthead_flags']['CRC']) { $extheaderrawCRC = fread($fd, 5); $ThisFileInfo['id3v2']['exthead_flags']['CRC'] = BigEndian2Int($extheaderrawCRC, 1); } $ThisFileInfo['id3v2']['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); if ($ThisFileInfo['id3v2']['exthead_flags']['restrictions']) { // Restrictions %ppqrrstt $extheaderrawrestrictions = fread($fd, 1); $ThisFileInfo['id3v2']['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions $ThisFileInfo['id3v2']['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions } } // end extended header // Frames // All ID3v2 frames consists of one frame header followed by one or more // fields containing the actual information. The header is always 10 // bytes and laid out as follows: // // Frame ID $xx xx xx xx (four characters) // Size 4 * %0xxxxxxx // Flags $xx xx $sizeofframes = $ThisFileInfo['id3v2']['headerlength'] - ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); if (isset($ThisFileInfo['id3v2']['extheaderlength'])) { $sizeofframes -= $ThisFileInfo['id3v2']['extheaderlength']; } if (isset($ThisFileInfo['id3v2']['flags']['isfooter']) && $ThisFileInfo['id3v2']['flags']['isfooter']) { $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio } if ($sizeofframes > 0) { $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) if (isset($ThisFileInfo['id3v2']['flags']['unsynch']) && $ThisFileInfo['id3v2']['flags']['unsynch'] && $ThisFileInfo['id3v2']['majorversion'] <= 3) { $framedata = DeUnSynchronise($framedata); } // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead // of on tag level, making it easier to skip frames, increasing the streamability // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that // there exists an unsynchronised frame, while the new unsynchronisation flag in // the frame header [S:4.1.2] indicates unsynchronisation. $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header while (isset($framedata) && strlen($framedata) > 0) { // cycle through until no more frame data is left to parse if (strlen($framedata) < ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion'])) { // insufficient room left in ID3v2 header for actual data - must be padding $ThisFileInfo['id3v2']['padding']['start'] = $framedataoffset; $ThisFileInfo['id3v2']['padding']['length'] = strlen($framedata); $ThisFileInfo['id3v2']['padding']['valid'] = true; for ($i = 0; $i < $ThisFileInfo['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $ThisFileInfo['id3v2']['padding']['valid'] = false; $ThisFileInfo['id3v2']['padding']['errorpos'] = $ThisFileInfo['id3v2']['padding']['start'] + $i; $ThisFileInfo['warning'] .= "\n" . 'Invalid ID3v2 padding found at offset ' . $ThisFileInfo['id3v2']['padding']['errorpos']; break; } } break; // skip rest of ID3v2 header } if ($ThisFileInfo['id3v2']['majorversion'] == 2) { // Frame ID $xx xx xx (three characters) // Size $xx xx xx (24-bit integer) // Flags $xx xx $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header $framedata = substr($framedata, 6); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 3); $frame_size = BigEndian2Int(substr($frame_header, 3, 3), 0); $frame_flags = ''; // not used for anything, just to avoid E_NOTICEs } elseif ($ThisFileInfo['id3v2']['majorversion'] > 2) { // Frame ID $xx xx xx xx (four characters) // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) // Flags $xx xx $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header $framedata = substr($framedata, 10); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 4); if ($ThisFileInfo['id3v2']['majorversion'] == 3) { $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } else { // ID3v2.4+ $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) } if ($frame_size < strlen($framedata) + 4) { $nextFrameID = substr($framedata, $frame_size, 4); if (IsValidID3v2FrameName($nextFrameID, $ThisFileInfo['id3v2']['majorversion'])) { // next frame is OK } elseif ($frame_name == chr(0) . 'MP3' || $frame_name == chr(0) . chr(0) . 'MP' || $frame_name == ' MP3' || $frame_name == 'MP3e') { // MP3ext known broken frames - "ok" for the purposes of this test } elseif ($ThisFileInfo['id3v2']['majorversion'] == 4 && IsValidID3v2FrameName(substr($framedata, BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3)) { $ThisFileInfo['warning'] .= "\n" . 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of Helium2 (www.helium2.com) is a known culprit of this. Tag has been parsed as ID3v2.3'; $ThisFileInfo['id3v2']['majorversion'] = 3; $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } if ($ThisFileInfo['id3v2']['majorversion'] == 2 && $frame_name == chr(0) . chr(0) . chr(0) || $frame_name == chr(0) . chr(0) . chr(0) . chr(0)) { // padding encountered $ThisFileInfo['id3v2']['padding']['start'] = $framedataoffset; $ThisFileInfo['id3v2']['padding']['length'] = strlen($framedata); $ThisFileInfo['id3v2']['padding']['valid'] = true; for ($i = 0; $i < $ThisFileInfo['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $ThisFileInfo['id3v2']['padding']['valid'] = false; $ThisFileInfo['id3v2']['padding']['errorpos'] = $ThisFileInfo['id3v2']['padding']['start'] + $i; $ThisFileInfo['warning'] .= "\n" . 'Invalid ID3v2 padding found at offset ' . $ThisFileInfo['id3v2']['padding']['errorpos']; break; } } break; // skip rest of ID3v2 header } if ($frame_name == 'COM ') { $ThisFileInfo['warning'] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; $frame_name = 'COMM'; } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $ThisFileInfo['id3v2']['majorversion'])) { $ThisFileInfo['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); $ThisFileInfo['id3v2']["{$frame_name}"]['datalength'] = CastAsInt($frame_size); $ThisFileInfo['id3v2']["{$frame_name}"]['dataoffset'] = $framedataoffset; $framedata = substr($framedata, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $ThisFileInfo); } else { // invalid frame length or FrameID if ($frame_size <= strlen($framedata)) { if (IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $ThisFileInfo['id3v2']['majorversion'])) { // next frame is valid, just skip the current frame $framedata = substr($framedata, $frame_size); $InvalidFrameMessageType = 'warning'; $InvalidFrameMessageText = ' Next frame is valid, skipping current frame.'; } else { // next frame is invalid too, abort processing unset($framedata); $InvalidFrameMessageType = 'error'; $InvalidFrameMessageText = ' Next frame is also invalid, aborting processing.'; } } elseif ($frame_size == strlen($framedata)) { // this is the last frame, just skip $InvalidFrameMessageType = 'warning'; $InvalidFrameMessageText = ' This was the last frame.'; } else { // next frame is invalid too, abort processing unset($framedata); $InvalidFrameMessageType = 'error'; $InvalidFrameMessageText = ' Invalid frame size, aborting.'; } if (!IsValidID3v2FrameName($frame_name, $ThisFileInfo['id3v2']['majorversion'])) { switch ($frame_name) { case chr(0) . chr(0) . 'MP': case chr(0) . 'MP3': case ' MP3': case 'MP3e': case chr(0) . 'MP': case ' MP': case 'MP3': $InvalidFrameMessageType = 'warning'; $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; break; default: $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $ThisFileInfo['id3v2']['majorversion'] . '))).'; break; } } elseif ($frame_size > strlen($framedata)) { $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag). (ERROR: $frame_size (' . $frame_size . ') > strlen($framedata) (' . strlen($framedata) . ')).'; } else { $ThisFileInfo["{$InvalidFrameMessageType}"] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $ThisFileInfo['id3v2']['majorversion'] . ' tag).'; } $ThisFileInfo["{$InvalidFrameMessageType}"] .= $InvalidFrameMessageText; } $framedataoffset += $frame_size + ID3v2HeaderLength($ThisFileInfo['id3v2']['majorversion']); } } // Footer // The footer is a copy of the header, but with a different identifier. // ID3v2 identifier "3DI" // ID3v2 version $04 00 // ID3v2 flags %abcd0000 // ID3v2 size 4 * %0xxxxxxx if (isset($ThisFileInfo['id3v2']['flags']['isfooter']) && $ThisFileInfo['id3v2']['flags']['isfooter']) { $footer = fread($fd, 10); if (substr($footer, 0, 3) == '3DI') { $ThisFileInfo['id3v2']['footer'] = true; $ThisFileInfo['id3v2']['majorversion_footer'] = ord(substr($footer, 3, 1)); $ThisFileInfo['id3v2']['minorversion_footer'] = ord(substr($footer, 4, 1)); } if ($ThisFileInfo['id3v2']['majorversion_footer'] <= 4) { $id3_flags = BigEndian2Bin(substr($footer, 5, 1)); $ThisFileInfo['id3v2']['flags']['unsynch_footer'] = substr($id3_flags, 0, 1); $ThisFileInfo['id3v2']['flags']['extfoot_footer'] = substr($id3_flags, 1, 1); $ThisFileInfo['id3v2']['flags']['experim_footer'] = substr($id3_flags, 2, 1); $ThisFileInfo['id3v2']['flags']['isfooter_footer'] = substr($id3_flags, 3, 1); $ThisFileInfo['id3v2']['footerlength'] = BigEndian2Int(substr($footer, 6, 4), 1); } } // end footer if (isset($ThisFileInfo['id3v2']['comments']['genre'])) { foreach ($ThisFileInfo['id3v2']['comments']['genre'] as $key => $value) { unset($ThisFileInfo['id3v2']['comments']['genre'][$key]); $ThisFileInfo['id3v2']['comments'] = array_merge_noclobber($ThisFileInfo['id3v2']['comments'], ParseID3v2GenreString($value)); } } if (isset($ThisFileInfo['id3v2']['comments']['track'])) { foreach ($ThisFileInfo['id3v2']['comments']['track'] as $key => $value) { if (strstr($value, '/')) { list($ThisFileInfo['id3v2']['comments']['track'][$key], $ThisFileInfo['id3v2']['comments']['totaltracks'][$key]) = explode('/', $ThisFileInfo['id3v2']['comments']['track'][$key]); } // Convert track number to integer (ID3v2 track numbers could be returned as a // string ('03' for example) - this will ensure all track numbers are integers $ThisFileInfo['id3v2']['comments']['track'][$key] = intval($ThisFileInfo['id3v2']['comments']['track'][$key]); } } return true; }
function getMPEGHeaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'mpeg'; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])); $MPEGstreamDataLength = strlen($MPEGstreamData); $foundVideo = true; $VideoChunkOffset = 0; while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== MPEG_VIDEO_SEQUENCE_HEADER) { if ($VideoChunkOffset >= $MPEGstreamDataLength) { $foundVideo = false; break 2; } } if ($foundVideo) { // Start code 32 bits // horizontal frame size 12 bits // vertical frame size 12 bits // pixel aspect ratio 4 bits // frame rate 4 bits // bitrate 18 bits // marker bit 1 bit // VBV buffer size 10 bits // constrained parameter flag 1 bit // intra quant. matrix flag 1 bit // intra quant. matrix values 512 bits (present if matrix flag == 1) // non-intra quant. matrix flag 1 bit // non-intra quant. matrix values 512 bits (present if matrix flag == 1) $ThisFileInfo['video']['dataformat'] = 'mpeg'; // I don't know how to differentiate between MPEG-1 and MPEG-2 video stream // Any information appreciated: info@getid3.org //$ThisFileInfo['video']['codec'] = 'MPEG-1'; //$ThisFileInfo['video']['codec'] = 'MPEG-2'; $ThisFileInfo['video']['codec'] = 'MPEG'; $VideoChunkOffset += strlen(MPEG_VIDEO_SEQUENCE_HEADER) - 1; $FrameSizeDWORD = BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3)); $VideoChunkOffset += 3; $AspectRatioFrameRateDWORD = BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1)); $VideoChunkOffset += 1; $assortedinformation = BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4)); $VideoChunkOffset += 4; $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xfff000) >> 12; // 12 bits for horizontal frame size $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = $FrameSizeDWORD & 0xfff; // 12 bits for vertical frame size $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xf0) >> 4; $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = $AspectRatioFrameRateDWORD & 0xf; $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); $ThisFileInfo['mpeg']['video']['frame_rate'] = MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = Bin2Dec(substr($assortedinformation, 0, 18)); $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = Bin2Dec(substr($assortedinformation, 18, 1)); $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = Bin2Dec(substr($assortedinformation, 19, 10)); $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = Bin2Dec(substr($assortedinformation, 29, 1)); $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = Bin2Dec(substr($assortedinformation, 30, 1)); if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3ffff) { // 18 set bits $ThisFileInfo['warning'] .= "\n" . 'This version of getID3() [' . GETID3VERSION . '] cannot determine average bitrate of VBR MPEG video files'; $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr'; } else { $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; } $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; $ThisFileInfo['video']['lossless'] = false; $ThisFileInfo['video']['bits_per_sample'] = 24; } else { $ThisFileInfo['error'] .= "\n" . 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; } $AudioChunkOffset = 0; while (true) { while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== MPEG_AUDIO_START) { if ($AudioChunkOffset >= $MPEGstreamDataLength) { break 2; } } require_once GETID3_INCLUDEPATH . 'getid3.mp3.php'; for ($i = 0; $i <= 2; $i++) { // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it 9 bytes, some 10 bytes after // I have no idea why or what the difference is, so this is a stupid hack. // If anybody has any better idea of what's going on, please let me know - info@getid3.org $dummy = $ThisFileInfo; if (decodeMPEGaudioHeader($fd, $AudioChunkOffset + 3 + 8 + $i, $dummy, false)) { $ThisFileInfo = $dummy; $ThisFileInfo['audio']['bits_per_sample'] = 16; $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; $ThisFileInfo['audio']['lossless'] = false; break 2; } } } return true; }
function FixedPoint16_16($rawdata) { return BigEndian2Int(substr($rawdata, 0, 2)) + (double) (BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); }
function getRealHeaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'real'; $ThisFileInfo['bitrate'] = 0; $ThisFileInfo['playtime_seconds'] = 0; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $ChunkCounter = 0; while (ftell($fd) < $ThisFileInfo['avdataend']) { $ChunkData = fread($fd, 8); $ChunkName = substr($ChunkData, 0, 4); $ChunkSize = BigEndian2Int(substr($ChunkData, 4, 4)); $ThisFileInfo['real']['chunks'][$ChunkCounter]['name'] = $ChunkName; $ThisFileInfo['real']['chunks'][$ChunkCounter]['offset'] = ftell($fd) - 8; $ThisFileInfo['real']['chunks'][$ChunkCounter]['length'] = $ChunkSize; $ChunkData .= fread($fd, $ChunkSize - 8); $offset = 8; switch ($ChunkName) { case '.RMF': // RealMedia File Header $ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; switch ($ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version']) { case 0: $ThisFileInfo['real']['chunks'][$ChunkCounter]['file_version'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['headers_count'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; break; default: //$ThisFileInfo['warning'] .= "\n".'Expected .RMF-object_version to be "0", actual value is "'.$ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'].'" (should not be a problem)'; break; } break; case 'PROP': // Properties Header $ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] == 0) { $ThisFileInfo['real']['chunks'][$ChunkCounter]['max_bit_rate'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_bit_rate'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['max_packet_size'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_packet_size'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['num_packets'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['preroll'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['index_offset'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['data_offset'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['num_streams'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['flags_raw'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] / 1000; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] > 0) { $ThisFileInfo['bitrate'] += $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_bit_rate']; } $ThisFileInfo['real']['chunks'][$ChunkCounter]['flags']['save_enabled'] = (bool) ($ThisFileInfo['real']['chunks'][$ChunkCounter]['flags_raw'] & 0x1); $ThisFileInfo['real']['chunks'][$ChunkCounter]['flags']['perfect_play'] = (bool) ($ThisFileInfo['real']['chunks'][$ChunkCounter]['flags_raw'] & 0x2); $ThisFileInfo['real']['chunks'][$ChunkCounter]['flags']['live_broadcast'] = (bool) ($ThisFileInfo['real']['chunks'][$ChunkCounter]['flags_raw'] & 0x4); } break; case 'MDPR': // Media Properties Header $ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] == 0) { $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_number'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['max_bit_rate'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_bit_rate'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['max_packet_size'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_packet_size'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['start_time'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['preroll'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_name_size'] = BigEndian2Int(substr($ChunkData, $offset, 1)); $offset += 1; $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_name'] = substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_name_size']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_name_size']; $ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type_size'] = BigEndian2Int(substr($ChunkData, $offset, 1)); $offset += 1; $ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type'] = substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type_size']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type_size']; $ThisFileInfo['real']['chunks'][$ChunkCounter]['type_specific_len'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['type_specific_data'] = substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['type_specific_len']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['type_specific_len']; if (empty($ThisFileInfo['playtime_seconds'])) { $ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] + $ThisFileInfo['real']['chunks'][$ChunkCounter]['start_time']) / 1000); } if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['duration'] > 0) { if (strstr($ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type'], 'audio')) { $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_bit_rate']; $ThisFileInfo['audio']['dataformat'] = 'real'; } elseif (strstr($ThisFileInfo['real']['chunks'][$ChunkCounter]['mime_type'], 'video')) { $ThisFileInfo['video']['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $ThisFileInfo['real']['chunks'][$ChunkCounter]['avg_bit_rate']; $ThisFileInfo['video']['dataformat'] = 'real'; } $ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0); } } break; case 'CONT': // Content Description Header $ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] == 0) { $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['title_len'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['title'] = (string) substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['title_len']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['title_len']; $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['artist_len'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['artist'] = (string) substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['artist_len']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['artist_len']; $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['copyright_len'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['copyright'] = (string) substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['copyright_len']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['copyright_len']; $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['comment_len'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['comment'] = (string) substr($ChunkData, $offset, $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['comment_len']); $offset += $ThisFileInfo['real']['chunks'][$ChunkCounter]['raw']['comment_len']; $commentkeystocopy = array('title' => 'title', 'artist' => 'artist', 'comment' => 'comment'); foreach ($commentkeystocopy as $key => $val) { if ($ThisFileInfo['real']['chunks'][$ChunkCounter]["{$key}"]) { $ThisFileInfo['real']['comments']["{$val}"] = $ThisFileInfo['real']['chunks'][$ChunkCounter]["{$key}"]; } } // RealMedia tags have highest priority if (!empty($ThisFileInfo['real']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['real']['comments'], $ThisFileInfo, true, true, true); } // add tag to array of tags $ThisFileInfo['tags'][] = 'real'; } break; case 'DATA': // Data Chunk Header // do nothing break; case 'INDX': // Index Section Header $ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['object_version'] == 0) { $ThisFileInfo['real']['chunks'][$ChunkCounter]['num_indices'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; $ThisFileInfo['real']['chunks'][$ChunkCounter]['stream_number'] = BigEndian2Int(substr($ChunkData, $offset, 2)); $offset += 2; $ThisFileInfo['real']['chunks'][$ChunkCounter]['next_index_header'] = BigEndian2Int(substr($ChunkData, $offset, 4)); $offset += 4; if ($ThisFileInfo['real']['chunks'][$ChunkCounter]['next_index_header'] == 0) { // last index chunk found, ignore rest of file return true; } else { // non-last index chunk, seek to next index chunk (skipping actual index data) fseek($fd, $ThisFileInfo['real']['chunks'][$ChunkCounter]['next_index_header'], SEEK_SET); } } break; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled RealMedia chunk "' . $ChunkName . '" at offset ' . $ThisFileInfo['real']['chunks'][$ChunkCounter]['offset']; break; } $ChunkCounter++; } return true; }
function getLPACHeaderFilepointer(&$fd, &$ThisFileInfo) { fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $LPACheader = fread($fd, 14); if (substr($LPACheader, 0, 4) != 'LPAC') { $ThisFileInfo['error'] .= "\n" . 'Expected "LPAC" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . $StreamMarker . '"'; return false; } $ThisFileInfo['avdataoffset'] += 14; $ThisFileInfo['lpac']['file_version'] = BigEndian2Int(substr($LPACheader, 4, 1)); $ThisFileInfo['lpac']['raw']['audio_type'] = BigEndian2Int(substr($LPACheader, 5, 1)); $ThisFileInfo['lpac']['total_samples'] = BigEndian2Int(substr($LPACheader, 6, 4)); $ThisFileInfo['lpac']['raw']['parameters'] = BigEndian2Int(substr($LPACheader, 10, 4)); $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($ThisFileInfo['lpac']['raw']['audio_type'] & 0x40); $ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($ThisFileInfo['lpac']['raw']['audio_type'] & 0x4); $ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($ThisFileInfo['lpac']['raw']['audio_type'] & 0x2); $ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($ThisFileInfo['lpac']['raw']['audio_type'] & 0x1); if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) { $ThisFileInfo['warning'] .= "\n" . '24-bit and 16-bit flags cannot both be set'; } $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($ThisFileInfo['lpac']['raw']['parameters'] & 0x40000000); $ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($ThisFileInfo['lpac']['raw']['parameters'] & 0x8000000); $ThisFileInfo['lpac']['block_length'] = pow(2, ($ThisFileInfo['lpac']['raw']['parameters'] & 0x7000000) >> 24) * 256; $ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($ThisFileInfo['lpac']['raw']['parameters'] & 0x800000); $ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($ThisFileInfo['lpac']['raw']['parameters'] & 0x400000); $ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['lpac']['raw']['parameters'] & 0x40000); $ThisFileInfo['lpac']['flags']['quantization'] = ($ThisFileInfo['lpac']['raw']['parameters'] & 0x1f00) >> 8; $ThisFileInfo['lpac']['flags']['max_prediction_order'] = $ThisFileInfo['lpac']['raw']['parameters'] & 0x3f; if ($ThisFileInfo['lpac']['flags']['fast_compress'] && $ThisFileInfo['lpac']['flags']['max_prediction_order'] != 3) { $ThisFileInfo['warning'] .= "\n" . 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $ThisFileInfo['lpac']['flags']['max_prediction_order'] . '"'; } switch ($ThisFileInfo['lpac']['file_version']) { case 6: if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) { $ThisFileInfo['warning'] .= "\n" . 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; } if ($ThisFileInfo['lpac']['flags']['quantization'] != 20) { $ThisFileInfo['warning'] .= "\n" . 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $ThisFileInfo['lpac']['flags']['Q']; } break; default: $ThisFileInfo['warning'] .= "\n" . 'This version of getID3() only supports LPAC file format version 6, this file is version ' . $ThisFileInfo['lpac']['file_version'] . ' - please report to info@getid3.org'; break; } require_once GETID3_INCLUDEPATH . 'getid3.riff.php'; $dummy = array('avdataoffset' => $ThisFileInfo['avdataoffset'], 'avdataend' => $ThisFileInfo['avdataend'], 'filesize' => $ThisFileInfo['filesize'], 'error' => $ThisFileInfo['error'], 'warning' => $ThisFileInfo['warning'], 'tags' => $ThisFileInfo['tags'], 'comments' => $ThisFileInfo['comments']); getRIFFHeaderFilepointer($fd, $dummy); $ThisFileInfo['avdataoffset'] = $dummy['avdataoffset']; $ThisFileInfo['RIFF'] = $dummy['RIFF']; $ThisFileInfo['error'] = $dummy['error']; $ThisFileInfo['warning'] = $dummy['warning']; $ThisFileInfo['comments'] = $dummy['comments']; $ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate']; $ThisFileInfo['fileformat'] = 'lpac'; $ThisFileInfo['audio']['dataformat'] = 'lpac'; $ThisFileInfo['audio']['lossless'] = true; $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; $ThisFileInfo['audio']['channels'] = $ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1; if ($ThisFileInfo['lpac']['flags']['24_bit']) { $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['RIFF']['audio'][0]['bits_per_sample']; } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) { $ThisFileInfo['audio']['bits_per_sample'] = 16; } else { $ThisFileInfo['audio']['bits_per_sample'] = 8; } $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; return true; }
function getPNGHeaderFilepointer(&$fd, &$ThisFileInfo) { $ThisFileInfo['fileformat'] = 'png'; $ThisFileInfo['video']['dataformat'] = 'png'; $ThisFileInfo['video']['lossless'] = false; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); $PNGfiledata = fread($fd, FREAD_BUFFER_SIZE); $offset = 0; $PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A $offset += 8; if ($PNGidentifier != chr(0x89) . chr(0x50) . chr(0x4e) . chr(0x47) . chr(0xd) . chr(0xa) . chr(0x1a) . chr(0xa)) { $ThisFileInfo['error'] .= "\n" . 'First 8 bytes of file (' . PrintHexBytes($PNGidentifier) . ') did not match expected PNG identifier'; unset($ThisFileInfo['fileformat']); return false; } while (ftell($fd) - (strlen($PNGfiledata) - $offset) < $ThisFileInfo['filesize']) { $chunk['data_length'] = BigEndian2Int(substr($PNGfiledata, $offset, 4)); $offset += 4; while (strlen($PNGfiledata) - $offset < $chunk['data_length'] + 4 && ftell($fd) < $ThisFileInfo['filesize']) { $PNGfiledata .= fread($fd, FREAD_BUFFER_SIZE); } $chunk['type_text'] = substr($PNGfiledata, $offset, 4); $offset += 4; $chunk['type_raw'] = BigEndian2Int($chunk['type_text']); $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']); $offset += $chunk['data_length']; $chunk['crc'] = BigEndian2Int(substr($PNGfiledata, $offset, 4)); $offset += 4; $chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000); $chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x200000); $chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x2000); $chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x20); switch ($chunk['type_text']) { case 'IHDR': // Image Header $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['width'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4)); $ThisFileInfo['png'][$chunk['type_text']]['height'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4)); $ThisFileInfo['png'][$chunk['type_text']]['raw']['bit_depth'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 1)); $ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 9, 1)); $ThisFileInfo['png'][$chunk['type_text']]['raw']['compression_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 10, 1)); $ThisFileInfo['png'][$chunk['type_text']]['raw']['filter_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 11, 1)); $ThisFileInfo['png'][$chunk['type_text']]['raw']['interlace_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 12, 1)); $ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['raw']['compression_method']); $ThisFileInfo['png'][$chunk['type_text']]['color_type']['palette'] = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x1); $ThisFileInfo['png'][$chunk['type_text']]['color_type']['true_color'] = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x2); $ThisFileInfo['png'][$chunk['type_text']]['color_type']['alpha'] = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x4); $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['png'][$chunk['type_text']]['width']; $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['png'][$chunk['type_text']]['height']; $ThisFileInfo['video']['bits_per_sample'] = IHDRcalculateBitsPerSample($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'], $ThisFileInfo['png'][$chunk['type_text']]['raw']['bit_depth']); break; case 'PLTE': // Palette $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $paletteoffset = 0; for ($i = 0; $i <= 255; $i++) { //$ThisFileInfo['png'][$chunk['type_text']]['red'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); //$ThisFileInfo['png'][$chunk['type_text']]['green'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); //$ThisFileInfo['png'][$chunk['type_text']]['blue'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); $red = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); $green = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); $blue = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1)); $ThisFileInfo['png'][$chunk['type_text']][$i] = $red << 16 | $green << 8 | $blue; } break; case 'tRNS': // Transparency $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) { case 0: $ThisFileInfo['png'][$chunk['type_text']]['transparent_color_gray'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2)); break; case 2: $ThisFileInfo['png'][$chunk['type_text']]['transparent_color_red'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2)); $ThisFileInfo['png'][$chunk['type_text']]['transparent_color_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2)); $ThisFileInfo['png'][$chunk['type_text']]['transparent_color_blue'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2)); break; case 3: for ($i = 0; $i < strlen($ThisFileInfo['png'][$chunk['type_text']]['header']['data']); $i++) { $ThisFileInfo['png'][$chunk['type_text']]['palette_opacity'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $i, 1)); } break; case 4: case 6: $ThisFileInfo['error'] .= "\n" . 'Invalid color_type in tRNS chunk: ' . $ThisFileInfo['png']['IHDR']['raw']['color_type']; default: $ThisFileInfo['warning'] .= "\n" . 'Unhandled color_type in tRNS chunk: ' . $ThisFileInfo['png']['IHDR']['raw']['color_type']; break; } break; case 'gAMA': // Image Gamma $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['gamma'] = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']) / 100000; break; case 'cHRM': // Primary Chromaticities $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['white_x'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['white_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['red_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['red_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 12, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['green_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 16, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['green_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 20, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['blue_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 24, 4)) / 100000; $ThisFileInfo['png'][$chunk['type_text']]['blue_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 28, 4)) / 100000; break; case 'sRGB': // Standard RGB Color Space $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['reindering_intent'] = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']); $ThisFileInfo['png'][$chunk['type_text']]['reindering_intent_text'] = PNGsRGBintentLookup($ThisFileInfo['png'][$chunk['type_text']]['reindering_intent']); break; case 'iCCP': // Embedded ICC Profile $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($profilename, $compressiondata) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['profile_name'] = $profilename; $ThisFileInfo['png'][$chunk['type_text']]['compression_method'] = BigEndian2Int(substr($compressiondata, 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['compression_profile'] = substr($compressiondata, 1); $ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']); break; case 'tEXt': // Textual Data $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($keyword, $text) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['keyword'] = $keyword; $ThisFileInfo['png'][$chunk['type_text']]['text'] = $text; $ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text']; break; case 'zTXt': // Compressed Textual Data $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($keyword, $otherdata) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['keyword'] = $keyword; $ThisFileInfo['png'][$chunk['type_text']]['compression_method'] = BigEndian2Int(substr($otherdata, 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['compressed_text'] = substr($otherdata, 1); $ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']); switch ($ThisFileInfo['png'][$chunk['type_text']]['compression_method']) { case 0: $ThisFileInfo['png'][$chunk['type_text']]['text'] = gzuncompress($ThisFileInfo['png'][$chunk['type_text']]['compressed_text']); break; default: // unknown compression method break; } if (isset($ThisFileInfo['png'][$chunk['type_text']]['text'])) { $ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text']; } break; case 'iTXt': // International Textual Data $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($keyword, $otherdata) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['keyword'] = $keyword; $ThisFileInfo['png'][$chunk['type_text']]['compression'] = (bool) BigEndian2Int(substr($otherdata, 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['compression_method'] = BigEndian2Int(substr($otherdata, 1, 1)); $ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']); list($languagetag, $translatedkeyword, $text) = explode(chr(0x0), substr($otherdata, 2), 3); $ThisFileInfo['png'][$chunk['type_text']]['language_tag'] = $languagetag; $ThisFileInfo['png'][$chunk['type_text']]['translated_keyword'] = utf8_decode($translatedkeyword); if ($ThisFileInfo['png'][$chunk['type_text']]['compression']) { switch ($ThisFileInfo['png'][$chunk['type_text']]['compression_method']) { case 0: $ThisFileInfo['png'][$chunk['type_text']]['text'] = utf8_decode(gzuncompress($text)); break; default: // unknown compression method break; } } else { $ThisFileInfo['png'][$chunk['type_text']]['text'] = utf8_decode($text); } if (isset($ThisFileInfo['png'][$chunk['type_text']]['text'])) { $ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text']; } break; case 'bKGD': // Background Color $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) { case 0: case 4: $ThisFileInfo['png'][$chunk['type_text']]['background_gray'] = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']); break; case 2: case 6: $ThisFileInfo['png'][$chunk['type_text']]['background_red'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth'])); $ThisFileInfo['png'][$chunk['type_text']]['background_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth'])); $ThisFileInfo['png'][$chunk['type_text']]['background_blue'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth'])); break; case 3: $ThisFileInfo['png'][$chunk['type_text']]['background_index'] = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']); break; default: break; } break; case 'pHYs': // Physical Pixel Dimensions $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['pixels_per_unit_x'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4)); $ThisFileInfo['png'][$chunk['type_text']]['pixels_per_unit_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4)); $ThisFileInfo['png'][$chunk['type_text']]['unit_specifier'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 1)); $ThisFileInfo['png'][$chunk['type_text']]['unit'] = PNGpHYsUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']); break; case 'sBIT': // Significant Bits $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) { case 0: $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_gray'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); break; case 2: case 3: $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_red'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_blue'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1)); break; case 4: $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_gray'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_alpha'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1)); break; case 6: $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_red'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_blue'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1)); $ThisFileInfo['png'][$chunk['type_text']]['significant_bits_alpha'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 3, 1)); break; default: break; } break; case 'sPLT': // Suggested Palette $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($palettename, $otherdata) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['palette_name'] = $palettename; $sPLToffset = 0; $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bits'] = BigEndian2Int(substr($otherdata, $sPLToffset, 1)); $sPLToffset += 1; $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'] = $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bits'] / 8; $paletteCounter = 0; while ($sPLToffset < strlen($otherdata)) { $ThisFileInfo['png'][$chunk['type_text']]['red'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'])); $sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']; $ThisFileInfo['png'][$chunk['type_text']]['green'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'])); $sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']; $ThisFileInfo['png'][$chunk['type_text']]['blue'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'])); $sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']; $ThisFileInfo['png'][$chunk['type_text']]['alpha'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'])); $sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']; $ThisFileInfo['png'][$chunk['type_text']]['frequency'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, 2)); $sPLToffset += 2; $paletteCounter++; } break; case 'hIST': // Palette Histogram $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $hISTcounter = 0; while ($hISTcounter < strlen($ThisFileInfo['png'][$chunk['type_text']]['header']['data'])) { $ThisFileInfo['png'][$chunk['type_text']][$hISTcounter] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $hISTcounter / 2, 2)); $hISTcounter += 2; } break; case 'tIME': // Image Last-Modification Time $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['year'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2)); $ThisFileInfo['png'][$chunk['type_text']]['month'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1)); $ThisFileInfo['png'][$chunk['type_text']]['day'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 3, 1)); $ThisFileInfo['png'][$chunk['type_text']]['hour'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 1)); $ThisFileInfo['png'][$chunk['type_text']]['minute'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 5, 1)); $ThisFileInfo['png'][$chunk['type_text']]['second'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 6, 1)); $ThisFileInfo['png'][$chunk['type_text']]['unix'] = gmmktime($ThisFileInfo['png'][$chunk['type_text']]['hour'], $ThisFileInfo['png'][$chunk['type_text']]['minute'], $ThisFileInfo['png'][$chunk['type_text']]['second'], $ThisFileInfo['png'][$chunk['type_text']]['month'], $ThisFileInfo['png'][$chunk['type_text']]['day'], $ThisFileInfo['png'][$chunk['type_text']]['year']); break; case 'oFFs': // Image Offset $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['position_x'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4), false, true); $ThisFileInfo['png'][$chunk['type_text']]['position_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4), false, true); $ThisFileInfo['png'][$chunk['type_text']]['unit_specifier'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 1)); $ThisFileInfo['png'][$chunk['type_text']]['unit'] = PNGoFFsUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']); break; case 'pCAL': // Calibration Of Pixel Values $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; list($calibrationname, $otherdata) = explode(chr(0x0), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2); $ThisFileInfo['png'][$chunk['type_text']]['calibration_name'] = $calibrationname; $pCALoffset = 0; $ThisFileInfo['png'][$chunk['type_text']]['original_zero'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 4), false, true); $pCALoffset += 4; $ThisFileInfo['png'][$chunk['type_text']]['original_max'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 4), false, true); $pCALoffset += 4; $ThisFileInfo['png'][$chunk['type_text']]['equation_type'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 1)); $pCALoffset += 1; $ThisFileInfo['png'][$chunk['type_text']]['equation_type_text'] = PNGpCALequationTypeLookup($ThisFileInfo['png'][$chunk['type_text']]['equation_type']); $ThisFileInfo['png'][$chunk['type_text']]['parameter_count'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 1)); $pCALoffset += 1; $ThisFileInfo['png'][$chunk['type_text']]['parameters'] = explode(chr(0x0), substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset)); break; case 'sCAL': // Physical Scale Of Image Subject $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']]['unit_specifier'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); $ThisFileInfo['png'][$chunk['type_text']]['unit'] = PNGsCALUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']); list($pixelwidth, $pixelheight) = explode(chr(0x0), substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1)); $ThisFileInfo['png'][$chunk['type_text']]['pixel_width'] = $pixelwidth; $ThisFileInfo['png'][$chunk['type_text']]['pixel_height'] = $pixelheight; break; case 'gIFg': // GIF Graphic Control Extension $gIFgCounter = 0; if (isset($ThisFileInfo['png'][$chunk['type_text']]) && is_array($ThisFileInfo['png'][$chunk['type_text']])) { $gIFgCounter = count($ThisFileInfo['png'][$chunk['type_text']]); } $ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['disposal_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1)); $ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['user_input_flag'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1)); $ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['delay_time'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 2)); break; case 'gIFx': // GIF Application Extension $gIFxCounter = 0; if (isset($ThisFileInfo['png'][$chunk['type_text']]) && is_array($ThisFileInfo['png'][$chunk['type_text']])) { $gIFxCounter = count($ThisFileInfo['png'][$chunk['type_text']]); } $ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['header'] = $chunk; $ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['application_identifier'] = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 8); $ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['authentication_code'] = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 3); $ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['application_data'] = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 11); break; case 'IDAT': // Image Data $idatinformationfieldindex = 0; if (isset($ThisFileInfo['png']['IDAT']) && is_array($ThisFileInfo['png']['IDAT'])) { $idatinformationfieldindex = count($ThisFileInfo['png']['IDAT']); } unset($chunk['data']); $ThisFileInfo['png'][$chunk['type_text']][$idatinformationfieldindex]['header'] = $chunk; break; case 'IEND': // Image Trailer $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; break; default: //unset($chunk['data']); $ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk; $ThisFileInfo['warning'] .= "\n" . 'Unhandled chunk type: ' . $chunk['type_text']; break; } } // PNG tags have highest priority if (!empty($ThisFileInfo['png']['comments'])) { CopyFormatCommentsToRootComments($ThisFileInfo['png']['comments'], $ThisFileInfo, true, true, true); // add tag to array of tags $ThisFileInfo['tags'][] = 'png'; } 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]) & 0x6) >> 1; // CC $MPEGrawHeader['protection'] = ord($Header4Bytes[1]) & 0x1; // D $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xf0) >> 4; // EEEE $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0xc) >> 2; // FF $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x2) >> 1; // G $MPEGrawHeader['private'] = ord($Header4Bytes[2]) & 0x1; // H $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xc0) >> 6; // II $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; // JJ $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x8) >> 3; // K $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x4) >> 2; // L $MPEGrawHeader['emphasis'] = ord($Header4Bytes[3]) & 0x3; // MM return $MPEGrawHeader; }
function getID3v2Filepointer($fd, &$MP3fileInfo) { // Overall tag structure: // +-----------------------------+ // | Header (10 bytes) | // +-----------------------------+ // | Extended Header | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Frames (variable length) | // +-----------------------------+ // | Padding | // | (variable length, OPTIONAL) | // +-----------------------------+ // | Footer (10 bytes, OPTIONAL) | // +-----------------------------+ // Header // ID3v2/file identifier "ID3" // ID3v2 version $04 00 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) // ID3v2 size 4 * %0xxxxxxx rewind($fd); $header = fread($fd, 10); if (substr($header, 0, 3) == 'ID3') { $MP3fileInfo['id3']['id3v2']['header'] = TRUE; $MP3fileInfo['id3']['id3v2']['majorversion'] = ord($header[3]); $MP3fileInfo['id3']['id3v2']['minorversion'] = ord($header[4]); } if (isset($MP3fileInfo['id3']['id3v2']['header']) && $MP3fileInfo['id3']['id3v2']['majorversion'] <= 4) { // this script probably won't correctly parse ID3v2.5.x and above. $id3_flags = BigEndian2Bin($header[5]); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { // %ab000000 in v2.2 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['compression'] = $id3_flags[1]; // b - Compression } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { // %abc00000 in v2.3 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $MP3fileInfo['id3']['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4) { // %abcd0000 in v2.4 $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] = $id3_flags[0]; // a - Unsynchronisation $MP3fileInfo['id3']['id3v2']['flags']['exthead'] = $id3_flags[1]; // b - Extended header $MP3fileInfo['id3']['id3v2']['flags']['experim'] = $id3_flags[2]; // c - Experimental indicator $MP3fileInfo['id3']['id3v2']['flags']['isfooter'] = $id3_flags[3]; // d - Footer present } } } $MP3fileInfo['id3']['id3v2']['headerlength'] = BigEndian2Int(substr($header, 6, 4), 1) + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); // Extended Header if (isset($MP3fileInfo['id3']['id3v2']['flags']['exthead']) && $MP3fileInfo['id3']['id3v2']['flags']['exthead']) { // Extended header size 4 * %0xxxxxxx // Number of flag bytes $01 // Extended Flags $xx // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. $extheader = fread($fd, 4); $MP3fileInfo['id3']['id3v2']['extheaderlength'] = BigEndian2Int($extheader, 1); // The extended flags field, with its size described by 'number of flag bytes', is defined as: // %0bcd0000 // b - Tag is an update // Flag data length $00 // c - CRC data present // Flag data length $05 // Total frame CRC 5 * %0xxxxxxx // d - Tag restrictions // Flag data length $01 $extheaderflagbytes = fread($fd, 1); $extheaderflags = fread($fd, $extheaderflagbytes); $id3_exthead_flags = BigEndian2Bin(substr($header, 5, 1)); $MP3fileInfo['id3']['id3v2']['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); $MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); if ($MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC']) { $extheaderrawCRC = fread($fd, 5); $MP3fileInfo['id3']['id3v2']['exthead_flags']['CRC'] = BigEndian2Int($extheaderrawCRC, 1); } $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); if ($MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions']) { // Restrictions %ppqrrstt $extheaderrawrestrictions = fread($fd, 1); $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions $MP3fileInfo['id3']['id3v2']['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions } } // end extended header // Frames // All ID3v2 frames consists of one frame header followed by one or more // fields containing the actual information. The header is always 10 // bytes and laid out as follows: // // Frame ID $xx xx xx xx (four characters) // Size 4 * %0xxxxxxx // Flags $xx xx $sizeofframes = $MP3fileInfo['id3']['id3v2']['headerlength'] - ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); if (isset($MP3fileInfo['id3']['id3v2']['extheaderlength'])) { $sizeofframes -= $MP3fileInfo['id3']['id3v2']['extheaderlength']; } if (isset($MP3fileInfo['id3']['id3v2']['flags']['isfooter']) && $MP3fileInfo['id3']['id3v2']['flags']['isfooter']) { $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio } if ($sizeofframes > 0) { $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) if (isset($MP3fileInfo['id3']['id3v2']['flags']['unsynch']) && $MP3fileInfo['id3']['id3v2']['flags']['unsynch'] && $MP3fileInfo['id3']['id3v2']['majorversion'] <= 3) { $framedata = DeUnSynchronise($framedata); } // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead // of on tag level, making it easier to skip frames, increasing the streamability // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that // there exists an unsynchronised frame, while the new unsynchronisation flag in // the frame header [S:4.1.2] indicates unsynchronisation. include_once GETID3_INCLUDEPATH . 'getid3.frames.php'; // ID3v2FrameProcessing() $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header while (isset($framedata) && strlen($framedata) > 0) { // cycle through until no more frame data is left to parse if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { // Frame ID $xx xx xx (three characters) // Size $xx xx xx (24-bit integer) // Flags $xx xx $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header $framedata = substr($framedata, 6); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 3); $frame_size = BigEndian2Int(substr($frame_header, 3, 3), 0); $frame_flags = ''; // not used for anything, just to avoid E_NOTICEs } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] > 2) { // Frame ID $xx xx xx xx (four characters) // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) // Flags $xx xx $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header $framedata = substr($framedata, 10); // and leave the rest in $framedata $frame_name = substr($frame_header, 0, 4); if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 3) { $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } else { // ID3v2.4+ $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) } if ($frame_size < strlen($framedata) + 4) { $nextFrameID = substr($framedata, $frame_size, 4); if (IsValidID3v2FrameName($nextFrameID, $MP3fileInfo['id3']['id3v2']['majorversion'])) { // next frame is OK } else { if ($frame_name == chr(0) . 'MP3' || $frame_name == ' MP3' || $frame_name == 'MP3e') { // MP3ext known broken frames - "ok" for the purposes of this test } else { if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 4 && IsValidID3v2FrameName(substr($framedata, BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3)) { $MP3fileInfo['error'] .= "\n" . 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of Helium2 (www.helium2.com) is a known culprit of this. Tag has been parsed as ID3v2.3'; $MP3fileInfo['id3']['id3v2']['majorversion'] = 3; $frame_size = BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer } } } } $frame_flags = BigEndian2Bin(substr($frame_header, 8, 2)); } } if ($frame_name == chr(0) . chr(0) . chr(0) . chr(0)) { // padding encountered // $MP3fileInfo['id3']['id3v2']['padding']['start'] = $MP3fileInfo['id3']['id3v2']['headerlength'] - strlen($framedata); $MP3fileInfo['id3']['id3v2']['padding']['start'] = $framedataoffset; $MP3fileInfo['id3']['id3v2']['padding']['length'] = strlen($framedata); $MP3fileInfo['id3']['id3v2']['padding']['valid'] = TRUE; for ($i = 0; $i < $MP3fileInfo['id3']['id3v2']['padding']['length']; $i++) { if (substr($framedata, $i, 1) != chr(0)) { $MP3fileInfo['id3']['id3v2']['padding']['valid'] = FALSE; $MP3fileInfo['id3']['id3v2']['padding']['errorpos'] = $MP3fileInfo['id3']['id3v2']['padding']['start'] + $i; break; } } break; // skip rest of ID3v2 header } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName($frame_name, $MP3fileInfo['id3']['id3v2']['majorversion'])) { $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['data'] = substr($framedata, 0, $frame_size); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['datalength'] = CastAsInt($frame_size); $MP3fileInfo['id3']['id3v2']["{$frame_name}"]['dataoffset'] = $framedataoffset; $framedata = substr($framedata, $frame_size); // in getid3.frames.php - this function does all the FrameID-level parsing ID3v2FrameProcessing($frame_name, $frame_flags, $MP3fileInfo); $framedataoffset += $frame_size + ID3v2HeaderLength($MP3fileInfo['id3']['id3v2']['majorversion']); } else { // invalid frame length or FrameID $MP3fileInfo['error'] .= "\n" . 'error parsing "' . $frame_name . '" (' . $framedataoffset . ' bytes into the ID3v2.' . $MP3fileInfo['id3']['id3v2']['majorversion'] . ' tag).'; if ($frame_size > strlen($framedata)) { $MP3fileInfo['error'] .= ' (ERROR: $frame_size (' . $frame_size . ') > strlen($framedata) (' . strlen($framedata) . ')).'; } if (!IsValidID3v2FrameName($frame_name, $MP3fileInfo['id3']['id3v2']['majorversion'])) { $MP3fileInfo['error'] .= ' (ERROR: !IsValidID3v2FrameName("' . str_replace(chr(0), ' ', $frame_name) . '", ' . $MP3fileInfo['id3']['id3v2']['majorversion'] . '))).'; if ($frame_name == chr(0) . 'MP3' || $frame_name == ' MP3' || $frame_name == 'MP3e') { $MP3fileInfo['error'] .= ' [Note: this particular error has been known to happen with tags edited by "MP3ext V3.3.17(unicode)"]'; } else { if ($frame_name == 'COM ') { $MP3fileInfo['error'] .= ' [Note: this particular error has been known to happen with tags edited by "iTunes X v2.0.3"]'; } } } if ($frame_size <= strlen($framedata) && IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $MP3fileInfo['id3']['id3v2']['majorversion'])) { // next frame is valid, just skip the current frame $framedata = substr($framedata, $frame_size); } else { // next frame is invalid too, abort processing unset($framedata); } } } } // Footer // The footer is a copy of the header, but with a different identifier. // ID3v2 identifier "3DI" // ID3v2 version $04 00 // ID3v2 flags %abcd0000 // ID3v2 size 4 * %0xxxxxxx if (isset($MP3fileInfo['id3']['id3v2']['flags']['isfooter']) && $MP3fileInfo['id3']['id3v2']['flags']['isfooter']) { $footer = fread($fd, 10); if (substr($footer, 0, 3) == '3DI') { $MP3fileInfo['id3']['id3v2']['footer'] = true; $MP3fileInfo['id3']['id3v2']['majorversion_footer'] = ord(substr($footer, 3, 1)); $MP3fileInfo['id3']['id3v2']['minorversion_footer'] = ord(substr($footer, 4, 1)); } if ($MP3fileInfo['id3']['id3v2']['majorversion_footer'] <= 4) { $id3_flags = BigEndian2Bin(substr($footer, 5, 1)); $MP3fileInfo['id3']['id3v2']['flags']['unsynch_footer'] = substr($id3_flags, 0, 1); $MP3fileInfo['id3']['id3v2']['flags']['extfoot_footer'] = substr($id3_flags, 1, 1); $MP3fileInfo['id3']['id3v2']['flags']['experim_footer'] = substr($id3_flags, 2, 1); $MP3fileInfo['id3']['id3v2']['flags']['isfooter_footer'] = substr($id3_flags, 3, 1); $MP3fileInfo['id3']['id3v2']['footerlength'] = BigEndian2Int(substr($footer, 6, 4), 1); } } // end footer // Translate most common ID3v2 FrameIDs to easier-to-understand names if ($MP3fileInfo['id3']['id3v2']['majorversion'] == 2) { if (isset($MP3fileInfo['id3']['id3v2']['TT2'])) { $MP3fileInfo['id3']['id3v2']['title'] = $MP3fileInfo['id3']['id3v2']['TT2']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TP1'])) { $MP3fileInfo['id3']['id3v2']['artist'] = $MP3fileInfo['id3']['id3v2']['TP1']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TAL'])) { $MP3fileInfo['id3']['id3v2']['album'] = $MP3fileInfo['id3']['id3v2']['TAL']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TYE'])) { $MP3fileInfo['id3']['id3v2']['year'] = $MP3fileInfo['id3']['id3v2']['TYE']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TRK'])) { $MP3fileInfo['id3']['id3v2']['track'] = $MP3fileInfo['id3']['id3v2']['TRK']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TCO'])) { $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['TCO']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['COM'][0]['asciidata'])) { $MP3fileInfo['id3']['id3v2']['comment'] = $MP3fileInfo['id3']['id3v2']['COM'][0]['asciidata']; } } else { // $MP3fileInfo['id3']['id3v2']['majorversion'] > 2 if (isset($MP3fileInfo['id3']['id3v2']['TIT2'])) { $MP3fileInfo['id3']['id3v2']['title'] = $MP3fileInfo['id3']['id3v2']['TIT2']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TPE1'])) { $MP3fileInfo['id3']['id3v2']['artist'] = $MP3fileInfo['id3']['id3v2']['TPE1']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TALB'])) { $MP3fileInfo['id3']['id3v2']['album'] = $MP3fileInfo['id3']['id3v2']['TALB']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TYER'])) { $MP3fileInfo['id3']['id3v2']['year'] = $MP3fileInfo['id3']['id3v2']['TYER']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TRCK'])) { $MP3fileInfo['id3']['id3v2']['track'] = $MP3fileInfo['id3']['id3v2']['TRCK']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['TCON'])) { $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['TCON']['asciidata']; } if (isset($MP3fileInfo['id3']['id3v2']['COMM'][0]['asciidata'])) { $MP3fileInfo['id3']['id3v2']['comment'] = $MP3fileInfo['id3']['id3v2']['COMM'][0]['asciidata']; } } if (isset($MP3fileInfo['id3']['id3v2']['genre'])) { $MP3fileInfo['id3']['id3v2']['genrelist'] = ParseID3v2GenreString($MP3fileInfo['id3']['id3v2']['genre']); if ($MP3fileInfo['id3']['id3v2']['genrelist']['genreid'][0] !== '') { $MP3fileInfo['id3']['id3v2']['genreid'] = $MP3fileInfo['id3']['id3v2']['genrelist']['genreid'][0]; } $MP3fileInfo['id3']['id3v2']['genre'] = $MP3fileInfo['id3']['id3v2']['genrelist']['genre'][0]; } if (isset($MP3fileInfo['id3']['id3v2']['track']) && strpos($MP3fileInfo['id3']['id3v2']['track'], '/') !== FALSE) { $tracktotaltracks = explode('/', $MP3fileInfo['id3']['id3v2']['track']); $MP3fileInfo['id3']['id3v2']['track'] = $tracktotaltracks[0]; $MP3fileInfo['id3']['id3v2']['totaltracks'] = $tracktotaltracks[1]; } } else { // MajorVersion is > 4, or no ID3v2 header present if (isset($MP3fileInfo['id3']['id3v2']['header'])) { // MajorVersion is > 4 $MP3fileInfo['error'] .= "\n" . 'this script only parses up to ID3v2.4.x - this tag is ID3v2.' . $MP3fileInfo['id3']['id3v2']['majorversion'] . '.' . $MP3fileInfo['id3']['id3v2']['minorversion']; } else { // no ID3v2 header present - this is fine, just don't process anything. } } return TRUE; }