Exemplo n.º 1
0
 private function ParseID3v2Frame(&$parsed_frame)
 {
     $getid3 = $this->getid3;
     $id3v2_major_version = $getid3->info['id3v2']['majorversion'];
     $frame_name_long = getid3_id3v2::FrameNameLongLookup($parsed_frame['frame_name']);
     if ($frame_name_long) {
         $parsed_frame['framenamelong'] = $frame_name_long;
     }
     $frame_name_short = getid3_id3v2::FrameNameShortLookup($parsed_frame['frame_name']);
     if ($frame_name_short) {
         $parsed_frame['framenameshort'] = $frame_name_short;
     }
     if ($id3v2_major_version >= 3) {
         // frame flags are not part of the ID3v2.2 standard
         if ($id3v2_major_version == 3) {
             //    Frame Header Flags
             //    %abc00000 %ijk00000
             $parsed_frame['flags']['TagAlterPreservation'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x8000);
             // a - Tag alter preservation
             $parsed_frame['flags']['FileAlterPreservation'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x4000);
             // b - File alter preservation
             $parsed_frame['flags']['ReadOnly'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x2000);
             // c - Read only
             $parsed_frame['flags']['compression'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x80);
             // i - Compression
             $parsed_frame['flags']['Encryption'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x40);
             // j - Encryption
             $parsed_frame['flags']['GroupingIdentity'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x20);
             // k - Grouping identity
         } elseif ($id3v2_major_version == 4) {
             //    Frame Header Flags
             //    %0abc0000 %0h00kmnp
             $parsed_frame['flags']['TagAlterPreservation'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x4000);
             // a - Tag alter preservation
             $parsed_frame['flags']['FileAlterPreservation'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x2000);
             // b - File alter preservation
             $parsed_frame['flags']['ReadOnly'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x1000);
             // c - Read only
             $parsed_frame['flags']['GroupingIdentity'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x40);
             // h - Grouping identity
             $parsed_frame['flags']['compression'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x8);
             // k - Compression
             $parsed_frame['flags']['Encryption'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x4);
             // m - Encryption
             $parsed_frame['flags']['Unsynchronisation'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x2);
             // n - Unsynchronisation
             $parsed_frame['flags']['DataLengthIndicator'] = (bool) ($parsed_frame['frame_flags_raw'] & 0x1);
             // p - Data length indicator
             // Frame-level de-unsynchronisation - ID3v2.4
             if ($parsed_frame['flags']['Unsynchronisation']) {
                 $parsed_frame['data'] = str_replace("ÿ", "ÿ", $parsed_frame['data']);
             }
         }
         //    Frame-level de-compression
         if ($parsed_frame['flags']['compression']) {
             $parsed_frame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], 0, 4));
             if (!function_exists('gzuncompress')) {
                 $getid3->warning('gzuncompress() support required to decompress ID3v2 frame "' . $parsed_frame['frame_name'] . '"');
             } elseif ($decompressed_data = @gzuncompress(substr($parsed_frame['data'], 4))) {
                 $parsed_frame['data'] = $decompressed_data;
             } else {
                 $getid3->warning('gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsed_frame['frame_name'] . '"');
             }
         }
     }
     if (isset($parsed_frame['datalength']) && $parsed_frame['datalength'] == 0) {
         $warning = 'Frame "' . $parsed_frame['frame_name'] . '" at offset ' . $parsed_frame['dataoffset'] . ' has no data portion';
         switch ($parsed_frame['frame_name']) {
             case 'WCOM':
                 $warning .= ' (this is known to happen with files tagged by RioPort)';
                 break;
             default:
                 break;
         }
         $getid3->warning($warning);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'UFID' || $id3v2_major_version == 2 && $parsed_frame['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_terminator_pos = strpos($parsed_frame['data'], "");
         $frame_id_string = substr($parsed_frame['data'], 0, $frame_terminator_pos);
         $parsed_frame['ownerid'] = $frame_id_string;
         $parsed_frame['data'] = substr($parsed_frame['data'], $frame_terminator_pos + strlen(""));
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'TXXX' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['description'] = $frame_description;
         $parsed_frame['data'] = substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = trim($getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']));
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             // remove possible terminating \x00 (put by encoding id or software bug)
             $string = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']);
             if ($string[strlen($string) - 1] = "") {
                 $string = substr($string, 0, strlen($string) - 1);
             }
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $string;
             unset($string);
         }
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'WXXX' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsed_frame['data'] = substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
         $frame_terminator_pos = strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         if ($frame_terminator_pos) {
             // there are null bytes after the data - this is not according to spec
             // only use data up to first null byte
             $frame_urldata = (string) substr($parsed_frame['data'], 0, $frame_terminator_pos);
         } else {
             // no null bytes following data, just use all data
             $frame_urldata = (string) $parsed_frame['data'];
         }
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['url'] = $frame_urldata;
         $parsed_frame['description'] = $frame_description;
         if (!empty($parsed_frame['framenameshort']) && $parsed_frame['url']) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['url']);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($parsed_frame['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>
         $parsed_frame['url'] = trim($parsed_frame['data']);
         if (!empty($parsed_frame['framenameshort']) && $parsed_frame['url']) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $parsed_frame['url'];
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version == 3 && $parsed_frame['frame_name'] == 'IPLS' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($parsed_frame['encodingid']);
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']);
         }
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'MCDI' || $id3v2_major_version == 2 && $parsed_frame['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>
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $parsed_frame['data'];
         }
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'ETCO' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['timestampformat'] = ord($parsed_frame['data'][$frame_offset++]);
         while ($frame_offset < strlen($parsed_frame['data'])) {
             $parsed_frame['typeid'] = $parsed_frame['data'][$frame_offset++];
             $parsed_frame['type'] = getid3_id3v2::ETCOEventLookup($parsed_frame['typeid']);
             $parsed_frame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
             $frame_offset += 4;
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'MLLT' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], 0, 2));
         $parsed_frame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], 2, 3));
         $parsed_frame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], 5, 3));
         $parsed_frame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int($parsed_frame['data'][8]);
         $parsed_frame['bitsformsdeviation'] = getid3_lib::BigEndian2Int($parsed_frame['data'][9]);
         $parsed_frame['data'] = substr($parsed_frame['data'], 10);
         while ($frame_offset < strlen($parsed_frame['data'])) {
             $deviation_bitstream .= getid3_lib::BigEndian2Bin($parsed_frame['data'][$frame_offset++]);
         }
         $reference_counter = 0;
         while (strlen($deviation_bitstream) > 0) {
             $parsed_frame[$reference_counter]['bytedeviation'] = bindec(substr($deviation_bitstream, 0, $parsed_frame['bitsforbytesdeviation']));
             $parsed_frame[$reference_counter]['msdeviation'] = bindec(substr($deviation_bitstream, $parsed_frame['bitsforbytesdeviation'], $parsed_frame['bitsformsdeviation']));
             $deviation_bitstream = substr($deviation_bitstream, $parsed_frame['bitsforbytesdeviation'] + $parsed_frame['bitsformsdeviation']);
             $reference_counter++;
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'SYTC' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['timestampformat'] = ord($parsed_frame['data'][$frame_offset++]);
         $timestamp_counter = 0;
         while ($frame_offset < strlen($parsed_frame['data'])) {
             $parsed_frame[$timestamp_counter]['tempo'] = ord($parsed_frame['data'][$frame_offset++]);
             if ($parsed_frame[$timestamp_counter]['tempo'] == 255) {
                 $parsed_frame[$timestamp_counter]['tempo'] += ord($parsed_frame['data'][$frame_offset++]);
             }
             $parsed_frame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
             $frame_offset += 4;
             $timestamp_counter++;
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'USLT' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_language = substr($parsed_frame['data'], $frame_offset, 3);
         $frame_offset += 3;
         if ($frame_offset > strlen($parsed_frame['data'])) {
             $frame_offset = strlen($parsed_frame['data']) - 1;
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsed_frame['data'] = substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['data'] = $parsed_frame['data'];
         $parsed_frame['language'] = $frame_language;
         $parsed_frame['languagename'] = getid3_id3v2::LanguageLookup($frame_language, false);
         $parsed_frame['description'] = $frame_description;
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'SYLT' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_language = substr($parsed_frame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsed_frame['timestampformat'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['contenttypeid'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['contenttype'] = getid3_id3v2::SYTLContentTypeLookup($parsed_frame['contenttypeid']);
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['language'] = $frame_language;
         $parsed_frame['languagename'] = getid3_id3v2::LanguageLookup($frame_language, false);
         $timestamp_index = 0;
         $frame_remaining_data = substr($parsed_frame['data'], $frame_offset);
         while (strlen($frame_remaining_data)) {
             $frame_offset = 0;
             $frame_terminator_pos = strpos($frame_remaining_data, getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
             if ($frame_terminator_pos === false) {
                 $frame_remaining_data = '';
             } else {
                 if (ord(substr($frame_remaining_data, $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
                     $frame_terminator_pos++;
                     // strpos() fooled because 2nd byte of Unicode chars are often 0x00
                 }
                 $parsed_frame['lyrics'][$timestamp_index]['data'] = substr($frame_remaining_data, $frame_offset, $frame_terminator_pos - $frame_offset);
                 $frame_remaining_data = substr($frame_remaining_data, $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
                 if ($timestamp_index == 0 && ord($frame_remaining_data[0]) != 0) {
                     // timestamp probably omitted for first data item
                 } else {
                     $parsed_frame['lyrics'][$timestamp_index]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remaining_data, 0, 4));
                     $frame_remaining_data = substr($frame_remaining_data, 4);
                 }
                 $timestamp_index++;
             }
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'COMM' || $id3v2_major_version == 2 && $parsed_frame['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>
         if (strlen($parsed_frame['data']) < 5) {
             $getid3->warning('Invalid data (too short) for "' . $parsed_frame['frame_name'] . '" frame at offset ' . $parsed_frame['dataoffset']);
             return true;
         }
         $frame_offset = 0;
         $frame_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_language = substr($parsed_frame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_text = (string) substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['language'] = $frame_language;
         $parsed_frame['languagename'] = getid3_id3v2::LanguageLookup($frame_language, false);
         $parsed_frame['description'] = $frame_description;
         $parsed_frame['data'] = $frame_text;
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']);
         }
         return true;
     }
     if ($id3v2_major_version >= 4 && $parsed_frame['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_terminator_pos = strpos($parsed_frame['data'], "");
         $frame_id_string = substr($parsed_frame['data'], 0, $frame_terminator_pos);
         if (ord($frame_id_string) === 0) {
             $frame_id_string = '';
         }
         $frame_remaining_data = substr($parsed_frame['data'], $frame_terminator_pos + strlen(""));
         $parsed_frame['description'] = $frame_id_string;
         while (strlen($frame_remaining_data)) {
             $frame_offset = 0;
             $frame_channeltypeid = ord(substr($frame_remaining_data, $frame_offset++, 1));
             $parsed_frame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid;
             $parsed_frame[$frame_channeltypeid]['channeltype'] = getid3_id3v2::RVA2ChannelTypeLookup($frame_channeltypeid);
             $parsed_frame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remaining_data, $frame_offset, 2), true);
             // 16-bit signed
             $frame_offset += 2;
             $parsed_frame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remaining_data, $frame_offset++, 1));
             $frame_bytespeakvolume = ceil($parsed_frame[$frame_channeltypeid]['bitspeakvolume'] / 8);
             $parsed_frame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remaining_data, $frame_offset, $frame_bytespeakvolume));
             $frame_remaining_data = substr($frame_remaining_data, $frame_offset + $frame_bytespeakvolume);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version == 3 && $parsed_frame['frame_name'] == 'RVAD' || $id3v2_major_version == 2 && $parsed_frame['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 = getid3_lib::BigEndian2Bin($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
         $parsed_frame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
         $parsed_frame['bitsvolume'] = ord($parsed_frame['data'][$frame_offset++]);
         $frame_bytesvolume = ceil($parsed_frame['bitsvolume'] / 8);
         $parsed_frame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsed_frame['incdec']['right'] === false) {
             $parsed_frame['volumechange']['right'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsed_frame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsed_frame['incdec']['left'] === false) {
             $parsed_frame['volumechange']['left'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsed_frame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         $parsed_frame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         if ($id3v2_major_version == 3) {
             $parsed_frame['data'] = substr($parsed_frame['data'], $frame_offset);
             if (strlen($parsed_frame['data']) > 0) {
                 $parsed_frame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
                 $parsed_frame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
                 $parsed_frame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsed_frame['incdec']['rightrear'] === false) {
                     $parsed_frame['volumechange']['rightrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsed_frame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsed_frame['incdec']['leftrear'] === false) {
                     $parsed_frame['volumechange']['leftrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsed_frame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
                 $parsed_frame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsed_frame['data'] = substr($parsed_frame['data'], $frame_offset);
             if (strlen($parsed_frame['data']) > 0) {
                 $parsed_frame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
                 $parsed_frame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsed_frame['incdec']['center'] === false) {
                     $parsed_frame['volumechange']['center'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsed_frame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsed_frame['data'] = substr($parsed_frame['data'], $frame_offset);
             if (strlen($parsed_frame['data']) > 0) {
                 $parsed_frame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
                 $parsed_frame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsed_frame['incdec']['bass'] === false) {
                     $parsed_frame['volumechange']['bass'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsed_frame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 4 && $parsed_frame['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($parsed_frame['data'][$frame_offset++]);
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_id_string = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_id_string) === 0) {
             $frame_id_string = '';
         }
         $parsed_frame['description'] = $frame_id_string;
         $frame_remaining_data = substr($parsed_frame['data'], $frame_terminator_pos + strlen(""));
         while (strlen($frame_remaining_data)) {
             $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remaining_data, 0, 2)) / 2;
             $parsed_frame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remaining_data, 2, 2), true);
             $frame_remaining_data = substr($frame_remaining_data, 4);
         }
         $parsed_frame['interpolationmethod'] = $frame_interpolationmethod;
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version == 3 && $parsed_frame['frame_name'] == 'EQUA' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['adjustmentbits'] = $parsed_frame['data'][$frame_offset++];
         $frame_adjustment_bytes = ceil($parsed_frame['adjustmentbits'] / 8);
         $frame_remaining_data = (string) substr($parsed_frame['data'], $frame_offset);
         while (strlen($frame_remaining_data) > 0) {
             $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remaining_data, 0, 2));
             $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
             $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
             $parsed_frame[$frame_frequency]['incdec'] = $frame_incdec;
             $parsed_frame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remaining_data, 2, $frame_adjustment_bytes));
             if ($parsed_frame[$frame_frequency]['incdec'] === false) {
                 $parsed_frame[$frame_frequency]['adjustment'] *= -1;
             }
             $frame_remaining_data = substr($frame_remaining_data, 2 + $frame_adjustment_bytes);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'RVRB' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['left'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['right'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['bouncesL'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['bouncesR'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['feedbackLL'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['feedbackLR'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['feedbackRR'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['feedbackRL'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['premixLR'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['premixRL'] = ord($parsed_frame['data'][$frame_offset++]);
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'APIC' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         if ($id3v2_major_version == 2 && strlen($parsed_frame['data']) > $frame_offset) {
             $frame_imagetype = substr($parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
                 $frame_mimetype = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $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_terminator_pos + strlen("");
             } else {
                 $frame_offset += 3;
             }
         }
         if ($id3v2_major_version > 2 && strlen($parsed_frame['data']) > $frame_offset) {
             $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
             $frame_mimetype = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
             if (ord($frame_mimetype) === 0) {
                 $frame_mimetype = '';
             }
             $frame_offset = $frame_terminator_pos + strlen("");
         }
         $frame_picturetype = ord($parsed_frame['data'][$frame_offset++]);
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         if ($id3v2_major_version == 2) {
             $parsed_frame['imagetype'] = $frame_imagetype;
         } else {
             $parsed_frame['mime'] = $frame_mimetype;
         }
         $parsed_frame['picturetypeid'] = $frame_picturetype;
         $parsed_frame['picturetype'] = getid3_id3v2::APICPictureTypeLookup($frame_picturetype);
         $parsed_frame['description'] = $frame_description;
         $parsed_frame['data'] = substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)));
         if ($getid3->option_tags_images) {
             $image_chunk_check = getid3_lib_image_size::get($parsed_frame['data']);
             if ($image_chunk_check[2] >= 1 && $image_chunk_check[2] <= 3) {
                 $parsed_frame['image_mime'] = image_type_to_mime_type($image_chunk_check[2]);
                 if ($image_chunk_check[0]) {
                     $parsed_frame['image_width'] = $image_chunk_check[0];
                 }
                 if ($image_chunk_check[1]) {
                     $parsed_frame['image_height'] = $image_chunk_check[1];
                 }
                 $parsed_frame['image_bytes'] = strlen($parsed_frame['data']);
             }
         }
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'GEOB' || $id3v2_major_version == 2 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_mimetype) === 0) {
             $frame_mimetype = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_filename = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_filename) === 0) {
             $frame_filename = '';
         }
         $frame_offset = $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
         $parsed_frame['objectdata'] = (string) substr($parsed_frame['data'], $frame_offset);
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['mime'] = $frame_mimetype;
         $parsed_frame['filename'] = $frame_filename;
         $parsed_frame['description'] = $frame_description;
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'PCNT' || $id3v2_major_version == 2 && $parsed_frame['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 ...)
         $parsed_frame['data'] = getid3_lib::BigEndian2Int($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'POPM' || $id3v2_major_version == 2 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_email_address = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_email_address) === 0) {
             $frame_email_address = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_rating = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['data'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset));
         $parsed_frame['email'] = $frame_email_address;
         $parsed_frame['rating'] = $frame_rating;
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'RBUF' || $id3v2_major_version == 2 && $parsed_frame['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;
         $parsed_frame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 3));
         $frame_offset += 3;
         $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
         $parsed_frame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version == 2 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_owner_id = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['ownerid'] = $frame_owner_id;
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         $parsed_frame['description'] = $frame_description;
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'AENC' || $id3v2_major_version == 2 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_owner_id = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_owner_id) === 0) {
             $frame_owner_id == '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['ownerid'] = $frame_owner_id;
         $parsed_frame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['encryptioninfo'] = (string) substr($parsed_frame['data'], $frame_offset);
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['frame_name'] == 'LINK' || $id3v2_major_version == 2 && $parsed_frame['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 ($id3v2_major_version == 2) {
             $parsed_frame['frameid'] = substr($parsed_frame['data'], $frame_offset, 3);
             $frame_offset += 3;
         } else {
             $parsed_frame['frameid'] = substr($parsed_frame['data'], $frame_offset, 4);
             $frame_offset += 4;
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_url = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_url) === 0) {
             $frame_url = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['url'] = $frame_url;
         $parsed_frame['additionaldata'] = (string) substr($parsed_frame['data'], $frame_offset);
         if (!empty($parsed_frame['framenameshort']) && $parsed_frame['url']) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = utf8_encode($parsed_frame['url']);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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;
         $parsed_frame['timestampformat'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['position'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset));
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_language = substr($parsed_frame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsed_frame['language'] = $frame_language;
         $parsed_frame['languagename'] = getid3_id3v2::LanguageLookup($frame_language, false);
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         if (!empty($parsed_frame['framenameshort']) && !empty($parsed_frame['data'])) {
             $getid3->info['id3v2']['comments'][$parsed_frame['framenameshort']][] = $getid3->iconv($parsed_frame['encoding'], 'UTF-8', $parsed_frame['data']);
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_pricepaid = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
         $parsed_frame['pricepaid']['currency'] = getid3_id3v2::LookupCurrencyUnits($parsed_frame['pricepaid']['currencyid']);
         $parsed_frame['pricepaid']['value'] = substr($frame_pricepaid, 3);
         $parsed_frame['purchasedate'] = substr($parsed_frame['data'], $frame_offset, 8);
         if (!getid3_id3v2::IsValidDateStampString($parsed_frame['purchasedate'])) {
             $parsed_frame['purchasedateunix'] = gmmktime(0, 0, 0, substr($parsed_frame['purchasedate'], 4, 2), substr($parsed_frame['purchasedate'], 6, 2), substr($parsed_frame['purchasedate'], 0, 4));
         }
         $frame_offset += 8;
         $parsed_frame['seller'] = (string) substr($parsed_frame['data'], $frame_offset);
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_text_encoding = ord($parsed_frame['data'][$frame_offset++]);
         if ($id3v2_major_version <= 3 && $frame_text_encoding > 1 || $id3v2_major_version == 4 && $frame_text_encoding > 3) {
             $getid3->warning('Invalid text encoding byte (' . $frame_text_encoding . ') in frame "' . $parsed_frame['frame_name'] . '" - defaulting to ISO-8859-1 encoding');
         }
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_price_string = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_rawpricearray = explode('/', $frame_price_string);
         foreach ($frame_rawpricearray as $key => $val) {
             $frame_currencyid = substr($val, 0, 3);
             $parsed_frame['price'][$frame_currencyid]['currency'] = getid3_id3v2::LookupCurrencyUnits($frame_currencyid);
             $parsed_frame['price'][$frame_currencyid]['value'] = substr($val, 3);
         }
         $frame_date_string = substr($parsed_frame['data'], $frame_offset, 8);
         $frame_offset += 8;
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_contacturl = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_received_as_id = ord($parsed_frame['data'][$frame_offset++]);
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_sellername = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_sellername) === 0) {
             $frame_sellername = '';
         }
         $frame_offset = $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
         $frame_terminator_pos = @strpos($parsed_frame['data'], getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding), $frame_offset);
         if (ord(substr($parsed_frame['data'], $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding)), 1)) === 0) {
             $frame_terminator_pos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminator_pos + strlen(getid3_id3v2::TextEncodingTerminatorLookup($frame_text_encoding));
         $frame_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         $frame_offset = $frame_terminator_pos + strlen("");
         $frame_sellerlogo = substr($parsed_frame['data'], $frame_offset);
         $parsed_frame['encodingid'] = $frame_text_encoding;
         $parsed_frame['encoding'] = $this->TextEncodingNameLookup($frame_text_encoding);
         $parsed_frame['pricevaliduntil'] = $frame_date_string;
         $parsed_frame['contacturl'] = $frame_contacturl;
         $parsed_frame['receivedasid'] = $frame_received_as_id;
         $parsed_frame['receivedas'] = getid3_id3v2::COMRReceivedAsLookup($frame_received_as_id);
         $parsed_frame['sellername'] = $frame_sellername;
         $parsed_frame['description'] = $frame_description;
         $parsed_frame['mime'] = $frame_mimetype;
         $parsed_frame['logo'] = $frame_sellerlogo;
         unset($parsed_frame['data']);
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_owner_id = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_owner_id) === 0) {
             $frame_owner_id = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['ownerid'] = $frame_owner_id;
         $parsed_frame['methodsymbol'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_owner_id = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_owner_id) === 0) {
             $frame_owner_id = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['ownerid'] = $frame_owner_id;
         $parsed_frame['groupsymbol'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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_terminator_pos = @strpos($parsed_frame['data'], "", $frame_offset);
         $frame_owner_id = substr($parsed_frame['data'], $frame_offset, $frame_terminator_pos - $frame_offset);
         if (ord($frame_owner_id) === 0) {
             $frame_owner_id = '';
         }
         $frame_offset = $frame_terminator_pos + strlen("");
         $parsed_frame['ownerid'] = $frame_owner_id;
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         return true;
     }
     if ($id3v2_major_version >= 4 && $parsed_frame['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;
         $parsed_frame['groupsymbol'] = ord($parsed_frame['data'][$frame_offset++]);
         $parsed_frame['data'] = (string) substr($parsed_frame['data'], $frame_offset);
         return true;
     }
     if ($id3v2_major_version >= 4 && $parsed_frame['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;
         $parsed_frame['data'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
         return true;
     }
     if ($id3v2_major_version >= 4 && $parsed_frame['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;
         $parsed_frame['datastart'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsed_frame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsed_frame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['bitsperpoint'] = ord($parsed_frame['data'][$frame_offset++]);
         $frame_bytesperpoint = ceil($parsed_frame['bitsperpoint'] / 8);
         for ($i = 0; $i < $frame_indexpoints; $i++) {
             $parsed_frame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, $frame_bytesperpoint));
             $frame_offset += $frame_bytesperpoint;
         }
         unset($parsed_frame['data']);
         return true;
     }
     if ($id3v2_major_version >= 3 && $parsed_frame['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;
         $parsed_frame['peakamplitude'] = (double) getid3_lib::BigEndian2Int(substr($parsed_frame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $rg_track_adjustment = decbin(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $rg_album_adjustment = decbin(substr($parsed_frame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsed_frame['raw']['track']['name'] = bindec(substr($rg_track_adjustment, 0, 3));
         $parsed_frame['raw']['track']['originator'] = bindec(substr($rg_track_adjustment, 3, 3));
         $parsed_frame['raw']['track']['signbit'] = bindec($rg_track_adjustment[6]);
         $parsed_frame['raw']['track']['adjustment'] = bindec(substr($rg_track_adjustment, 7, 9));
         $parsed_frame['raw']['album']['name'] = bindec(substr($rg_album_adjustment, 0, 3));
         $parsed_frame['raw']['album']['originator'] = bindec(substr($rg_album_adjustment, 3, 3));
         $parsed_frame['raw']['album']['signbit'] = bindec($rg_album_adjustment[6]);
         $parsed_frame['raw']['album']['adjustment'] = bindec(substr($rg_album_adjustment, 7, 9));
         $parsed_frame['track']['name'] = getid3_lib_replaygain::NameLookup($parsed_frame['raw']['track']['name']);
         $parsed_frame['track']['originator'] = getid3_lib_replaygain::OriginatorLookup($parsed_frame['raw']['track']['originator']);
         $parsed_frame['track']['adjustment'] = getid3_lib_replaygain::AdjustmentLookup($parsed_frame['raw']['track']['adjustment'], $parsed_frame['raw']['track']['signbit']);
         $parsed_frame['album']['name'] = getid3_lib_replaygain::NameLookup($parsed_frame['raw']['album']['name']);
         $parsed_frame['album']['originator'] = getid3_lib_replaygain::OriginatorLookup($parsed_frame['raw']['album']['originator']);
         $parsed_frame['album']['adjustment'] = getid3_lib_replaygain::AdjustmentLookup($parsed_frame['raw']['album']['adjustment'], $parsed_frame['raw']['album']['signbit']);
         $getid3->info['replay_gain']['track']['peak'] = $parsed_frame['peakamplitude'];
         $getid3->info['replay_gain']['track']['originator'] = $parsed_frame['track']['originator'];
         $getid3->info['replay_gain']['track']['adjustment'] = $parsed_frame['track']['adjustment'];
         $getid3->info['replay_gain']['album']['originator'] = $parsed_frame['album']['originator'];
         $getid3->info['replay_gain']['album']['adjustment'] = $parsed_frame['album']['adjustment'];
         unset($parsed_frame['data']);
         return true;
     }
     return true;
 }
Exemplo n.º 2
0
 function GenerateID3v2FrameData($frame_name, $source_data_array)
 {
     if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
         return false;
     }
     $framedata = '';
     if ($this->majorversion < 3 || $this->majorversion > 4) {
         $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
     } else {
         // $this->majorversion 3 or 4
         switch ($frame_name) {
             case 'UFID':
                 // 4.1   UFID Unique file identifier
                 // Owner identifier        <text string> $00
                 // Identifier              <up to 64 bytes binary data>
                 if (strlen($source_data_array['data']) > 64) {
                     $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in ' . $frame_name . ' (supplied data was ' . strlen($source_data_array['data']) . ' bytes long)';
                 } else {
                     $framedata .= str_replace("", '', $source_data_array['ownerid']) . "";
                     $framedata .= substr($source_data_array['data'], 0, 64);
                     // max 64 bytes - truncate anything longer
                 }
                 break;
             case 'TXXX':
                 // 4.2.2 TXXX User defined text information frame
                 // Text encoding     $xx
                 // Description       <text string according to encoding> $00 (00)
                 // Value             <text string according to encoding>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'WXXX':
                 // 4.3.2 WXXX User defined URL link frame
                 // Text encoding     $xx
                 // Description       <text string according to encoding> $00 (00)
                 // URL               <text string>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
                     //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
                     // probably should be an error, need to rewrite IsValidURL() to handle other encodings
                     $this->warnings[] = 'Invalid URL in ' . $frame_name . ' (' . $source_data_array['data'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'IPLS':
                 // 4.4  IPLS Involved people list (ID3v2.3 only)
                 // Text encoding     $xx
                 // People list strings    <textstrings>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'MCDI':
                 // 4.4   MCDI Music CD identifier
                 // CD TOC                <binary data>
                 $framedata .= $source_data_array['data'];
                 break;
             case 'ETCO':
                 // 4.5   ETCO Event timing codes
                 // 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.
                 if ($source_data_array['timestampformat'] > 2 || $source_data_array['timestampformat'] < 1) {
                     $this->errors[] = 'Invalid Time Stamp Format byte in ' . $frame_name . ' (' . $source_data_array['timestampformat'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['timestampformat']);
                     foreach ($source_data_array as $key => $val) {
                         if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
                             $this->errors[] = 'Invalid Event Type byte in ' . $frame_name . ' (' . $val['typeid'] . ')';
                         } elseif ($key != 'timestampformat' && $key != 'flags') {
                             if ($val['timestamp'] > 0 && $previousETCOtimestamp >= $val['timestamp']) {
                                 //   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.
                                 $this->errors[] = 'Out-of-order timestamp in ' . $frame_name . ' (' . $val['timestamp'] . ') for Event Type (' . $val['typeid'] . ')';
                             } else {
                                 $framedata .= chr($val['typeid']);
                                 $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
                             }
                         }
                     }
                 }
                 break;
             case 'MLLT':
                 // 4.6   MLLT MPEG location lookup table
                 // 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....
                 if ($source_data_array['framesbetweenreferences'] > 0 && $source_data_array['framesbetweenreferences'] <= 65535) {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
                 } else {
                     $this->errors[] = 'Invalid MPEG Frames Between References in ' . $frame_name . ' (' . $source_data_array['framesbetweenreferences'] . ')';
                 }
                 if ($source_data_array['bytesbetweenreferences'] > 0 && $source_data_array['bytesbetweenreferences'] <= 16777215) {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
                 } else {
                     $this->errors[] = 'Invalid bytes Between References in ' . $frame_name . ' (' . $source_data_array['bytesbetweenreferences'] . ')';
                 }
                 if ($source_data_array['msbetweenreferences'] > 0 && $source_data_array['msbetweenreferences'] <= 16777215) {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
                 } else {
                     $this->errors[] = 'Invalid Milliseconds Between References in ' . $frame_name . ' (' . $source_data_array['msbetweenreferences'] . ')';
                 }
                 if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
                     if ($source_data_array['bitsforbytesdeviation'] % 4 == 0) {
                         $framedata .= chr($source_data_array['bitsforbytesdeviation']);
                     } else {
                         $this->errors[] = 'Bits For Bytes Deviation in ' . $frame_name . ' (' . $source_data_array['bitsforbytesdeviation'] . ') must be a multiple of 4.';
                     }
                 } else {
                     $this->errors[] = 'Invalid Bits For Bytes Deviation in ' . $frame_name . ' (' . $source_data_array['bitsforbytesdeviation'] . ')';
                 }
                 if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
                     if ($source_data_array['bitsformsdeviation'] % 4 == 0) {
                         $framedata .= chr($source_data_array['bitsformsdeviation']);
                     } else {
                         $this->errors[] = 'Bits For Milliseconds Deviation in ' . $frame_name . ' (' . $source_data_array['bitsforbytesdeviation'] . ') must be a multiple of 4.';
                     }
                 } else {
                     $this->errors[] = 'Invalid Bits For Milliseconds Deviation in ' . $frame_name . ' (' . $source_data_array['bitsformsdeviation'] . ')';
                 }
                 foreach ($source_data_array as $key => $val) {
                     if ($key != 'framesbetweenreferences' && $key != 'bytesbetweenreferences' && $key != 'msbetweenreferences' && $key != 'bitsforbytesdeviation' && $key != 'bitsformsdeviation' && $key != 'flags') {
                         $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
                         $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT);
                     }
                 }
                 for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
                     $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
                     $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4));
                     $framedata .= chr($highnibble & $lownibble);
                 }
                 break;
             case 'SYTC':
                 // 4.7   SYTC Synchronised tempo codes
                 // 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
                 if ($source_data_array['timestampformat'] > 2 || $source_data_array['timestampformat'] < 1) {
                     $this->errors[] = 'Invalid Time Stamp Format byte in ' . $frame_name . ' (' . $source_data_array['timestampformat'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['timestampformat']);
                     foreach ($source_data_array as $key => $val) {
                         if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
                             $this->errors[] = 'Invalid Event Type byte in ' . $frame_name . ' (' . $val['typeid'] . ')';
                         } elseif ($key != 'timestampformat' && $key != 'flags') {
                             if ($val['tempo'] < 0 || $val['tempo'] > 510) {
                                 $this->errors[] = 'Invalid Tempo (max = 510) in ' . $frame_name . ' (' . $val['tempo'] . ') at timestamp (' . $val['timestamp'] . ')';
                             } else {
                                 if ($val['tempo'] > 255) {
                                     $framedata .= chr(255);
                                     $val['tempo'] -= 255;
                                 }
                                 $framedata .= chr($val['tempo']);
                                 $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
                             }
                         }
                     }
                 }
                 break;
             case 'USLT':
                 // 4.8   USLT Unsynchronised lyric/text transcription
                 // Text encoding        $xx
                 // Language             $xx xx xx
                 // Content descriptor   <text string according to encoding> $00 (00)
                 // Lyrics/text          <full text string according to encoding>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
                     $this->errors[] = 'Invalid Language in ' . $frame_name . ' (' . $source_data_array['language'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= strtolower($source_data_array['language']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'SYLT':
                 // 4.9   SYLT Synchronised lyric/text
                 // 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 ...)
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
                     $this->errors[] = 'Invalid Language in ' . $frame_name . ' (' . $source_data_array['language'] . ')';
                 } elseif ($source_data_array['timestampformat'] > 2 || $source_data_array['timestampformat'] < 1) {
                     $this->errors[] = 'Invalid Time Stamp Format byte in ' . $frame_name . ' (' . $source_data_array['timestampformat'] . ')';
                 } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
                     $this->errors[] = 'Invalid Content Type byte in ' . $frame_name . ' (' . $source_data_array['contenttypeid'] . ')';
                 } elseif (!is_array($source_data_array['data'])) {
                     $this->errors[] = 'Invalid Lyric/Timestamp data in ' . $frame_name . ' (must be an array)';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= strtolower($source_data_array['language']);
                     $framedata .= chr($source_data_array['timestampformat']);
                     $framedata .= chr($source_data_array['contenttypeid']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     ksort($source_data_array['data']);
                     foreach ($source_data_array['data'] as $key => $val) {
                         $framedata .= $val['data'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                         $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
                     }
                 }
                 break;
             case 'COMM':
                 // 4.10  COMM Comments
                 // 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>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
                     $this->errors[] = 'Invalid Language in ' . $frame_name . ' (' . $source_data_array['language'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= strtolower($source_data_array['language']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'RVA2':
                 // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
                 // 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 ...)
                 $framedata .= str_replace("", '', $source_data_array['description']) . "";
                 foreach ($source_data_array as $key => $val) {
                     if ($key != 'description') {
                         $framedata .= chr($val['channeltypeid']);
                         $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true);
                         // signed 16-bit
                         if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
                             $framedata .= chr($val['bitspeakvolume']);
                             if ($val['bitspeakvolume'] > 0) {
                                 $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
                             }
                         } else {
                             $this->errors[] = 'Invalid Bits Representing Peak Volume in ' . $frame_name . ' (' . $val['bitspeakvolume'] . ') (range = 0 to 255)';
                         }
                     }
                 }
                 break;
             case 'RVAD':
                 // 4.12  RVAD Relative volume adjustment (ID3v2.3 only)
                 // 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 ...)
                 // 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 ...)
                 // Relative volume change, center     $xx xx (xx ...) // e
                 // Peak volume center                 $xx xx (xx ...)
                 // Relative volume change, bass       $xx xx (xx ...) // f
                 // Peak volume bass                   $xx xx (xx ...)
                 if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
                     $this->errors[] = 'Invalid Bits For Volume Description byte in ' . $frame_name . ' (' . $source_data_array['bitsvolume'] . ') (range = 1 to 255)';
                 } else {
                     $incdecflag .= '00';
                     $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0';
                     // a - Relative volume change, right
                     $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0';
                     // b - Relative volume change, left
                     $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0';
                     // c - Relative volume change, right back
                     $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0';
                     // d - Relative volume change, left back
                     $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0';
                     // e - Relative volume change, center
                     $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0';
                     // f - Relative volume change, bass
                     $framedata .= chr(bindec($incdecflag));
                     $framedata .= chr($source_data_array['bitsvolume']);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
                     if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] || $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] || $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume'] / 8), false);
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume'] / 8), false);
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume'] / 8), false);
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume'] / 8), false);
                     }
                     if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume'] / 8), false);
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume'] / 8), false);
                     }
                     if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume'] / 8), false);
                         $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume'] / 8), false);
                     }
                 }
                 break;
             case 'EQU2':
                 // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
                 // 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
                 if ($source_data_array['interpolationmethod'] < 0 || $source_data_array['interpolationmethod'] > 1) {
                     $this->errors[] = 'Invalid Interpolation Method byte in ' . $frame_name . ' (' . $source_data_array['interpolationmethod'] . ') (valid = 0 or 1)';
                 } else {
                     $framedata .= chr($source_data_array['interpolationmethod']);
                     $framedata .= str_replace("", '', $source_data_array['description']) . "";
                     foreach ($source_data_array['data'] as $key => $val) {
                         $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
                         $framedata .= getid3_lib::BigEndian2String($val, 2, false, true);
                         // signed 16-bit
                     }
                 }
                 break;
             case 'EQUA':
                 // 4.12  EQUA Equalisation (ID3v2.3 only)
                 // 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 ...)
                 if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
                     $this->errors[] = 'Invalid Adjustment Bits byte in ' . $frame_name . ' (' . $source_data_array['bitsvolume'] . ') (range = 1 to 255)';
                 } else {
                     $framedata .= chr($source_data_array['adjustmentbits']);
                     foreach ($source_data_array as $key => $val) {
                         if ($key != 'bitsvolume') {
                             if ($key > 32767 || $key < 0) {
                                 $this->errors[] = 'Invalid Frequency in ' . $frame_name . ' (' . $key . ') (range = 0 to 32767)';
                             } else {
                                 if ($val >= 0) {
                                     // put MSB of frequency to 1 if increment, 0 if decrement
                                     $key |= 0x8000;
                                 }
                                 $framedata .= getid3_lib::BigEndian2String($key, 2, false);
                                 $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
                             }
                         }
                     }
                 }
                 break;
             case 'RVRB':
                 // 4.13  RVRB Reverb
                 // 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
                 if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
                     $this->errors[] = 'Invalid Reverb Left in ' . $frame_name . ' (' . $source_data_array['left'] . ') (range = 0 to 65535)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
                     $this->errors[] = 'Invalid Reverb Left in ' . $frame_name . ' (' . $source_data_array['right'] . ') (range = 0 to 65535)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Bounces, Left in ' . $frame_name . ' (' . $source_data_array['bouncesL'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Bounces, Right in ' . $frame_name . ' (' . $source_data_array['bouncesR'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in ' . $frame_name . ' (' . $source_data_array['feedbackLL'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in ' . $frame_name . ' (' . $source_data_array['feedbackLR'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in ' . $frame_name . ' (' . $source_data_array['feedbackRR'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
                     $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in ' . $frame_name . ' (' . $source_data_array['feedbackRL'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
                     $this->errors[] = 'Invalid Premix, Left-To-Right in ' . $frame_name . ' (' . $source_data_array['premixLR'] . ') (range = 0 to 255)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
                     $this->errors[] = 'Invalid Premix, Right-To-Left in ' . $frame_name . ' (' . $source_data_array['premixRL'] . ') (range = 0 to 255)';
                 } else {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
                     $framedata .= chr($source_data_array['bouncesL']);
                     $framedata .= chr($source_data_array['bouncesR']);
                     $framedata .= chr($source_data_array['feedbackLL']);
                     $framedata .= chr($source_data_array['feedbackLR']);
                     $framedata .= chr($source_data_array['feedbackRR']);
                     $framedata .= chr($source_data_array['feedbackRL']);
                     $framedata .= chr($source_data_array['premixLR']);
                     $framedata .= chr($source_data_array['premixRL']);
                 }
                 break;
             case 'APIC':
                 // 4.14  APIC Attached picture
                 // Text encoding      $xx
                 // MIME type          <text string> $00
                 // Picture type       $xx
                 // Description        <text string according to encoding> $00 (00)
                 // Picture data       <binary data>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
                     $this->errors[] = 'Invalid Picture Type byte in ' . $frame_name . ' (' . $source_data_array['picturetypeid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif ($this->majorversion >= 3 && !$this->ID3v2IsValidAPICimageformat($source_data_array['mime'])) {
                     $this->errors[] = 'Invalid MIME Type in ' . $frame_name . ' (' . $source_data_array['mime'] . ') for ID3v2.' . $this->majorversion;
                 } elseif ($source_data_array['mime'] == '-->' && !$this->IsValidURL($source_data_array['data'], false, false)) {
                     //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
                     // probably should be an error, need to rewrite IsValidURL() to handle other encodings
                     $this->warnings[] = 'Invalid URL in ' . $frame_name . ' (' . $source_data_array['data'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= str_replace("", '', $source_data_array['mime']) . "";
                     $framedata .= chr($source_data_array['picturetypeid']);
                     $framedata .= @$source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'GEOB':
                 // 4.15  GEOB General encapsulated object
                 // 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>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                 } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
                     $this->errors[] = 'Invalid MIME Type in ' . $frame_name . ' (' . $source_data_array['mime'] . ')';
                 } elseif (!$source_data_array['description']) {
                     $this->errors[] = 'Missing Description in ' . $frame_name;
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= str_replace("", '', $source_data_array['mime']) . "";
                     $framedata .= $source_data_array['filename'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'PCNT':
                 // 4.16  PCNT Play counter
                 //   When the counter reaches all one's, one byte is inserted in
                 //   front of the counter thus making the counter eight bits bigger
                 // Counter        $xx xx xx xx (xx ...)
                 $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
                 break;
             case 'POPM':
                 // 4.17  POPM Popularimeter
                 //   When the counter reaches all one's, one byte is inserted in
                 //   front of the counter thus making the counter eight bits bigger
                 // Email to user   <text string> $00
                 // Rating          $xx
                 // Counter         $xx xx xx xx (xx ...)
                 if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
                     $this->errors[] = 'Invalid Rating byte in ' . $frame_name . ' (' . $source_data_array['rating'] . ') (range = 0 to 255)';
                 } elseif (!IsValidEmail($source_data_array['email'])) {
                     $this->errors[] = 'Invalid Email in ' . $frame_name . ' (' . $source_data_array['email'] . ')';
                 } else {
                     $framedata .= str_replace("", '', $source_data_array['email']) . "";
                     $framedata .= chr($source_data_array['rating']);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
                 }
                 break;
             case 'RBUF':
                 // 4.18  RBUF Recommended buffer size
                 // Buffer size               $xx xx xx
                 // Embedded info flag        %0000000x
                 // Offset to next tag        $xx xx xx xx
                 if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
                     $this->errors[] = 'Invalid Buffer Size in ' . $frame_name;
                 } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
                     $this->errors[] = 'Invalid Offset To Next Tag in ' . $frame_name;
                 } else {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
                     $flag .= '0000000';
                     $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
                     $framedata .= chr(bindec($flag));
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
                 }
                 break;
             case 'AENC':
                 // 4.19  AENC Audio encryption
                 // Owner identifier   <text string> $00
                 // Preview start      $xx xx
                 // Preview length     $xx xx
                 // Encryption info    <binary data>
                 if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
                     $this->errors[] = 'Invalid Preview Start in ' . $frame_name . ' (' . $source_data_array['previewstart'] . ')';
                 } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
                     $this->errors[] = 'Invalid Preview Length in ' . $frame_name . ' (' . $source_data_array['previewlength'] . ')';
                 } else {
                     $framedata .= str_replace("", '', $source_data_array['ownerid']) . "";
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
                     $framedata .= $source_data_array['encryptioninfo'];
                 }
                 break;
             case 'LINK':
                 // 4.20  LINK Linked information
                 // Frame identifier               $xx xx xx xx
                 // URL                            <text string> $00
                 // ID and additional data         <text string(s)>
                 if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
                     $this->errors[] = 'Invalid Frame Identifier in ' . $frame_name . ' (' . $source_data_array['frameid'] . ')';
                 } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
                     //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
                     // probably should be an error, need to rewrite IsValidURL() to handle other encodings
                     $this->warnings[] = 'Invalid URL in ' . $frame_name . ' (' . $source_data_array['data'] . ')';
                 } elseif (($source_data_array['frameid'] == 'AENC' || $source_data_array['frameid'] == 'APIC' || $source_data_array['frameid'] == 'GEOB' || $source_data_array['frameid'] == 'TXXX') && $source_data_array['additionaldata'] == '') {
                     $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of ' . $source_data_array['frameid'] . ' in ' . $frame_name;
                 } elseif ($source_data_array['frameid'] == 'USER' && getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '') {
                     $this->errors[] = 'Language must be specified as additional data for Frame Identifier of ' . $source_data_array['frameid'] . ' in ' . $frame_name;
                 } elseif ($source_data_array['frameid'] == 'PRIV' && $source_data_array['additionaldata'] == '') {
                     $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of ' . $source_data_array['frameid'] . ' in ' . $frame_name;
                 } elseif (($source_data_array['frameid'] == 'COMM' || $source_data_array['frameid'] == 'SYLT' || $source_data_array['frameid'] == 'USLT') && (getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '' || substr($source_data_array['additionaldata'], 3) == '')) {
                     $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of ' . $source_data_array['frameid'] . ' in ' . $frame_name;
                 } else {
                     $framedata .= $source_data_array['frameid'];
                     $framedata .= str_replace("", '', $source_data_array['data']) . "";
                     switch ($source_data_array['frameid']) {
                         case 'COMM':
                         case 'SYLT':
                         case 'USLT':
                         case 'PRIV':
                         case 'USER':
                         case 'AENC':
                         case 'APIC':
                         case 'GEOB':
                         case 'TXXX':
                             $framedata .= $source_data_array['additionaldata'];
                             break;
                         case 'ASPI':
                         case 'ETCO':
                         case 'EQU2':
                         case 'MCID':
                         case 'MLLT':
                         case 'OWNE':
                         case 'RVA2':
                         case 'RVRB':
                         case 'SYTC':
                         case 'IPLS':
                         case 'RVAD':
                         case 'EQUA':
                             // no additional data required
                             break;
                         case 'RBUF':
                             if ($this->majorversion == 3) {
                                 // no additional data required
                             } else {
                                 $this->errors[] = $source_data_array['frameid'] . ' is not a valid Frame Identifier in ' . $frame_name . ' (in ID3v2.' . $this->majorversion . ')';
                             }
                         default:
                             if (substr($source_data_array['frameid'], 0, 1) == 'T' || substr($source_data_array['frameid'], 0, 1) == 'W') {
                                 // no additional data required
                             } else {
                                 $this->errors[] = $source_data_array['frameid'] . ' is not a valid Frame Identifier in ' . $frame_name . ' (in ID3v2.' . $this->majorversion . ')';
                             }
                             break;
                     }
                 }
                 break;
             case 'POSS':
                 // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
                 // Time stamp format         $xx
                 // Position                  $xx (xx ...)
                 if ($source_data_array['timestampformat'] < 1 || $source_data_array['timestampformat'] > 2) {
                     $this->errors[] = 'Invalid Time Stamp Format in ' . $frame_name . ' (' . $source_data_array['timestampformat'] . ') (valid = 1 or 2)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
                     $this->errors[] = 'Invalid Position in ' . $frame_name . ' (' . $source_data_array['position'] . ') (range = 0 to 4294967295)';
                 } else {
                     $framedata .= chr($source_data_array['timestampformat']);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
                 }
                 break;
             case 'USER':
                 // 4.22  USER Terms of use (ID3v2.3+ only)
                 // Text encoding        $xx
                 // Language             $xx xx xx
                 // The actual text      <text string according to encoding>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ')';
                 } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
                     $this->errors[] = 'Invalid Language in ' . $frame_name . ' (' . $source_data_array['language'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= strtolower($source_data_array['language']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'OWNE':
                 // 4.23  OWNE Ownership frame (ID3v2.3+ only)
                 // Text encoding     $xx
                 // Price paid        <text string> $00
                 // Date of purch.    <text string>
                 // Seller            <text string according to encoding>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ')';
                 } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
                     $this->errors[] = 'Invalid Price Paid in ' . $frame_name . ' (' . $source_data_array['pricepaid']['value'] . ')';
                 } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
                     $this->errors[] = 'Invalid Date Of Purchase in ' . $frame_name . ' (' . $source_data_array['purchasedate'] . ') (format = YYYYMMDD)';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     $framedata .= str_replace("", '', $source_data_array['pricepaid']['value']) . "";
                     $framedata .= $source_data_array['purchasedate'];
                     $framedata .= $source_data_array['seller'];
                 }
                 break;
             case 'COMR':
                 // 4.24  COMR Commercial frame (ID3v2.3+ only)
                 // 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>
                 $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                 if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                     $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ')';
                 } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
                     $this->errors[] = 'Invalid Valid Until date in ' . $frame_name . ' (' . $source_data_array['pricevaliduntil'] . ') (format = YYYYMMDD)';
                 } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
                     $this->errors[] = 'Invalid Contact URL in ' . $frame_name . ' (' . $source_data_array['contacturl'] . ') (allowed schemes: http, https, ftp, mailto)';
                 } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
                     $this->errors[] = 'Invalid Received As byte in ' . $frame_name . ' (' . $source_data_array['contacturl'] . ') (range = 0 to 8)';
                 } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
                     $this->errors[] = 'Invalid MIME Type in ' . $frame_name . ' (' . $source_data_array['mime'] . ')';
                 } else {
                     $framedata .= chr($source_data_array['encodingid']);
                     unset($pricestring);
                     foreach ($source_data_array['price'] as $key => $val) {
                         if ($this->ID3v2IsValidPriceString($key . $val['value'])) {
                             $pricestrings[] = $key . $val['value'];
                         } else {
                             $this->errors[] = 'Invalid Price String in ' . $frame_name . ' (' . $key . $val['value'] . ')';
                         }
                     }
                     $framedata .= implode('/', $pricestrings);
                     $framedata .= $source_data_array['pricevaliduntil'];
                     $framedata .= str_replace("", '', $source_data_array['contacturl']) . "";
                     $framedata .= chr($source_data_array['receivedasid']);
                     $framedata .= $source_data_array['sellername'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['description'] . getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
                     $framedata .= $source_data_array['mime'] . "";
                     $framedata .= $source_data_array['logo'];
                 }
                 break;
             case 'ENCR':
                 // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
                 // Owner identifier    <text string> $00
                 // Method symbol       $xx
                 // Encryption data     <binary data>
                 if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
                     $this->errors[] = 'Invalid Group Symbol in ' . $frame_name . ' (' . $source_data_array['methodsymbol'] . ') (range = 0 to 255)';
                 } else {
                     $framedata .= str_replace("", '', $source_data_array['ownerid']) . "";
                     $framedata .= ord($source_data_array['methodsymbol']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'GRID':
                 // 4.26  GRID Group identification registration (ID3v2.3+ only)
                 // Owner identifier      <text string> $00
                 // Group symbol          $xx
                 // Group dependent data  <binary data>
                 if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
                     $this->errors[] = 'Invalid Group Symbol in ' . $frame_name . ' (' . $source_data_array['groupsymbol'] . ') (range = 0 to 255)';
                 } else {
                     $framedata .= str_replace("", '', $source_data_array['ownerid']) . "";
                     $framedata .= ord($source_data_array['groupsymbol']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'PRIV':
                 // 4.27  PRIV Private frame (ID3v2.3+ only)
                 // Owner identifier      <text string> $00
                 // The private data      <binary data>
                 $framedata .= str_replace("", '', $source_data_array['ownerid']) . "";
                 $framedata .= $source_data_array['data'];
                 break;
             case 'SIGN':
                 // 4.28  SIGN Signature frame (ID3v2.4+ only)
                 // Group symbol      $xx
                 // Signature         <binary data>
                 if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
                     $this->errors[] = 'Invalid Group Symbol in ' . $frame_name . ' (' . $source_data_array['groupsymbol'] . ') (range = 0 to 255)';
                 } else {
                     $framedata .= ord($source_data_array['groupsymbol']);
                     $framedata .= $source_data_array['data'];
                 }
                 break;
             case 'SEEK':
                 // 4.29  SEEK Seek frame (ID3v2.4+ only)
                 // Minimum offset to next tag       $xx xx xx xx
                 if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
                     $this->errors[] = 'Invalid Minimum Offset in ' . $frame_name . ' (' . $source_data_array['data'] . ') (range = 0 to 4294967295)';
                 } else {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
                 }
                 break;
             case 'ASPI':
                 // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
                 // 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)
                 if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
                     $this->errors[] = 'Invalid Indexed Data Start in ' . $frame_name . ' (' . $source_data_array['datastart'] . ') (range = 0 to 4294967295)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
                     $this->errors[] = 'Invalid Indexed Data Length in ' . $frame_name . ' (' . $source_data_array['datalength'] . ') (range = 0 to 4294967295)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
                     $this->errors[] = 'Invalid Number Of Index Points in ' . $frame_name . ' (' . $source_data_array['indexpoints'] . ') (range = 0 to 65535)';
                 } elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
                     $this->errors[] = 'Invalid Bits Per Index Point in ' . $frame_name . ' (' . $source_data_array['bitsperpoint'] . ') (range = 0 to 255)';
                 } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
                     $this->errors[] = 'Number Of Index Points does not match actual supplied data in ' . $frame_name;
                 } else {
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
                     $framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
                     foreach ($source_data_array['indexes'] as $key => $val) {
                         $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
                     }
                 }
                 break;
             case 'RGAD':
                 //   RGAD Replay Gain Adjustment
                 //   http://privatewww.essex.ac.uk/~djmrob/replaygain/
                 // 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
                 if ($source_data_array['track_adjustment'] > 51 || $source_data_array['track_adjustment'] < -51) {
                     $this->errors[] = 'Invalid Track Adjustment in ' . $frame_name . ' (' . $source_data_array['track_adjustment'] . ') (range = -51.0 to +51.0)';
                 } elseif ($source_data_array['album_adjustment'] > 51 || $source_data_array['album_adjustment'] < -51) {
                     $this->errors[] = 'Invalid Album Adjustment in ' . $frame_name . ' (' . $source_data_array['album_adjustment'] . ') (range = -51.0 to +51.0)';
                 } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
                     $this->errors[] = 'Invalid Track Name Code in ' . $frame_name . ' (' . $source_data_array['raw']['track_name'] . ') (range = 0 to 2)';
                 } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
                     $this->errors[] = 'Invalid Album Name Code in ' . $frame_name . ' (' . $source_data_array['raw']['album_name'] . ') (range = 0 to 2)';
                 } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
                     $this->errors[] = 'Invalid Track Originator Code in ' . $frame_name . ' (' . $source_data_array['raw']['track_originator'] . ') (range = 0 to 3)';
                 } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
                     $this->errors[] = 'Invalid Album Originator Code in ' . $frame_name . ' (' . $source_data_array['raw']['album_originator'] . ') (range = 0 to 3)';
                 } else {
                     $framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
                     $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
                     $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
                 }
                 break;
             default:
                 if ($frame_name[0] == 'T') {
                     // 4.2. T???  Text information frames
                     // Text encoding                $xx
                     // Information                  <text string(s) according to encoding>
                     $source_data_array['encodingid'] = isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid;
                     if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
                         $this->errors[] = 'Invalid Text Encoding in ' . $frame_name . ' (' . $source_data_array['encodingid'] . ') for ID3v2.' . $this->majorversion;
                     } else {
                         $framedata .= chr($source_data_array['encodingid']);
                         $framedata .= $source_data_array['data'];
                     }
                 } elseif ($frame_name[0] == 'W') {
                     // 4.3. W???  URL link frames
                     // URL              <text string>
                     if (!$this->IsValidURL($source_data_array['data'], false, false)) {
                         //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
                         // probably should be an error, need to rewrite IsValidURL() to handle other encodings
                         $this->warnings[] = 'Invalid URL in ' . $frame_name . ' (' . $source_data_array['data'] . ')';
                     } else {
                         $framedata .= $source_data_array['data'];
                     }
                 } else {
                     $this->errors[] = $frame_name . ' not yet supported in $this->GenerateID3v2FrameData()';
                 }
                 break;
         }
     }
     if (!empty($this->errors)) {
         return false;
     }
     return $framedata;
 }