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