} fseek($fd, $id3v2dataoffset, SEEK_SET); $sizeofframes = $id3info['id3']['id3v2']['headerlength'] - ID3v2HeaderLength($id3info['id3']['id3v2']['majorversion']); if (isset($id3info['id3']['id3v2']['extheaderlength'])) { $sizeofframes -= $id3info['id3']['id3v2']['extheaderlength']; } if (isset($id3info['id3']['id3v2']['flags']['isfooter']) && $id3info['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($id3info['id3']['id3v2']['flags']['unsynch']) && $id3info['id3']['id3v2']['flags']['unsynch'] && $id3info['id3']['id3v2']['majorversion'] <= 3) { $framedata = DeUnSynchronise($framedata); } $framedata = substr($framedata, $frameoffset - 10); // minus length of 10-byte ID3v2 header if ($id3info['id3']['id3v2']['majorversion'] == 2) { $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 ($id3info['id3']['id3v2']['majorversion'] > 2) { $frame_header = substr($framedata, 0, 10);
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 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 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; }