예제 #1
0
 public function Analyze()
 {
     $getid3 = $this->getid3;
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $au_header = fread($getid3->fp, 8);
     // Magic bytes: .snd
     $getid3->info['au'] = array();
     $info_au =& $getid3->info['au'];
     $getid3->info['fileformat'] = 'au';
     $getid3->info['audio']['dataformat'] = 'au';
     $getid3->info['audio']['bitrate_mode'] = 'cbr';
     $info_au['encoding'] = 'ISO-8859-1';
     $info_au['header_length'] = getid3_lib::BigEndian2Int(substr($au_header, 4, 4));
     $au_header .= fread($getid3->fp, $info_au['header_length'] - 8);
     $getid3->info['avdataoffset'] += $info_au['header_length'];
     getid3_lib::ReadSequence('BigEndian2Int', $info_au, $au_header, 8, array('data_size' => 4, 'data_format_id' => 4, 'sample_rate' => 4, 'channels' => 4));
     $info_au['comments']['comment'][] = trim(substr($au_header, 24));
     $info_au['data_format'] = getid3_au::AUdataFormatNameLookup($info_au['data_format_id']);
     $info_au['used_bits_per_sample'] = getid3_au::AUdataFormatUsedBitsPerSampleLookup($info_au['data_format_id']);
     if ($info_au['bits_per_sample'] = getid3_au::AUdataFormatBitsPerSampleLookup($info_au['data_format_id'])) {
         $getid3->info['audio']['bits_per_sample'] = $info_au['bits_per_sample'];
     } else {
         unset($info_au['bits_per_sample']);
     }
     $getid3->info['audio']['sample_rate'] = $info_au['sample_rate'];
     $getid3->info['audio']['channels'] = $info_au['channels'];
     if ($getid3->info['avdataoffset'] + $info_au['data_size'] > $getid3->info['avdataend']) {
         $getid3->warning('Possible truncated file - expecting "' . $info_au['data_size'] . '" bytes of audio data, only found ' . ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) . ' bytes"');
     }
     $getid3->info['playtime_seconds'] = $info_au['data_size'] / ($info_au['sample_rate'] * $info_au['channels'] * ($info_au['used_bits_per_sample'] / 8));
     $getid3->info['audio']['bitrate'] = $info_au['data_size'] * 8 / $getid3->info['playtime_seconds'];
     return true;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $SZIPHeader = fread($this->getid3->fp, 6);
     if (substr($SZIPHeader, 0, 4) != "SZ\n") {
         $info['error'][] = 'Expecting "53 5A 0A 04" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)) . '"';
         return false;
     }
     $info['fileformat'] = 'szip';
     $info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
     $info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
     while (!feof($this->getid3->fp)) {
         $NextBlockID = fread($this->getid3->fp, 2);
         switch ($NextBlockID) {
             case 'SZ':
                 // Note that szip files can be concatenated, this has the same effect as
                 // concatenating the files. this also means that global header blocks
                 // might be present between directory/data blocks.
                 fseek($this->getid3->fp, 4, SEEK_CUR);
                 break;
             case 'BH':
                 $BHheaderbytes = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 3));
                 $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes);
                 $BHheaderoffset = 0;
                 while (strpos($BHheaderdata, "", $BHheaderoffset) > 0) {
                     //filename as \0 terminated string  (empty string indicates end)
                     //owner as \0 terminated string (empty is same as last file)
                     //group as \0 terminated string (empty is same as last file)
                     //3 byte filelength in this block
                     //2 byte access flags
                     //4 byte creation time (like in unix)
                     //4 byte modification time (like in unix)
                     //4 byte access time (like in unix)
                     $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['filename']) + 1;
                     $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['owner']) + 1;
                     $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['group']) + 1;
                     $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
                     $BHheaderoffset += 3;
                     $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
                     $BHheaderoffset += 2;
                     $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $info['szip']['BH'][] = $BHdataArray;
                 }
                 break;
             default:
                 break 2;
         }
     }
     return true;
 }
 public function Analyze()
 {
     $getid3 = $this->getid3;
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $szip_rkau = fread($getid3->fp, 6);
     // Magic bytes:  'SZ'."\x0A\x04"
     $getid3->info['fileformat'] = 'szip';
     $getid3->info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($szip_rkau, 4, 1));
     $getid3->info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($szip_rkau, 5, 1));
     while (!feof($getid3->fp)) {
         $next_block_id = fread($getid3->fp, 2);
         switch ($next_block_id) {
             case 'SZ':
                 // Note that szip files can be concatenated, this has the same effect as
                 // concatenating the files. this also means that global header blocks
                 // might be present between directory/data blocks.
                 fseek($getid3->fp, 4, SEEK_CUR);
                 break;
             case 'BH':
                 $bh_header_bytes = getid3_lib::BigEndian2Int(fread($getid3->fp, 3));
                 $bh_header_data = fread($getid3->fp, $bh_header_bytes);
                 $bh_header_offset = 0;
                 while (strpos($bh_header_data, "", $bh_header_offset) > 0) {
                     //filename as \0 terminated string  (empty string indicates end)
                     //owner as \0 terminated string (empty is same as last file)
                     //group as \0 terminated string (empty is same as last file)
                     //3 byte filelength in this block
                     //2 byte access flags
                     //4 byte creation time (like in unix)
                     //4 byte modification time (like in unix)
                     //4 byte access time (like in unix)
                     $bh_data_array['filename'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, ""));
                     $bh_header_offset += strlen($bh_data_array['filename']) + 1;
                     $bh_data_array['owner'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, ""));
                     $bh_header_offset += strlen($bh_data_array['owner']) + 1;
                     $bh_data_array['group'] = substr($bh_header_data, $bh_header_offset, strcspn($bh_header_data, ""));
                     $bh_header_offset += strlen($bh_data_array['group']) + 1;
                     $bh_data_array['filelength'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 3));
                     $bh_header_offset += 3;
                     $bh_data_array['access_flags'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 2));
                     $bh_header_offset += 2;
                     $bh_data_array['creation_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4));
                     $bh_header_offset += 4;
                     $bh_data_array['modification_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4));
                     $bh_header_offset += 4;
                     $bh_data_array['access_time'] = getid3_lib::BigEndian2Int(substr($bh_header_data, $bh_header_offset, 4));
                     $bh_header_offset += 4;
                     $getid3->info['szip']['BH'][] = $bh_data_array;
                 }
                 break;
             default:
                 break 2;
         }
     }
     return true;
 }
예제 #4
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AUheader = fread($this->getid3->fp, 8);
     $magic = '.snd';
     if (substr($AUheader, 0, 4) != $magic) {
         $info['error'][] = 'Expecting "' . getid3_lib::PrintHexBytes($magic) . '" (".snd") at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes(substr($AUheader, 0, 4)) . '"';
         return false;
     }
     // shortcut
     $info['au'] = array();
     $thisfile_au =& $info['au'];
     $info['fileformat'] = 'au';
     $info['audio']['dataformat'] = 'au';
     $info['audio']['bitrate_mode'] = 'cbr';
     $thisfile_au['encoding'] = 'ISO-8859-1';
     $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
     $AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8);
     $info['avdataoffset'] += $thisfile_au['header_length'];
     $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
     $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
     $thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
     $thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
     $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
     $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
     $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
     if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
         $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
     } else {
         unset($thisfile_au['bits_per_sample']);
     }
     $info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
     $info['audio']['channels'] = $thisfile_au['channels'];
     if ($info['avdataoffset'] + $thisfile_au['data_size'] > $info['avdataend']) {
         $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['data_size'] . '" bytes of audio data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"';
     }
     $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
     $info['audio']['bitrate'] = $thisfile_au['data_size'] * 8 / $info['playtime_seconds'];
     return true;
 }
 function EBML2Int($EBMLstring)
 {
     // http://matroska.org/specs/
     // Element ID coded with an UTF-8 like system:
     // 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
     // 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
     // 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
     // Values with all x at 0 and 1 are reserved (hence the -2).
     // Data size, in octets, is also coded with an UTF-8 like system :
     // 1xxx xxxx                                                                              - value 0 to  2^7-2
     // 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
     // 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
     // 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
     // 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
     // 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
     // 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
     if (0x80 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x7f);
     } elseif (0x40 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x3f);
     } elseif (0x20 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x1f);
     } elseif (0x10 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0xf);
     } elseif (0x8 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x7);
     } elseif (0x4 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x3);
     } elseif (0x2 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x1);
     } elseif (0x1 & ord($EBMLstring[0])) {
         $EBMLstring[0] = chr(ord($EBMLstring[0]) & 0x0);
     } else {
         return false;
     }
     return getid3_lib::BigEndian2Int($EBMLstring);
 }
예제 #6
0
 function getid3_dts(&$fd, &$ThisFileInfo)
 {
     // Specs taken from "DTS Coherent Acoustics;Core and Extensions,  ETSI TS 102 114 V1.2.1 (2002-12)"
     // (http://pda.etsi.org/pda/queryform.asp)
     // With thanks to Gambit <*****@*****.**> http://mac.sourceforge.net/atl/
     $ThisFileInfo['fileformat'] = 'dts';
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $DTSheader = fread($fd, 16);
     $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4));
     if ($ThisFileInfo['dts']['raw']['magic'] != 0x7ffe8001) {
         $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "0x' . str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT) . '"';
         unset($ThisFileInfo['fileformat']);
         unset($ThisFileInfo['dts']);
         return false;
     }
     $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
     $bsOffset = 0;
     $ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5));
     $bsOffset += 5;
     $ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7));
     $bsOffset += 7;
     $ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14));
     $bsOffset += 14;
     $ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6));
     $bsOffset += 6;
     $ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5));
     $bsOffset += 5;
     $ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3));
     $bsOffset += 3;
     $ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     if ($ThisFileInfo['dts']['flags']['crc_present']) {
         $ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16));
         $bsOffset += 16;
     }
     $ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
     $ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
     $ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
     $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
     $ThisFileInfo['dts']['flags']['lossless'] = $ThisFileInfo['dts']['raw']['bitrate'] == 31 ? true : false;
     $ThisFileInfo['dts']['bitrate_mode'] = $ThisFileInfo['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr';
     $ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
     $ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
     $ThisFileInfo['audio']['dataformat'] = 'dts';
     $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless'];
     $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode'];
     $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample'];
     $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate'];
     $ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels'];
     $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate'];
     if (isset($ThisFileInfo['avdataend'])) {
         $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
     }
     return true;
 }
예제 #7
0
 function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo)
 {
     // shortcuts
     $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion'];
     $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenamelong'])) {
         unset($parsedFrame['framenamelong']);
     }
     $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenameshort'])) {
         unset($parsedFrame['framenameshort']);
     }
     if ($id3v2_majorversion >= 3) {
         // frame flags are not part of the ID3v2.2 standard
         if ($id3v2_majorversion == 3) {
             //    Frame Header Flags
             //    %abc00000 %ijk00000
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // c - Read only
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80);
             // i - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // j - Encryption
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20);
             // k - Grouping identity
         } elseif ($id3v2_majorversion == 4) {
             //    Frame Header Flags
             //    %0abc0000 %0h00kmnp
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000);
             // c - Read only
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // h - Grouping identity
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8);
             // k - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4);
             // m - Encryption
             $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2);
             // n - Unsynchronisation
             $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1);
             // p - Data length indicator
             // Frame-level de-unsynchronisation - ID3v2.4
             if ($parsedFrame['flags']['Unsynchronisation']) {
                 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
             }
         }
         //    Frame-level de-compression
         if ($parsedFrame['flags']['compression']) {
             $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
             if (!function_exists('gzuncompress')) {
                 $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             } elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
                 $parsedFrame['data'] = $decompresseddata;
             } else {
                 $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             }
         }
     }
     if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) {
         $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion';
         switch ($parsedFrame['frame_name']) {
             case 'WCOM':
                 $warning .= ' (this is known to happen with files tagged by RioPort)';
                 break;
             default:
                 break;
         }
         $ThisFileInfo['warning'][] = $warning;
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') {
         // 4.1   UFI  Unique file identifier
         //   There may be more than one 'UFID' frame in a tag,
         //   but only one with the same 'Owner identifier'.
         // <Header for 'Unique file identifier', ID: 'UFID'>
         // Owner identifier        <text string> $00
         // Identifier              <up to 64 bytes binary data>
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         $parsedFrame['ownerid'] = $frame_idstring;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') {
         // 4.2.2 TXX  User defined text information frame
         //   There may be more than one 'TXXX' frame in each tag,
         //   but only one with the same description.
         // <Header for 'User defined text information frame', ID: 'TXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // Value             <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']));
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['frame_name'][0] == 'T') {
         // 4.2. T??[?] Text information frame
         //   There may only be one text information frame of its kind in an tag.
         // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
         // excluding 'TXXX' described in 4.2.6.>
         // Text encoding                $xx
         // Information                  <text string(s) according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             // remove possible terminating \x00 (put by encoding id or software bug)
             $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
             if ($string[strlen($string) - 1] == "") {
                 $string = substr($string, 0, strlen($string) - 1);
             }
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
             unset($string);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') {
         // 4.3.2 WXX  User defined URL link frame
         //   There may be more than one 'WXXX' frame in each tag,
         //   but only one with the same description
         // <Header for 'User defined URL link frame', ID: 'WXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // URL               <text string>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         if ($frame_terminatorpos) {
             // there are null bytes after the data - this is not according to spec
             // only use data up to first null byte
             $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
         } else {
             // no null bytes following data, just use all data
             $frame_urldata = (string) $parsedFrame['data'];
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['url'] = $frame_urldata;
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['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>
         $parsedFrame['url'] = trim($parsedFrame['data']);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') {
         // 4.4  IPL  Involved people list (ID3v2.2 only)
         //   There may only be one 'IPL' frame in each tag
         // <Header for 'User defined URL link frame', ID: 'IPL'>
         // Text encoding     $xx
         // People list strings    <textstrings>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['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($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
             $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
             $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
         $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
         $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
         $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
         $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
         $parsedFrame['data'] = substr($parsedFrame['data'], 10);
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         }
         $reference_counter = 0;
         while (strlen($deviationbitstream) > 0) {
             $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
             $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
             $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
             $reference_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $timestamp_counter = 0;
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
                 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
             }
             $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
             $timestamp_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') {
         // 4.9   ULT  Unsynchronised lyric/text transcription
         //   There may be more than one 'Unsynchronised lyrics/text transcription' frame
         //   in each tag, but only one with the same language and content descriptor.
         // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Content descriptor   <text string according to encoding> $00 (00)
         // Lyrics/text          <full text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = $parsedFrame['data'];
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') {
         // 4.10  SLT  Synchronised lyric/text
         //   There may be more than one 'SYLT' frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Time stamp format    $xx
         //   $01  (32-bit value) MPEG frames from beginning of file
         //   $02  (32-bit value) milliseconds from beginning of file
         // Content type         $xx
         // Content descriptor   <text string according to encoding> $00 (00)
         //   Terminated text to be synced (typically a syllable)
         //   Sync identifier (terminator to above string)   $00 (00)
         //   Time stamp                                     $xx (xx ...)
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $timestampindex = 0;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
             if ($frame_terminatorpos === false) {
                 $frame_remainingdata = '';
             } else {
                 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                     $frame_terminatorpos++;
                     // strpos() fooled because 2nd byte of Unicode chars are often 0x00
                 }
                 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
                 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
                 if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) {
                     // timestamp probably omitted for first data item
                 } else {
                     $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
                     $frame_remainingdata = substr($frame_remainingdata, 4);
                 }
                 $timestampindex++;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['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($parsedFrame['data']) < 5) {
             $ThisFileInfo['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset'];
         } else {
             $frame_offset = 0;
             $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
                 $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             }
             $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
             $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                 $frame_terminatorpos++;
                 // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             $parsedFrame['language'] = $frame_language;
             $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = $frame_text;
             if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                 $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
             }
         }
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') {
         // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
         //   There may be more than one 'RVA2' frame in each tag,
         //   but only one with the same identification string
         // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
         // Identification          <text string> $00
         //   The 'identification' string is used to identify the situation and/or
         //   device where this adjustment should apply. The following is then
         //   repeated for every channel:
         // Type of channel         $xx
         // Volume adjustment       $xx xx
         // Bits representing peak  $xx
         // Peak volume             $xx (xx ...)
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         $parsedFrame['description'] = $frame_idstring;
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $parsedFrame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid;
             $parsedFrame[$frame_channeltypeid]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
             $parsedFrame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true);
             // 16-bit signed
             $frame_offset += 2;
             $parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8);
             $parsedFrame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
             $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['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(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
         $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
         $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
         $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['right'] === false) {
             $parsedFrame['volumechange']['right'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['left'] === false) {
             $parsedFrame['volumechange']['left'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         if ($id3v2_majorversion == 3) {
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
                 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
                 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['rightrear'] === false) {
                     $parsedFrame['volumechange']['rightrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['leftrear'] === false) {
                     $parsedFrame['volumechange']['leftrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
                 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['center'] === false) {
                     $parsedFrame['volumechange']['center'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
                 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['bass'] === false) {
                     $parsedFrame['volumechange']['bass'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') {
         // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
         //   There may be more than one 'EQU2' frame in each tag,
         //   but only one with the same identification string
         // <Header of 'Equalisation (2)', ID: 'EQU2'>
         // Interpolation method  $xx
         //   $00  Band
         //   $01  Linear
         // Identification        <text string> $00
         //   The following is then repeated for every adjustment point
         // Frequency          $xx xx
         // Volume adjustment  $xx xx
         $frame_offset = 0;
         $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $parsedFrame['description'] = $frame_idstring;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         while (strlen($frame_remainingdata)) {
             $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
             $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
             $frame_remainingdata = substr($frame_remainingdata, 4);
         }
         $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
         $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
         $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata) > 0) {
             $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
             $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
             $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
             $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
             $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
             if ($parsedFrame[$frame_frequency]['incdec'] === false) {
                 $parsedFrame[$frame_frequency]['adjustment'] *= -1;
             }
             $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') {
         // 4.15  PIC  Attached picture
         //   There may be several pictures attached to one file,
         //   each in their individual 'APIC' frame, but only one
         //   with the same content descriptor
         // <Header for 'Attached picture', ID: 'APIC'>
         // Text encoding      $xx
         // ID3v2.3+ => MIME type          <text string> $00
         // ID3v2.2  => Image format       $xx xx xx
         // Picture type       $xx
         // Description        <text string according to encoding> $00 (00)
         // Picture data       <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
             if (strtolower($frame_imagetype) == 'ima') {
                 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
                 // MIME type instead of 3-char ID3v2.2-format image type  (thanks xbhoffØpacbell*net)
                 $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
                 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
                 if (ord($frame_mimetype) === 0) {
                     $frame_mimetype = '';
                 }
                 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
                 if ($frame_imagetype == 'JPEG') {
                     $frame_imagetype = 'JPG';
                 }
                 $frame_offset = $frame_terminatorpos + strlen("");
             } else {
                 $frame_offset += 3;
             }
         }
         if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
             $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_mimetype) === 0) {
                 $frame_mimetype = '';
             }
             $frame_offset = $frame_terminatorpos + strlen("");
         }
         $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if ($id3v2_majorversion == 2) {
             $parsedFrame['imagetype'] = $frame_imagetype;
         } else {
             $parsedFrame['mime'] = $frame_mimetype;
         }
         $parsedFrame['picturetypeid'] = $frame_picturetype;
         $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data']);
         if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) {
             $parsedFrame['image_mime'] = 'image/' . getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
             if ($imagechunkcheck[0]) {
                 $parsedFrame['image_width'] = $imagechunkcheck[0];
             }
             if ($imagechunkcheck[1]) {
                 $parsedFrame['image_height'] = $imagechunkcheck[1];
             }
             $parsedFrame['image_bytes'] = strlen($parsedFrame['data']);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') {
         // 4.16  GEO  General encapsulated object
         //   There may be more than one 'GEOB' frame in each tag,
         //   but only one with the same content descriptor
         // <Header for 'General encapsulated object', ID: 'GEOB'>
         // Text encoding          $xx
         // MIME type              <text string> $00
         // Filename               <text string according to encoding> $00 (00)
         // Content description    <text string according to encoding> $00 (00)
         // Encapsulated object    <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_mimetype) === 0) {
             $frame_mimetype = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_filename) === 0) {
             $frame_filename = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['filename'] = $frame_filename;
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['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 ...)
         $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') {
         // 4.18  POP  Popularimeter
         //   There may be more than one 'POPM' frame in each tag,
         //   but only one with the same email address
         // <Header for 'Popularimeter', ID: 'POPM'>
         // Email to user   <text string> $00
         // Rating          $xx
         // Counter         $xx xx xx xx (xx ...)
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_emailaddress) === 0) {
             $frame_emailaddress = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         $parsedFrame['email'] = $frame_emailaddress;
         $parsedFrame['rating'] = $frame_rating;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['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;
         $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
         $frame_offset += 3;
         $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
         $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') {
         // 4.20  Encrypted meta frame (ID3v2.2 only)
         //   There may be more than one 'CRM' frame in a tag,
         //   but only one with the same 'owner identifier'
         // <Header for 'Encrypted meta frame', ID: 'CRM'>
         // Owner identifier      <textstring> $00 (00)
         // Content/explanation   <textstring> $00 (00)
         // Encrypted datablock   <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') {
         // 4.21  CRA  Audio encryption
         //   There may be more than one 'AENC' frames in a tag,
         //   but only one with the same 'Owner identifier'
         // <Header for 'Audio encryption', ID: 'AENC'>
         // Owner identifier   <text string> $00
         // Preview start      $xx xx
         // Preview length     $xx xx
         // Encryption info    <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid == '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['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_majorversion == 2) {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
         } else {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
             $frame_offset += 4;
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_url) === 0) {
             $frame_url = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['url'] = $frame_url;
         $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['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;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') {
         // 4.22  USER Terms of use (ID3v2.3+ only)
         //   There may be more than one 'Terms of use' frame in a tag,
         //   but only one with the same 'Language'
         // <Header for 'Terms of use frame', ID: 'USER'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // The actual text      <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') {
         // 4.23  OWNE Ownership frame (ID3v2.3+ only)
         //   There may only be one 'OWNE' frame in a tag
         // <Header for 'Ownership frame', ID: 'OWNE'>
         // Text encoding     $xx
         // Price paid        <text string> $00
         // Date of purch.    <text string>
         // Seller            <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
         $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
         $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
         $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
         if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
             $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
         }
         $frame_offset += 8;
         $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') {
         // 4.24  COMR Commercial frame (ID3v2.3+ only)
         //   There may be more than one 'commercial frame' in a tag,
         //   but no two may be identical
         // <Header for 'Commercial frame', ID: 'COMR'>
         // Text encoding      $xx
         // Price string       <text string> $00
         // Valid until        <text string>
         // Contact URL        <text string> $00
         // Received as        $xx
         // Name of seller     <text string according to encoding> $00 (00)
         // Description        <text string according to encoding> $00 (00)
         // Picture MIME type  <string> $00
         // Seller logo        <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rawpricearray = explode('/', $frame_pricestring);
         foreach ($frame_rawpricearray as $key => $val) {
             $frame_currencyid = substr($val, 0, 3);
             $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
             $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
         }
         $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
         $frame_offset += 8;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_sellername) === 0) {
             $frame_sellername = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['pricevaliduntil'] = $frame_datestring;
         $parsedFrame['contacturl'] = $frame_contacturl;
         $parsedFrame['receivedasid'] = $frame_receivedasid;
         $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
         $parsedFrame['sellername'] = $frame_sellername;
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['logo'] = $frame_sellerlogo;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') {
         // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
         //   There may be several 'ENCR' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Encryption method registration', ID: 'ENCR'>
         // Owner identifier    <text string> $00
         // Method symbol       $xx
         // Encryption data     <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') {
         // 4.26  GRID Group identification registration (ID3v2.3+ only)
         //   There may be several 'GRID' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Group ID registration', ID: 'GRID'>
         // Owner identifier      <text string> $00
         // Group symbol          $xx
         // Group dependent data  <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') {
         // 4.27  PRIV Private frame (ID3v2.3+ only)
         //   The tag may contain more than one 'PRIV' frame
         //   but only with different contents
         // <Header for 'Private frame', ID: 'PRIV'>
         // Owner identifier      <text string> $00
         // The private data      <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['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;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['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;
         $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['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;
         $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
         for ($i = 0; $i < $frame_indexpoints; $i++) {
             $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
             $frame_offset += $frame_bytesperpoint;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['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;
         $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
         $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
         $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
         $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
         $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
         $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
         $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
         $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
         $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
         $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
         $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
         $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
         $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
         $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
         $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
         $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
         $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
         $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
         $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
         unset($parsedFrame['data']);
     }
     return true;
 }
예제 #8
0
 function analyze($filename)
 {
     if (!empty($this->startup_error)) {
         return $this->error($this->startup_error);
     }
     if (!empty($this->startup_warning)) {
         $this->warning($this->startup_warning);
     }
     // init result array and set parameters
     $this->info = array();
     $this->info['GETID3_VERSION'] = GETID3_VERSION;
     // Check encoding/iconv support
     if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
         $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
         if (GETID3_OS_ISWINDOWS) {
             $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
         } else {
             $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
         }
         return $this->error($errormessage);
     }
     // Disable magic_quotes_runtime, if neccesary
     $old_magic_quotes_runtime = get_magic_quotes_runtime();
     // store current setting of magic_quotes_runtime
     if ($old_magic_quotes_runtime) {
         set_magic_quotes_runtime(0);
         // turn off magic_quotes_runtime
         if (get_magic_quotes_runtime()) {
             return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled');
         }
     }
     // remote files not supported
     if (preg_match('/^(ht|f)tp:\\/\\//', $filename)) {
         return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
     }
     // open local file
     if (!($fp = @fopen($filename, 'rb'))) {
         return $this->error('Could not open file "' . $filename . '"');
     }
     // set parameters
     $this->info['filesize'] = filesize($filename);
     // option_max_2gb_check
     if ($this->option_max_2gb_check) {
         // PHP doesn't support integers larger than 31-bit (~2GB)
         // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
         // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
         fseek($fp, 0, SEEK_END);
         if ($this->info['filesize'] != 0 && ftell($fp) == 0 || $this->info['filesize'] < 0 || ftell($fp) < 0) {
             unset($this->info['filesize']);
             fclose($fp);
             return $this->error('File is most likely larger than 2GB and is not supported by PHP');
         }
     }
     // set more parameters
     $this->info['avdataoffset'] = 0;
     $this->info['avdataend'] = $this->info['filesize'];
     $this->info['fileformat'] = '';
     // filled in later
     $this->info['audio']['dataformat'] = '';
     // filled in later, unset if not used
     $this->info['video']['dataformat'] = '';
     // filled in later, unset if not used
     $this->info['tags'] = array();
     // filled in later, unset if not used
     $this->info['error'] = array();
     // filled in later, unset if not used
     $this->info['warning'] = array();
     // filled in later, unset if not used
     $this->info['comments'] = array();
     // filled in later, unset if not used
     $this->info['encoding'] = $this->encoding;
     // required by id3v2 and iso modules - can be unset at the end if desired
     // set redundant parameters - might be needed in some include file
     $this->info['filename'] = basename($filename);
     $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
     $this->info['filenamepath'] = $this->info['filepath'] . '/' . $this->info['filename'];
     // handle ID3v2 tag - done first - already at beginning of file
     // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
     if ($this->option_tag_id3v2) {
         $GETID3_ERRORARRAY =& $this->info['warning'];
         if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.tag.id3v2.php', __FILE__, false)) {
             $tag = new getid3_id3v2($fp, $this->info);
         }
     } else {
         fseek($fp, 0, SEEK_SET);
         $header = fread($fp, 10);
         if (substr($header, 0, 3) == 'ID3') {
             $this->info['id3v2']['header'] = true;
             $this->info['id3v2']['majorversion'] = ord($header[3]);
             $this->info['id3v2']['minorversion'] = ord($header[4]);
             $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10;
             // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
             $this->info['id3v2']['tag_offset_start'] = 0;
             $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
             $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end'];
         }
     }
     // handle ID3v1 tag
     if ($this->option_tag_id3v1) {
         if (!@(include_once GETID3_INCLUDEPATH . 'module.tag.id3v1.php')) {
             return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
         }
         $tag = new getid3_id3v1($fp, $this->info);
     }
     // handle APE tag
     if ($this->option_tag_apetag) {
         if (!@(include_once GETID3_INCLUDEPATH . 'module.tag.apetag.php')) {
             return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
         }
         $tag = new getid3_apetag($fp, $this->info);
     }
     // handle lyrics3 tag
     if ($this->option_tag_lyrics3) {
         if (!@(include_once GETID3_INCLUDEPATH . 'module.tag.lyrics3.php')) {
             return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
         }
         $tag = new getid3_lyrics3($fp, $this->info);
     }
     // read 32 kb file data
     fseek($fp, $this->info['avdataoffset'], SEEK_SET);
     $formattest = fread($fp, 32774);
     // determine format
     $determined_format = $this->GetFileFormat($formattest, $filename);
     // unable to determine file format
     if (!$determined_format) {
         fclose($fp);
         return $this->error('unable to determine file format');
     }
     // check for illegal ID3 tags
     if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
         if ($determined_format['fail_id3'] === 'ERROR') {
             fclose($fp);
             return $this->error('ID3 tags not allowed on this file type.');
         } elseif ($determined_format['fail_id3'] === 'WARNING') {
             $this->info['warning'][] = 'ID3 tags not allowed on this file type.';
         }
     }
     // check for illegal APE tags
     if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
         if ($determined_format['fail_ape'] === 'ERROR') {
             fclose($fp);
             return $this->error('APE tags not allowed on this file type.');
         } elseif ($determined_format['fail_ape'] === 'WARNING') {
             $this->info['warning'][] = 'APE tags not allowed on this file type.';
         }
     }
     // set mime type
     $this->info['mime_type'] = $determined_format['mime_type'];
     // supported format signature pattern detected, but module deleted
     if (!file_exists(GETID3_INCLUDEPATH . $determined_format['include'])) {
         fclose($fp);
         return $this->error('Format not supported, module, ' . $determined_format['include'] . ', was removed.');
     }
     // module requires iconv support
     if (!function_exists('iconv') && @$determined_format['iconv_req']) {
         return $this->error('iconv support is required for this module (' . $determined_format['include'] . ').');
     }
     // include module
     include_once GETID3_INCLUDEPATH . $determined_format['include'];
     // instantiate module class
     $class_name = 'getid3_' . $determined_format['module'];
     if (!class_exists($class_name)) {
         return $this->error('Format not supported, module, ' . $determined_format['include'] . ', is corrupt.');
     }
     if (isset($determined_format['option'])) {
         $class = new $class_name($fp, $this->info, $determined_format['option']);
     } else {
         $class = new $class_name($fp, $this->info);
     }
     // close file
     fclose($fp);
     // process all tags - copy to 'tags' and convert charsets
     if ($this->option_tags_process) {
         $this->HandleAllTags();
     }
     // perform more calculations
     if ($this->option_extra_info) {
         $this->ChannelsBitratePlaytimeCalculations();
         $this->CalculateCompressionRatioVideo();
         $this->CalculateCompressionRatioAudio();
         $this->CalculateReplayGain();
         $this->ProcessAudioStreams();
     }
     // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
     if ($this->option_md5_data) {
         // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
         if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
             $this->getHashdata('md5');
         }
     }
     // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
     if ($this->option_sha1_data) {
         $this->getHashdata('sha1');
     }
     // remove undesired keys
     $this->CleanUp();
     // restore magic_quotes_runtime setting
     set_magic_quotes_runtime($old_magic_quotes_runtime);
     // return info array
     return $this->info;
 }
 function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData = false)
 {
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
     $FLVheader = fread($fd, 5);
     $ThisFileInfo['fileformat'] = 'flv';
     $ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3);
     $ThisFileInfo['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
     $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
     if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
         $ThisFileInfo['error'][] = 'Expecting "FLV" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . $ThisFileInfo['flv']['header']['signature'] . '"';
         unset($ThisFileInfo['flv']);
         unset($ThisFileInfo['fileformat']);
         return false;
     }
     $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x4);
     $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x1);
     $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
     $FLVheaderFrameLength = 9;
     if ($FrameSizeDataLength > $FLVheaderFrameLength) {
         fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
     }
     $Duration = 0;
     while (ftell($fd) + 1 < $ThisFileInfo['avdataend']) {
         //if (!$ThisFileInfo['flv']['header']['hasAudio'] || isset($ThisFileInfo['flv']['audio']['audioFormat'])) {
         //	if (!$ThisFileInfo['flv']['header']['hasVideo'] || isset($ThisFileInfo['flv']['video']['videoCodec'])) {
         //		break;
         //	}
         //}
         $ThisTagHeader = fread($fd, 16);
         $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
         $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
         $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
         $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
         $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
         $NextOffset = ftell($fd) - 1 + $DataLength;
         switch ($TagType) {
             case GETID3_FLV_TAG_AUDIO:
                 if (!isset($ThisFileInfo['flv']['audio']['audioFormat'])) {
                     $ThisFileInfo['flv']['audio']['audioFormat'] = $LastHeaderByte & 0x7;
                     $ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte & 0x30) / 0x10;
                     $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
                     $ThisFileInfo['flv']['audio']['audioType'] = ($LastHeaderByte & 0x80) / 0x80;
                 }
                 break;
             case GETID3_FLV_TAG_VIDEO:
                 if (!isset($ThisFileInfo['flv']['video']['videoCodec'])) {
                     $ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x7;
                     switch ($ThisFileInfo['flv']['video']['videoCodec']) {
                         case GETID3_FLV_VIDEO_H263:
                             $FLVvideoHeader = fread($fd, 11);
                             $PictureSizeType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2)) >> 7;
                             $PictureSizeType = $PictureSizeType & 0x7;
                             $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
                             switch ($PictureSizeType) {
                                 case 0:
                                     $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                     $PictureSizeEnc <<= 1;
                                     $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xff00) >> 8;
                                     $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
                                     $PictureSizeEnc <<= 1;
                                     $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xff00) >> 8;
                                     break;
                                 case 1:
                                     $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 4));
                                     $PictureSizeEnc <<= 1;
                                     $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xffff0000) >> 16;
                                     $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 4));
                                     $PictureSizeEnc <<= 1;
                                     $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xffff0000) >> 16;
                                     break;
                                 case 2:
                                     $ThisFileInfo['video']['resolution_x'] = 352;
                                     $ThisFileInfo['video']['resolution_y'] = 288;
                                     break;
                                 case 3:
                                     $ThisFileInfo['video']['resolution_x'] = 176;
                                     $ThisFileInfo['video']['resolution_y'] = 144;
                                     break;
                                 case 4:
                                     $ThisFileInfo['video']['resolution_x'] = 128;
                                     $ThisFileInfo['video']['resolution_y'] = 96;
                                     break;
                                 case 5:
                                     $ThisFileInfo['video']['resolution_x'] = 320;
                                     $ThisFileInfo['video']['resolution_y'] = 240;
                                     break;
                                 case 6:
                                     $ThisFileInfo['video']['resolution_x'] = 160;
                                     $ThisFileInfo['video']['resolution_y'] = 120;
                                     break;
                                 default:
                                     $ThisFileInfo['video']['resolution_x'] = 0;
                                     $ThisFileInfo['video']['resolution_y'] = 0;
                                     break;
                             }
                             break;
                         case GETID3_FLV_VIDEO_SCREEN:
                             $bits = new BitStreamReader(fread($fd, $DataLength));
                             $bits->seek(4, SEEK_CUR);
                             $ThisFileInfo['video']['resolution_x'] = $bits->getInt(12);
                             $bits->seek(4, SEEK_CUR);
                             $ThisFileInfo['video']['resolution_y'] = $bits->getInt(12);
                             break;
                         case GETID3_FLV_VIDEO_VP6:
                             $bits = new BitStreamReader(fread($fd, $DataLength));
                             $adjW = $bits->getInt(4);
                             $adjH = $bits->getInt(4);
                             $mode = $bits->getInt(1);
                             if ($mode === 0) {
                                 $bits->seek(15, SEEK_CUR);
                                 $ThisFileInfo['video']['resolution_y'] = $bits->getInt(8) * 16 - $adjH;
                                 $ThisFileInfo['video']['resolution_x'] = $bits->getInt(8) * 16 - $adjW;
                             }
                             break;
                         case GETID3_FLV_VIDEO_VP6ALPHA:
                             $bits = new BitStreamReader(fread($fd, $DataLength));
                             $adjW = $bits->getInt(4);
                             $adjH = $bits->getInt(4);
                             $mode = $bits->getInt(1);
                             // mode is for ? unknown ?
                             if ($mode === 0) {
                                 $bits->seek(39, SEEK_CUR);
                                 $ThisFileInfo['video']['resolution_y'] = $bits->getInt(8) * 16 - $adjH;
                                 $ThisFileInfo['video']['resolution_x'] = $bits->getInt(8) * 16 - $adjW;
                             }
                             break;
                         case GETID3_FLV_VIDEO_SCREENVIDEO_2:
                             $ThisFileInfo['video']['resolution_x'] = $bits->getInt(12);
                             $ThisFileInfo['video']['resolution_y'] = $bits->getInt(12);
                             break;
                     }
                 }
                 break;
                 // Meta tag
             // Meta tag
             case GETID3_FLV_TAG_META:
                 fseek($fd, -1, SEEK_CUR);
                 $reader = new AMFReader(fread($fd, $DataLength));
                 $eventName = $reader->getItem();
                 if ($eventName == 'onMetaData') {
                     $ThisFileInfo['meta']['onMetaData'] = $reader->getItem();
                 }
                 unset($reader);
                 $ThisFileInfo['video']['frame_rate'] = @$ThisFileInfo['meta']['onMetaData']['framerate'];
                 $ThisFileInfo['video']['resolution_x'] = @$ThisFileInfo['meta']['onMetaData']['width'];
                 $ThisFileInfo['video']['resolution_y'] = @$ThisFileInfo['meta']['onMetaData']['height'];
                 break;
             default:
                 // noop
                 break;
         }
         if ($Timestamp > $Duration) {
             $Duration = $Timestamp;
         }
         fseek($fd, $NextOffset, SEEK_SET);
     }
     if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
         $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
     }
     if ($ThisFileInfo['flv']['header']['hasAudio']) {
         $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
         $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
         $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
         $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1;
         // 0=mono,1=stereo
         $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['flv']['audio']['audioFormat'] ? false : true;
         // 0=uncompressed
         $ThisFileInfo['audio']['dataformat'] = 'flv';
     }
     if (@$ThisFileInfo['flv']['header']['hasVideo']) {
         $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
         $ThisFileInfo['video']['dataformat'] = 'flv';
         $ThisFileInfo['video']['lossless'] = false;
     }
     return true;
 }
예제 #10
0
 public function Analyze()
 {
     $getid3 = $this->getid3;
     $getid3->include_module('audio-video.riff');
     // Magic bytes - 'LPAC'
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $lpac_header = fread($getid3->fp, 14);
     $getid3->info['avdataoffset'] += 14;
     $getid3->info['lpac'] = array();
     $info_lpac =& $getid3->info['lpac'];
     $getid3->info['fileformat'] = 'lpac';
     $getid3->info['audio']['dataformat'] = 'lpac';
     $getid3->info['audio']['lossless'] = true;
     $getid3->info['audio']['bitrate_mode'] = 'vbr';
     $info_lpac['file_version'] = getid3_lib::BigEndian2Int($lpac_header[4]);
     $flags['audio_type'] = getid3_lib::BigEndian2Int($lpac_header[5]);
     $info_lpac['total_samples'] = getid3_lib::BigEndian2Int(substr($lpac_header, 6, 4));
     $flags['parameters'] = getid3_lib::BigEndian2Int(substr($lpac_header, 10, 4));
     $info_lpac['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
     $info_lpac['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4);
     $info_lpac['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2);
     $info_lpac['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1);
     if ($info_lpac['flags']['24_bit'] && $info_lpac['flags']['16_bit']) {
         $getid3->warning('24-bit and 16-bit flags cannot both be set');
     }
     $info_lpac['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
     $info_lpac['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000);
     $info_lpac['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256;
     $info_lpac['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000);
     $info_lpac['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000);
     $info_lpac['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000);
     $info_lpac['quantization'] = ($flags['parameters'] & 0x1f00) >> 8;
     $info_lpac['max_prediction_order'] = $flags['parameters'] & 0x3f;
     if ($info_lpac['flags']['fast_compress'] && $info_lpac['max_prediction_order'] != 3) {
         $getid3->warning('max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info_lpac['max_prediction_order'] . '"');
     }
     switch ($info_lpac['file_version']) {
         case 6:
             if ($info_lpac['flags']['adaptive_quantization']) {
                 $getid3->warning('adaptive_quantization expected to be false in LPAC file stucture v6, actually true');
             }
             if ($info_lpac['quantization'] != 20) {
                 $getid3->warning('Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info_lpac['flags']['Q']);
             }
             break;
         default:
             //$getid3->warning('This version of getID3() only supports LPAC file format version 6, this file is version '.$info_lpac['file_version'].' - please report to info@getid3.org');
             break;
     }
     // Clone getid3 - messing with something - better safe than sorry
     $clone = clone $getid3;
     // Analyze clone by fp
     $riff = new getid3_riff($clone);
     $riff->Analyze();
     // Import from clone and destroy
     $getid3->info['avdataoffset'] = $clone->info['avdataoffset'];
     $getid3->info['riff'] = $clone->info['riff'];
     //$info_lpac['comments']['comment'] = $clone->info['comments'];
     $getid3->info['audio']['sample_rate'] = $clone->info['audio']['sample_rate'];
     $getid3->warnings($clone->warnings());
     unset($clone);
     $getid3->info['audio']['channels'] = $info_lpac['flags']['stereo'] ? 2 : 1;
     if ($info_lpac['flags']['24_bit']) {
         $getid3->info['audio']['bits_per_sample'] = $getid3->info['riff']['audio'][0]['bits_per_sample'];
     } elseif ($info_lpac['flags']['16_bit']) {
         $getid3->info['audio']['bits_per_sample'] = 16;
     } else {
         $getid3->info['audio']['bits_per_sample'] = 8;
     }
     if ($info_lpac['flags']['fast_compress']) {
         // fast
         $getid3->info['audio']['encoder_options'] = '-1';
     } else {
         switch ($info_lpac['max_prediction_order']) {
             case 20:
                 // simple
                 $getid3->info['audio']['encoder_options'] = '-2';
                 break;
             case 30:
                 // medium
                 $getid3->info['audio']['encoder_options'] = '-3';
                 break;
             case 40:
                 // high
                 $getid3->info['audio']['encoder_options'] = '-4';
                 break;
             case 60:
                 // extrahigh
                 $getid3->info['audio']['encoder_options'] = '-5';
                 break;
         }
     }
     $getid3->info['playtime_seconds'] = $info_lpac['total_samples'] / $getid3->info['audio']['sample_rate'];
     $getid3->info['audio']['bitrate'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['playtime_seconds'];
     return true;
 }
예제 #11
0
 function Analyze()
 {
     $info =& $this->getid3->info;
     // shortcut
     $info['midi']['raw'] = array();
     $thisfile_midi =& $info['midi'];
     $thisfile_midi_raw =& $thisfile_midi['raw'];
     $info['fileformat'] = 'midi';
     $info['audio']['dataformat'] = 'midi';
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $MIDIdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size());
     $offset = 0;
     $MIDIheaderID = substr($MIDIdata, $offset, 4);
     // 'MThd'
     if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) {
         $info['error'][] = 'Expecting "' . getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTHD) . '" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes($MIDIheaderID) . '"';
         unset($info['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
     $offset += 4;
     $thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
         while (strlen($MIDIdata) - $offset < 8) {
             $MIDIdata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size());
         }
         $trackID = substr($MIDIdata, $offset, 4);
         $offset += 4;
         if ($trackID == GETID3_MIDI_MAGIC_MTRK) {
             $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
             $offset += 4;
             // $thisfile_midi['tracks'][$i]['size'] = $tracksize;
             $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
             $offset += $tracksize;
         } else {
             $info['error'][] = 'Expecting "' . getid3_lib::PrintHexBytes(GETID3_MIDI_MAGIC_MTRK) . '" at ' . ($offset - 4) . ', found "' . getid3_lib::PrintHexBytes($trackID) . '" instead';
             return false;
         }
     }
     if (!isset($trackdataarray) || !is_array($trackdataarray)) {
         $info['error'][] = 'Cannot find MIDI track information';
         unset($thisfile_midi);
         unset($info['fileformat']);
         return false;
     }
     if ($this->scanwholefile) {
         // this can take quite a long time, so have the option to bypass it if speed is very important
         $thisfile_midi['totalticks'] = 0;
         $info['playtime_seconds'] = 0;
         $CurrentMicroSecondsPerBeat = 500000;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $CurrentBeatsPerMinute = 120;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $MicroSecondsPerQuarterNoteAfter = array();
         foreach ($trackdataarray as $tracknumber => $trackdata) {
             $eventsoffset = 0;
             $LastIssuedMIDIcommand = 0;
             $LastIssuedMIDIchannel = 0;
             $CumulativeDeltaTime = 0;
             $TicksAtCurrentBPM = 0;
             while ($eventsoffset < strlen($trackdata)) {
                 $eventid = 0;
                 if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
                     $eventid = count($MIDIevents[$tracknumber]);
                 }
                 $deltatime = 0;
                 for ($i = 0; $i < 4; $i++) {
                     $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
                     $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7f);
                     if ($deltatimebyte & 0x80) {
                         // another byte follows
                     } else {
                         break;
                     }
                 }
                 $CumulativeDeltaTime += $deltatime;
                 $TicksAtCurrentBPM += $deltatime;
                 $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
                 $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
                 if ($MIDI_event_channel & 0x80) {
                     // OK, normal event - MIDI command has MSB set
                     $LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
                     $LastIssuedMIDIchannel = $MIDI_event_channel & 0xf;
                 } else {
                     // running event - assume last command
                     $eventsoffset--;
                 }
                 $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
                 $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
                 if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x8) {
                     // Note off (key is released)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x9) {
                     // Note on (key is pressed)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xa) {
                     // Key after-touch
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xb) {
                     // Control Change
                     $controllernum = ord(substr($trackdata, $eventsoffset++, 1));
                     $newvalue = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xc) {
                     // Program (patch) change
                     $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
                     $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
                     if ($tracknumber == 10) {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
                     } else {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
                     }
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xd) {
                     // Channel after-touch
                     $channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xe) {
                     // Pitch wheel change (2000H is normal or no change)
                     $changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $pitchwheelchange = ($changeMSB & 0x7f) << 7 & ($changeLSB & 0x7f);
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xf && $MIDIevents[$tracknumber][$eventid]['channel'] == 0xf) {
                     $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
                     $eventsoffset += $METAeventLength;
                     switch ($METAeventCommand) {
                         case 0x0:
                             // Set track sequence number
                             $track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
                             break;
                         case 0x1:
                             // Text: generic
                             $text_generic = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
                             $thisfile_midi['comments']['comment'][] = $text_generic;
                             break;
                         case 0x2:
                             // Text: copyright
                             $text_copyright = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
                             $thisfile_midi['comments']['copyright'][] = $text_copyright;
                             break;
                         case 0x3:
                             // Text: track name
                             $text_trackname = substr($METAeventData, 0, $METAeventLength);
                             $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
                             break;
                         case 0x4:
                             // Text: track instrument name
                             $text_instrument = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
                             break;
                         case 0x5:
                             // Text: lyrics
                             $text_lyrics = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
                             if (!isset($thisfile_midi['lyrics'])) {
                                 $thisfile_midi['lyrics'] = '';
                             }
                             $thisfile_midi['lyrics'] .= $text_lyrics . "\n";
                             break;
                         case 0x6:
                             // Text: marker
                             $text_marker = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
                             break;
                         case 0x7:
                             // Text: cue point
                             $text_cuepoint = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
                             break;
                         case 0x2f:
                             // End Of Track
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
                             break;
                         case 0x51:
                             // Tempo: microseconds / quarter note
                             $CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             if ($CurrentMicroSecondsPerBeat == 0) {
                                 $info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
                                 return false;
                             }
                             $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
                             $CurrentBeatsPerMinute = 1000000 / $CurrentMicroSecondsPerBeat * 60;
                             $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
                             $TicksAtCurrentBPM = 0;
                             break;
                         case 0x58:
                             // Time signature
                             $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData[0]);
                             $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData[1]));
                             // $02 -> x/4, $03 -> x/8, etc
                             $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData[2]);
                             // number of 32nd notes to the quarter note
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote']   = $timesig_32inqnote;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator']   = $timesig_numerator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text']        = $timesig_numerator.'/'.$timesig_denominator;
                             $thisfile_midi['timesignature'][] = $timesig_numerator . '/' . $timesig_denominator;
                             break;
                         case 0x59:
                             // Keysignature
                             $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData[0]);
                             if ($keysig_sharpsflats & 0x80) {
                                 // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
                                 $keysig_sharpsflats -= 256;
                             }
                             $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData[1]);
                             // 0 -> major, 1 -> minor
                             $keysigs = array(-7 => 'Cb', -6 => 'Gb', -5 => 'Db', -4 => 'Ab', -3 => 'Eb', -2 => 'Bb', -1 => 'F', 0 => 'C', 1 => 'G', 2 => 'D', 3 => 'A', 4 => 'E', 5 => 'B', 6 => 'F#', 7 => 'C#');
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor']  = (bool) $keysig_majorminor;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text']   = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
                             // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
                             $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats] . ' ' . ((bool) $keysig_majorminor ? 'minor' : 'major');
                             break;
                         case 0x7f:
                             // Sequencer specific information
                             $custom_data = substr($METAeventData, 0, $METAeventLength);
                             break;
                         default:
                             $info['warning'][] = 'Unhandled META Event Command: ' . $METAeventCommand;
                             break;
                     }
                 } else {
                     $info['warning'][] = 'Unhandled MIDI Event ID: ' . $MIDIevents[$tracknumber][$eventid]['eventid'] . ' + Channel ID: ' . $MIDIevents[$tracknumber][$eventid]['channel'];
                 }
             }
             if ($tracknumber > 0 || count($trackdataarray) == 1) {
                 $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
             }
         }
         $previoustickoffset = null;
         ksort($MicroSecondsPerQuarterNoteAfter);
         foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
             if (is_null($previoustickoffset)) {
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
                 continue;
             }
             if ($thisfile_midi['totalticks'] > $tickoffset) {
                 if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                     $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                     return false;
                 }
                 $info['playtime_seconds'] += ($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($prevmicrosecondsperbeat / 1000000);
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
             }
         }
         if ($thisfile_midi['totalticks'] > $previoustickoffset) {
             if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                 $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                 return false;
             }
             $info['playtime_seconds'] += ($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($microsecondsperbeat / 1000000);
         }
     }
     if (!empty($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (!empty($thisfile_midi['lyrics'])) {
         $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
     }
     return true;
 }
예제 #12
0
 /**
  * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
  * External usage: audio.ogg
  */
 public function parsePICTURE()
 {
     $info =& $this->getid3->info;
     $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
     $picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
     $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
     $descr_length = getid3_lib::BigEndian2Int($this->fread(4));
     if ($descr_length) {
         $picture['description'] = $this->fread($descr_length);
     }
     $picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
     $picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
     $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
     $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
     $picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
     if ($picture['image_mime'] == '-->') {
         $picture['data'] = $this->fread($picture['datalength']);
     } else {
         $picture['data'] = $this->saveAttachment(str_replace('/', '_', $picture['picturetype']) . '_' . $this->ftell(), $this->ftell(), $picture['datalength'], $picture['image_mime']);
     }
     $info['flac']['PICTURE'][] = $picture;
     return true;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'swf';
     $info['video']['dataformat'] = 'swf';
     // http://www.openswf.org/spec/SWFfileformat.html
     $this->fseek($info['avdataoffset']);
     $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']);
     // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
     $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
     switch ($info['swf']['header']['signature']) {
         case 'FWS':
             $info['swf']['header']['compressed'] = false;
             break;
         case 'CWS':
             $info['swf']['header']['compressed'] = true;
             break;
         default:
             $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes($info['swf']['header']['signature']) . '"';
             unset($info['swf']);
             unset($info['fileformat']);
             return false;
             break;
     }
     $info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
     $info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
     if ($info['swf']['header']['compressed']) {
         $SWFHead = substr($SWFfileData, 0, 8);
         $SWFfileData = substr($SWFfileData, 8);
         if ($decompressed = @gzuncompress($SWFfileData)) {
             $SWFfileData = $SWFHead . $decompressed;
         } else {
             $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)';
             return false;
         }
     }
     $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3;
     $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8);
     $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT);
     for ($i = 1; $i < $FrameSizeDataLength; $i++) {
         $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
     }
     list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
     $info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
     $info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
     // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
     // Next in the header is the frame rate, which is kind of weird.
     // It is supposed to be stored as a 16bit integer, but the first byte
     // (or last depending on how you look at it) is completely ignored.
     // Example: 0x000C  ->  0x0C  ->  12     So the frame rate is 12 fps.
     // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
     $info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
     $info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
     $info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
     $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
     $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) {
         $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
     }
     //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
     // SWF tags
     $CurrentOffset = 12 + $FrameSizeDataLength;
     $SWFdataLength = strlen($SWFfileData);
     while ($CurrentOffset < $SWFdataLength) {
         //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
         $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
         $TagID = ($TagIDTagLength & 0xfffc) >> 6;
         $TagLength = $TagIDTagLength & 0x3f;
         $CurrentOffset += 2;
         if ($TagLength == 0x3f) {
             $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
             $CurrentOffset += 4;
         }
         unset($TagData);
         $TagData['offset'] = $CurrentOffset;
         $TagData['size'] = $TagLength;
         $TagData['id'] = $TagID;
         $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
         switch ($TagID) {
             case 0:
                 // end of movie
                 break 2;
             case 9:
                 // Set background color
                 //$info['swf']['tags'][] = $TagData;
                 $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
                 break;
             default:
                 if ($this->ReturnAllTagData) {
                     $info['swf']['tags'][] = $TagData;
                 }
                 break;
         }
         $CurrentOffset += $TagLength;
     }
     return true;
 }
예제 #14
0
 public static function MPEGaudioHeaderDecode($Header4Bytes)
 {
     // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
     // A - Frame sync (all bits set)
     // B - MPEG Audio version ID
     // C - Layer description
     // D - Protection bit
     // E - Bitrate index
     // F - Sampling rate frequency index
     // G - Padding bit
     // H - Private bit
     // I - Channel Mode
     // J - Mode extension (Only if Joint stereo)
     // K - Copyright
     // L - Original
     // M - Emphasis
     if (strlen($Header4Bytes) != 4) {
         return false;
     }
     $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xffe0) >> 4;
     $MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3;
     //    BB
     $MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x6) >> 1;
     //      CC
     $MPEGrawHeader['protection'] = ord($Header4Bytes[1]) & 0x1;
     //        D
     $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xf0) >> 4;
     // EEEE
     $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0xc) >> 2;
     //     FF
     $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x2) >> 1;
     //       G
     $MPEGrawHeader['private'] = ord($Header4Bytes[2]) & 0x1;
     //        H
     $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xc0) >> 6;
     // II
     $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4;
     //   JJ
     $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x8) >> 3;
     //     K
     $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x4) >> 2;
     //      L
     $MPEGrawHeader['emphasis'] = ord($Header4Bytes[3]) & 0x3;
     //       MM
     return $MPEGrawHeader;
 }
예제 #15
0
 function TIFFendian2Int($bytestring, $byteorder)
 {
     if ($byteorder == 'Intel') {
         return getid3_lib::LittleEndian2Int($bytestring);
     } elseif ($byteorder == 'Motorola') {
         return getid3_lib::BigEndian2Int($bytestring);
     }
     return false;
 }
 public function getBit()
 {
     $result = getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1;
     $this->skipBits(1);
     return $result;
 }
예제 #17
0
 public function analyze($filename, $filesize = null, $original_filename = '')
 {
     try {
         if (!$this->openfile($filename, $filesize)) {
             return $this->info;
         }
         // Handle tags
         foreach (array('id3v2' => 'id3v2', 'id3v1' => 'id3v1', 'apetag' => 'ape', 'lyrics3' => 'lyrics3') as $tag_name => $tag_key) {
             $option_tag = 'option_tag_' . $tag_name;
             if ($this->{$option_tag}) {
                 $this->include_module('tag.' . $tag_name);
                 try {
                     $tag_class = 'getid3_' . $tag_name;
                     $tag = new $tag_class($this);
                     $tag->Analyze();
                 } catch (getid3_exception $e) {
                     throw $e;
                 }
             }
         }
         if (isset($this->info['id3v2']['tag_offset_start'])) {
             $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
         }
         foreach (array('id3v1' => 'id3v1', 'apetag' => 'ape', 'lyrics3' => 'lyrics3') as $tag_name => $tag_key) {
             if (isset($this->info[$tag_key]['tag_offset_start'])) {
                 $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
             }
         }
         // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
         if (!$this->option_tag_id3v2) {
             fseek($this->fp, 0);
             $header = fread($this->fp, 10);
             if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
                 $this->info['id3v2']['header'] = true;
                 $this->info['id3v2']['majorversion'] = ord($header[3]);
                 $this->info['id3v2']['minorversion'] = ord($header[4]);
                 $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10;
                 // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
             }
         }
         // read 32 kb file data
         fseek($this->fp, $this->info['avdataoffset']);
         $formattest = fread($this->fp, 32774);
         // determine format
         $determined_format = $this->GetFileFormat($formattest, $original_filename ? $original_filename : $filename);
         // unable to determine file format
         if (!$determined_format) {
             fclose($this->fp);
             return $this->error('unable to determine file format');
         }
         // check for illegal ID3 tags
         if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
             if ($determined_format['fail_id3'] === 'ERROR') {
                 fclose($this->fp);
                 return $this->error('ID3 tags not allowed on this file type.');
             } elseif ($determined_format['fail_id3'] === 'WARNING') {
                 $this->warning('ID3 tags not allowed on this file type.');
             }
         }
         // check for illegal APE tags
         if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
             if ($determined_format['fail_ape'] === 'ERROR') {
                 fclose($this->fp);
                 return $this->error('APE tags not allowed on this file type.');
             } elseif ($determined_format['fail_ape'] === 'WARNING') {
                 $this->warning('APE tags not allowed on this file type.');
             }
         }
         // set mime type
         $this->info['mime_type'] = $determined_format['mime_type'];
         // supported format signature pattern detected, but module deleted
         if (!file_exists(GETID3_INCLUDEPATH . $determined_format['include'])) {
             fclose($this->fp);
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" was removed.');
         }
         // module requires iconv support
         // Check encoding/iconv support
         if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
             $errormessage = 'iconv() support is required for this module (' . $determined_format['include'] . ') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
             if (GETID3_OS_ISWINDOWS) {
                 $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
             } else {
                 $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
             }
             return $this->error($errormessage);
         }
         // include module
         include_once GETID3_INCLUDEPATH . $determined_format['include'];
         // instantiate module class
         $class_name = 'getid3_' . $determined_format['module'];
         if (!class_exists($class_name)) {
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" is corrupt.');
         }
         $class = new $class_name($this);
         $class->Analyze();
         unset($class);
         // close file
         fclose($this->fp);
         // process all tags - copy to 'tags' and convert charsets
         if ($this->option_tags_process) {
             $this->HandleAllTags();
         }
         // perform more calculations
         if ($this->option_extra_info) {
             $this->ChannelsBitratePlaytimeCalculations();
             $this->CalculateCompressionRatioVideo();
             $this->CalculateCompressionRatioAudio();
             $this->CalculateReplayGain();
             $this->ProcessAudioStreams();
         }
         // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->option_md5_data) {
             // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
             if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
                 $this->getHashdata('md5');
             }
         }
         // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->option_sha1_data) {
             $this->getHashdata('sha1');
         }
         // remove undesired keys
         $this->CleanUp();
     } catch (Exception $e) {
         $this->error('Caught exception: ' . $e->getMessage());
     }
     // return info array
     return $this->info;
 }
예제 #18
0
 private function FLACparsePICTURE($meta_data_block_data)
 {
     $getid3 = $this->getid3;
     $picture =& $getid3->info['flac']['PICTURE'][sizeof($getid3->info['flac']['PICTURE']) - 1];
     $offset = 0;
     $picture['type'] = $this->FLACpictureTypeLookup(getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)));
     $offset += 4;
     $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
     $offset += $length;
     $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['description'] = substr($meta_data_block_data, $offset, $length);
     $offset += $length;
     $picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
     $offset += 4;
     $picture['image_data'] = substr($meta_data_block_data, $offset, $length);
     $offset += $length;
     unset($getid3->info['flac']['PICTURE']['raw']);
     return true;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $LPACheader = fread($this->getid3->fp, 14);
     if (substr($LPACheader, 0, 4) != 'LPAC') {
         $info['error'][] = 'Expected "LPAC" at offset ' . $info['avdataoffset'] . ', found "' . $StreamMarker . '"';
         return false;
     }
     $info['avdataoffset'] += 14;
     $info['fileformat'] = 'lpac';
     $info['audio']['dataformat'] = 'lpac';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1));
     $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1));
     $info['lpac']['total_samples'] = getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4));
     $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
     $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
     $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4);
     $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2);
     $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1);
     if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
         $info['warning'][] = '24-bit and 16-bit flags cannot both be set';
     }
     $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
     $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000);
     $info['lpac']['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256;
     $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000);
     $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000);
     $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000);
     $info['lpac']['quantization'] = ($flags['parameters'] & 0x1f00) >> 8;
     $info['lpac']['max_prediction_order'] = $flags['parameters'] & 0x3f;
     if ($info['lpac']['flags']['fast_compress'] && $info['lpac']['max_prediction_order'] != 3) {
         $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info['lpac']['max_prediction_order'] . '"';
     }
     switch ($info['lpac']['file_version']) {
         case 6:
             if ($info['lpac']['flags']['adaptive_quantization']) {
                 $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
             }
             if ($info['lpac']['quantization'] != 20) {
                 $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info['lpac']['flags']['Q'];
             }
             break;
         default:
             //$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org';
             break;
     }
     $getid3_temp = new getID3();
     $getid3_temp->openfile($this->getid3->filename);
     $getid3_temp->info = $info;
     $getid3_riff = new getid3_riff($getid3_temp);
     $getid3_riff->Analyze();
     $info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
     $info['riff'] = $getid3_temp->info['riff'];
     $info['error'] = $getid3_temp->info['error'];
     $info['warning'] = $getid3_temp->info['warning'];
     $info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
     $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
     unset($getid3_temp, $getid3_riff);
     $info['audio']['channels'] = $info['lpac']['flags']['stereo'] ? 2 : 1;
     if ($info['lpac']['flags']['24_bit']) {
         $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
     } elseif ($info['lpac']['flags']['16_bit']) {
         $info['audio']['bits_per_sample'] = 16;
     } else {
         $info['audio']['bits_per_sample'] = 8;
     }
     if ($info['lpac']['flags']['fast_compress']) {
         // fast
         $info['audio']['encoder_options'] = '-1';
     } else {
         switch ($info['lpac']['max_prediction_order']) {
             case 20:
                 // simple
                 $info['audio']['encoder_options'] = '-2';
                 break;
             case 30:
                 // medium
                 $info['audio']['encoder_options'] = '-3';
                 break;
             case 40:
                 // high
                 $info['audio']['encoder_options'] = '-4';
                 break;
             case 60:
                 // extrahigh
                 $info['audio']['encoder_options'] = '-5';
                 break;
         }
     }
     $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     return true;
 }
예제 #20
0
 function getid3_avr(&$fd, &$ThisFileInfo)
 {
     // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
     // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
     // offset    type    length    name        comments
     // ---------------------------------------------------------------------
     // 0    char    4    ID        format ID == "2BIT"
     // 4    char    8    name        sample name (unused space filled with 0)
     // 12    short    1    mono/stereo    0=mono, -1 (0xFFFF)=stereo
     //                     With stereo, samples are alternated,
     //                     the first voice is the left :
     //                     (LRLRLRLRLRLRLRLRLR...)
     // 14    short    1    resolution    8, 12 or 16 (bits)
     // 16    short    1    signed or not    0=unsigned, -1 (0xFFFF)=signed
     // 18    short    1    loop or not    0=no loop, -1 (0xFFFF)=loop on
     // 20    short    1    MIDI note    0xFFnn, where 0 <= nn <= 127
     //                     0xFFFF means "no MIDI note defined"
     // 22    byte    1    Replay speed    Frequence in the Replay software
     //                     0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
     //                     3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
     //                     6=43.885 Khz, 7=47.261 Khz
     //                     -1 (0xFF)=no defined Frequence
     // 23    byte    3    sample rate    in Hertz
     // 26    long    1    size in bytes (2 * bytes in stereo)
     // 30    long    1    loop begin    0 for no loop
     // 34    long    1    loop size    equal to 'size' for no loop
     // 38  short   2   Reserved, MIDI keyboard split */
     // 40  short   2   Reserved, sample compression */
     // 42  short   2   Reserved */
     // 44  char   20;  Additional filename space, used if (name[7] != 0)
     // 64    byte    64    user data
     // 128    bytes    ?    sample data    (12 bits samples are coded on 16 bits:
     //                     0000 xxxx xxxx xxxx)
     // ---------------------------------------------------------------------
     // Note that all values are in motorola (big-endian) format, and that long is
     // assumed to be 4 bytes, and short 2 bytes.
     // When reading the samples, you should handle both signed and unsigned data,
     // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
     // 8-bit data between signed/unsigned just add 127 to the sample values.
     // Simularly for 16-bit data you should add 32769
     $ThisFileInfo['fileformat'] = 'avr';
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $AVRheader = fread($fd, 128);
     $ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
     if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') {
         $ThisFileInfo['error'][] = 'Expecting "2BIT" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . $ThisFileInfo['avr']['raw']['magic'] . '"';
         unset($ThisFileInfo['fileformat']);
         unset($ThisFileInfo['avr']);
         return false;
     }
     $ThisFileInfo['avdataoffset'] += 128;
     $ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
     $ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2));
     $ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2));
     $ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2));
     $ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2));
     $ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2));
     $ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1));
     $ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3));
     $ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4));
     $ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4));
     $ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4));
     $ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2));
     $ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2));
     $ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2));
     $ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
     $ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
     $ThisFileInfo['avr']['flags']['stereo'] = $ThisFileInfo['avr']['raw']['mono'] == 0 ? false : true;
     $ThisFileInfo['avr']['flags']['signed'] = $ThisFileInfo['avr']['raw']['signed'] == 0 ? false : true;
     $ThisFileInfo['avr']['flags']['loop'] = $ThisFileInfo['avr']['raw']['loop'] == 0 ? false : true;
     $ThisFileInfo['avr']['midi_notes'] = array();
     if (($ThisFileInfo['avr']['raw']['midi'] & 0xff00) != 0xff00) {
         $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xff00) >> 8;
     }
     if (($ThisFileInfo['avr']['raw']['midi'] & 0xff) != 0xff) {
         $ThisFileInfo['avr']['midi_notes'][] = $ThisFileInfo['avr']['raw']['midi'] & 0xff;
     }
     if ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] != $ThisFileInfo['avr']['sample_length'] * ($ThisFileInfo['avr']['bits_per_sample'] == 8 ? 1 : 2)) {
         $ThisFileInfo['warning'][] = 'Probable truncated file: expecting ' . $ThisFileInfo['avr']['sample_length'] * ($ThisFileInfo['avr']['bits_per_sample'] == 8 ? 1 : 2) . ' bytes of audio data, found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
     }
     $ThisFileInfo['audio']['dataformat'] = 'avr';
     $ThisFileInfo['audio']['lossless'] = true;
     $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
     $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample'];
     $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate'];
     $ThisFileInfo['audio']['channels'] = $ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1;
     $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels'] / $ThisFileInfo['avr']['sample_rate'];
     $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['avr']['sample_length'] * ($ThisFileInfo['avr']['bits_per_sample'] == 8 ? 8 : 16) / $ThisFileInfo['playtime_seconds'];
     return true;
 }
 private function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->container == 'riff') {
         return getid3_lib::LittleEndian2Int($byteword, $signed);
     }
     return getid3_lib::BigEndian2Int($byteword, false, $signed);
 }
 function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     $info =& $this->getid3->info;
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
     // http://wiki.multimedia.cx/index.php?title=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // MPEG layer                                      2    If you send AAC in MPEG-TS, set to 0
     // protection_absent                               1    0: CRC present; 1: no CRC
     // profile                                         2    0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
     // sampling_frequency_index                        4    15 not allowed
     // private_bit                                     1    usually 0
     // channel_configuration                           3
     // original/copy                                   1    0: original; 1: copy
     // home                                            1    usually 0
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)  // not present in some documentation?
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = $info['avdataoffset'];
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; $i++) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!getid3_lib::intValueSupported($byteoffset)) {
             $info['warning'][] = 'Unable to parse AAC file beyond ' . ftell($this->getid3->fp) . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         fseek($this->getid3->fp, $byteoffset, SEEK_SET);
         // First get substring
         $substring = fread($this->getid3->fp, 9);
         // header is 7 bytes (or 9 if CRC is present)
         $substringlength = strlen($substring);
         if ($substringlength != 9) {
             $info['error'][] = 'Failed to read 7 bytes at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // this would be easier with 64-bit math, but split it up to allow for 32-bit:
         $header1 = getid3_lib::BigEndian2Int(substr($substring, 0, 2));
         $header2 = getid3_lib::BigEndian2Int(substr($substring, 2, 4));
         $header3 = getid3_lib::BigEndian2Int(substr($substring, 6, 1));
         $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4;
         if ($info['aac']['header']['raw']['syncword'] != 0xfff) {
             $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)';
             //if ($info['fileformat'] == 'aac') {
             //	return true;
             //}
             unset($info['aac']);
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber == 0) {
             $info['aac']['header_type'] = 'ADTS';
             $info['fileformat'] = 'aac';
             $info['audio']['dataformat'] = 'aac';
             $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3;
             $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1;
             $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0;
             $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30;
             $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26;
             $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25;
             $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22;
             $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21;
             $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20;
             $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19;
             $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18;
             $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5;
             $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4;
             $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true;
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
             $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
             $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
             $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
             $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
             $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code'];
             if ($ReturnExtendedInfo) {
                 $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
                 $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
             }
             if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
                 $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead';
             }
             if ($info['aac']['header']['sample_frequency'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
             $info['audio']['channels'] = $info['aac']['header']['channels'];
         }
         $FrameLength = ($header2 & 0x3ffe0) >> 5;
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         getid3_lib::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2;
         if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $info['audio']['bitrate_mode'] = 'vbr';
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
         }
         $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0;
         if ($info['aac']['header']['crc_present']) {
             //$info['aac'][$framenumber]['crc'] = getid3_lib::BigEndian2Int(substr($substring, 7, 2);
         }
         if (!$ReturnExtendedInfo) {
             unset($info['aac'][$framenumber]);
         }
         /*
         $rounded_precision = 5000;
         $info['aac']['bitrate_distribution_rounded'] = array();
         foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
         	$rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
         	getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
         }
         ksort($info['aac']['bitrate_distribution_rounded']);
         */
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) {
             // keep scanning
         } else {
             $info['aac']['frames'] = $framenumber;
             $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             ksort($info['aac']['bitrate_distribution']);
             $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
             return true;
         }
     }
     // should never get here.
 }
 function getid3_vqf(&$fd, &$ThisFileInfo)
 {
     // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     $ThisFileInfo['fileformat'] = 'vqf';
     $ThisFileInfo['audio']['dataformat'] = 'vqf';
     $ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
     $ThisFileInfo['audio']['lossless'] = false;
     // shortcut
     $ThisFileInfo['vqf']['raw'] = array();
     $thisfile_vqf =& $ThisFileInfo['vqf'];
     $thisfile_vqf_raw =& $thisfile_vqf['raw'];
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $VQFheaderData = fread($fd, 16);
     $offset = 0;
     $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
     if ($thisfile_vqf_raw['header_tag'] != 'TWIN') {
         $ThisFileInfo['error'][] = 'Expecting "TWIN" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "' . $thisfile_vqf_raw['header_tag'] . '"';
         unset($ThisFileInfo['vqf']);
         unset($ThisFileInfo['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
     $offset += 8;
     $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
     $offset += 4;
     while (ftell($fd) < $ThisFileInfo['avdataend']) {
         $ChunkBaseOffset = ftell($fd);
         $chunkoffset = 0;
         $ChunkData = fread($fd, 8);
         $ChunkName = substr($ChunkData, $chunkoffset, 4);
         if ($ChunkName == 'DATA') {
             $ThisFileInfo['avdataoffset'] = $ChunkBaseOffset;
             break;
         }
         $chunkoffset += 4;
         $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
         $chunkoffset += 4;
         if ($ChunkSize > $ThisFileInfo['avdataend'] - ftell($fd)) {
             $ThisFileInfo['error'][] = 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset;
             break;
         }
         if ($ChunkSize > 0) {
             $ChunkData .= fread($fd, $ChunkSize);
         }
         switch ($ChunkName) {
             case 'COMM':
                 // shortcut
                 $thisfile_vqf['COMM'] = array();
                 $thisfile_vqf_COMM =& $thisfile_vqf['COMM'];
                 $thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
                 $ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
                 $ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
                 $ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate'] / 1000);
                 if ($ThisFileInfo['audio']['bitrate'] == 0) {
                     $ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
                     return false;
                 }
                 break;
             case 'NAME':
             case 'AUTH':
             case '(c) ':
             case 'FILE':
             case 'COMT':
             case 'ALBM':
                 $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
                 break;
             case 'DSIZ':
                 $thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
                 break;
             default:
                 // [i_a] hexdump chunk because it may be ANYTHING and we don't want illegal characters to end up inside the warning text!
                 $temp = strtr($ChunkName, "", ' ');
                 if (preg_match('/[^ -~]/', $temp)) {
                     $temp = unpack('H*', $temp);
                     //$temp = implode(' ', str_split($temp[1], 8));
                     $temp = '0x' . $temp[1];
                 }
                 $ThisFileInfo['warning'][] = 'Unhandled chunk type "' . $temp . '" at offset ' . $ChunkBaseOffset;
                 break;
         }
     }
     $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate'];
     if (isset($thisfile_vqf['DSIZ']) && $thisfile_vqf['DSIZ'] != $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')) {
         switch ($thisfile_vqf['DSIZ']) {
             case 0:
             case 1:
                 $ThisFileInfo['warning'][] = 'Invalid DSIZ value "' . $thisfile_vqf['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($thisfile_vqf['DSIZ'] + 1) . '.0';
                 $ThisFileInfo['audio']['encoder'] = 'Ahead Nero';
                 break;
             default:
                 $ThisFileInfo['warning'][] = 'Probable corrupted file - should be ' . $thisfile_vqf['DSIZ'] . ' bytes, actually ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'));
                 break;
         }
     }
     return true;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'ogg';
     // Warn about illegal tags - only vorbiscomments are allowed
     if (isset($info['id3v2'])) {
         $info['warning'][] = 'Illegal ID3v2 tag present.';
     }
     if (isset($info['id3v1'])) {
         $info['warning'][] = 'Illegal ID3v1 tag present.';
     }
     if (isset($info['ape'])) {
         $info['warning'][] = 'Illegal APE tag present.';
     }
     // Page 1 - Stream Header
     $this->fseek($info['avdataoffset']);
     $oggpageinfo = $this->ParseOggPageHeader();
     $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
         $info['error'][] = 'Could not find start of Ogg page in the first ' . $this->getid3->fread_buffer_size() . ' bytes (this might not be an Ogg-Vorbis file?)';
         unset($info['fileformat']);
         unset($info['ogg']);
         return false;
     }
     $filedata = $this->fread($oggpageinfo['page_length']);
     $filedataoffset = 0;
     if (substr($filedata, 0, 4) == 'fLaC') {
         $info['audio']['dataformat'] = 'flac';
         $info['audio']['bitrate_mode'] = 'vbr';
         $info['audio']['lossless'] = true;
     } elseif (substr($filedata, 1, 6) == 'vorbis') {
         $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
     } elseif (substr($filedata, 0, 8) == 'Speex   ') {
         // http://www.speex.org/manual/node10.html
         $info['audio']['dataformat'] = 'speex';
         $info['mime_type'] = 'audio/speex';
         $info['audio']['bitrate_mode'] = 'abr';
         $info['audio']['lossless'] = false;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8);
         // hard-coded to 'Speex   '
         $filedataoffset += 8;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
         $filedataoffset += 20;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
         $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
         $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
         $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
         $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
         $info['audio']['sample_rate'] = $info['speex']['sample_rate'];
         $info['audio']['channels'] = $info['speex']['channels'];
         if ($info['speex']['vbr']) {
             $info['audio']['bitrate_mode'] = 'vbr';
         }
     } elseif (substr($filedata, 0, 7) == "€" . 'theora') {
         // http://www.theora.org/doc/Theora.pdf (section 6.2)
         $info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7);
         // hard-coded to "\x80.'theora'
         $filedataoffset += 7;
         $info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
         $filedataoffset += 4;
         $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
         $filedataoffset += 1;
         $info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
         $filedataoffset += 3;
         $info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xfc00) >> 10;
         $info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x3e0) >> 5;
         $info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x18) >> 3;
         $info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x7) >> 0;
         // should be 0
         $info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
         $info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
         $info['video']['dataformat'] = 'theora';
         $info['mime_type'] = 'video/ogg';
         //$info['audio']['bitrate_mode'] = 'abr';
         //$info['audio']['lossless']     = false;
         $info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
         $info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
         if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
             $info['video']['frame_rate'] = (double) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
         }
         if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
             $info['video']['pixel_aspect_ratio'] = (double) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
         }
         $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 [' . $this->getid3->version() . '] -- bitrate, playtime and all audio data are currently unavailable';
     } elseif (substr($filedata, 0, 8) == "fishead") {
         // Ogg Skeleton version 3.0 Format Specification
         // http://xiph.org/ogg/doc/skeleton.html
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
         $filedataoffset += 2;
         $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
         $filedataoffset += 8;
         $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
         $filedataoffset += 20;
         $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'] . '.' . $info['ogg']['skeleton']['fishead']['raw']['version_minor'];
         $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'];
         $info['ogg']['skeleton']['fishead']['basetime'] = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'];
         $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc'];
         $counter = 0;
         do {
             $oggpageinfo = $this->ParseOggPageHeader();
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno'] . '.' . $counter++] = $oggpageinfo;
             $filedata = $this->fread($oggpageinfo['page_length']);
             $this->fseek($oggpageinfo['page_end_offset']);
             if (substr($filedata, 0, 8) == "fisbone") {
                 $filedataoffset = 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
                 $filedataoffset += 8;
                 $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
                 $filedataoffset += 4;
                 $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
                 $filedataoffset += 1;
                 $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3);
                 $filedataoffset += 3;
             } elseif (substr($filedata, 1, 6) == 'theora') {
                 $info['video']['dataformat'] = 'theora1';
                 $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 [' . $this->getid3->version() . ']';
                 //break;
             } elseif (substr($filedata, 1, 6) == 'vorbis') {
                 $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
             } else {
                 $info['error'][] = 'unexpected';
                 //break;
             }
             //} while ($oggpageinfo['page_seqno'] == 0);
         } while ($oggpageinfo['page_seqno'] == 0 && substr($filedata, 0, 8) != "fisbone");
         $this->fseek($oggpageinfo['page_start_offset']);
         $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 [' . $this->getid3->version() . ']';
         //return false;
     } else {
         $info['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found "' . substr($filedata, 0, 8) . '"';
         unset($info['ogg']);
         unset($info['mime_type']);
         return false;
     }
     // Page 2 - Comment Header
     $oggpageinfo = $this->ParseOggPageHeader();
     $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
     switch ($info['audio']['dataformat']) {
         case 'vorbis':
             $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
             $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6);
             // hard-coded to 'vorbis'
             $this->ParseVorbisComments();
             break;
         case 'flac':
             $flac = new getid3_flac($this->getid3);
             if (!$flac->parseMETAdata()) {
                 $info['error'][] = 'Failed to parse FLAC headers';
                 return false;
             }
             unset($flac);
             break;
         case 'speex':
             $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
             $this->ParseVorbisComments();
             break;
     }
     // Last Page - Number of Samples
     if (!getid3_lib::intValueSupported($info['avdataend'])) {
         $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
     } else {
         $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
         $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
         if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
             $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
             $info['avdataend'] = $this->ftell();
             $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
             $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
             if ($info['ogg']['samples'] == 0) {
                 $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
                 return false;
             }
             if (!empty($info['audio']['sample_rate'])) {
                 $info['ogg']['bitrate_average'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
             }
         }
     }
     if (!empty($info['ogg']['bitrate_average'])) {
         $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
     } elseif (!empty($info['ogg']['bitrate_nominal'])) {
         $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
     } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
         $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
     }
     if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
         if ($info['audio']['bitrate'] == 0) {
             $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
             return false;
         }
         $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']);
     }
     if (isset($info['ogg']['vendor'])) {
         $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
         // Vorbis only
         if ($info['audio']['dataformat'] == 'vorbis') {
             // Vorbis 1.0 starts with Xiph.Org
             if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
                 if ($info['audio']['bitrate_mode'] == 'abr') {
                     // Set -b 128 on abr files
                     $info['audio']['encoder_options'] = '-b ' . round($info['ogg']['bitrate_nominal'] / 1000);
                 } elseif ($info['audio']['bitrate_mode'] == 'vbr' && $info['audio']['channels'] == 2 && $info['audio']['sample_rate'] >= 44100 && $info['audio']['sample_rate'] <= 48000) {
                     // Set -q N on vbr files
                     $info['audio']['encoder_options'] = '-q ' . $this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
                 }
             }
             if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
                 $info['audio']['encoder_options'] = 'Nominal bitrate: ' . intval(round($info['ogg']['bitrate_nominal'] / 1000)) . 'kbps';
             }
         }
     }
     return true;
 }
예제 #25
0
 static function MultiByteCharString2HTML($string, $charset = 'ISO-8859-1')
 {
     $HTMLstring = '';
     switch ($charset) {
         case 'ISO-8859-1':
         case 'ISO8859-1':
         case 'ISO-8859-15':
         case 'ISO8859-15':
         case 'cp866':
         case 'ibm866':
         case '866':
         case 'cp1251':
         case 'Windows-1251':
         case 'win-1251':
         case '1251':
         case 'cp1252':
         case 'Windows-1252':
         case '1252':
         case 'KOI8-R':
         case 'koi8-ru':
         case 'koi8r':
         case 'BIG5':
         case '950':
         case 'GB2312':
         case '936':
         case 'BIG5-HKSCS':
         case 'Shift_JIS':
         case 'SJIS':
         case '932':
         case 'EUC-JP':
         case 'EUCJP':
             $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
             break;
         case 'UTF-8':
             $strlen = strlen($string);
             for ($i = 0; $i < $strlen; $i++) {
                 $char_ord_val = ord($string[$i]);
                 $charval = 0;
                 if ($char_ord_val < 0x80) {
                     $charval = $char_ord_val;
                 } elseif (($char_ord_val & 0xf0) >> 4 == 0xf && $i + 3 < $strlen) {
                     $charval = ($char_ord_val & 0x7) << 18;
                     $charval += (ord($string[++$i]) & 0x3f) << 12;
                     $charval += (ord($string[++$i]) & 0x3f) << 6;
                     $charval += ord($string[++$i]) & 0x3f;
                 } elseif (($char_ord_val & 0xe0) >> 5 == 0x7 && $i + 2 < $strlen) {
                     $charval = ($char_ord_val & 0xf) << 12;
                     $charval += (ord($string[++$i]) & 0x3f) << 6;
                     $charval += ord($string[++$i]) & 0x3f;
                 } elseif (($char_ord_val & 0xc0) >> 6 == 0x3 && $i + 1 < $strlen) {
                     $charval = ($char_ord_val & 0x1f) << 6;
                     $charval += ord($string[++$i]) & 0x3f;
                 }
                 if ($charval >= 32 && $charval <= 127) {
                     $HTMLstring .= htmlentities(chr($charval));
                 } else {
                     $HTMLstring .= '&#' . $charval . ';';
                 }
             }
             break;
         case 'UTF-16LE':
             for ($i = 0; $i < strlen($string); $i += 2) {
                 $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
                 if ($charval >= 32 && $charval <= 127) {
                     $HTMLstring .= chr($charval);
                 } else {
                     $HTMLstring .= '&#' . $charval . ';';
                 }
             }
             break;
         case 'UTF-16BE':
             for ($i = 0; $i < strlen($string); $i += 2) {
                 $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
                 if ($charval >= 32 && $charval <= 127) {
                     $HTMLstring .= chr($charval);
                 } else {
                     $HTMLstring .= '&#' . $charval . ';';
                 }
             }
             break;
         default:
             $HTMLstring = 'ERROR: Character set "' . $charset . '" not supported in MultiByteCharString2HTML()';
             break;
     }
     return $HTMLstring;
 }
예제 #26
0
 public function ParseMPCsv8()
 {
     // this is SV8
     // http://trac.musepack.net/trac/wiki/SV8Specification
     $info =& $this->getid3->info;
     $thisfile_mpc_header =& $info['mpc']['header'];
     $keyNameSize = 2;
     $maxHandledPacketLength = 9;
     // specs say: "n*8; 0 < n < 10"
     $offset = ftell($this->getid3->fp);
     while ($offset < $info['avdataend']) {
         $thisPacket = array();
         $thisPacket['offset'] = $offset;
         $packet_offset = 0;
         // Size is a variable-size field, could be 1-4 bytes (possibly more?)
         // read enough data in and figure out the exact size later
         $MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength);
         $packet_offset += $keyNameSize;
         $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
         $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
         if ($thisPacket['key'] == $thisPacket['key_name']) {
             $info['error'][] = 'Found unexpected key value "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
             return false;
         }
         $packetLength = 0;
         $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength);
         // includes keyname and packet_size field
         if ($thisPacket['packet_size'] === false) {
             $info['error'][] = 'Did not find expected packet length within ' . $maxHandledPacketLength . ' bytes at offset ' . ($thisPacket['offset'] + $keyNameSize);
             return false;
         }
         $packet_offset += $packetLength;
         $offset += $thisPacket['packet_size'];
         switch ($thisPacket['key']) {
             case 'SH':
                 // Stream Header
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
                 $packet_offset += 4;
                 $thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $packetLength = 0;
                 $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $packetLength = 0;
                 $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['sample_frequency_raw'] = ($otherUsefulData & 0xe000) >> 13;
                 $thisPacket['max_bands_used'] = ($otherUsefulData & 0x1f00) >> 8;
                 $thisPacket['channels'] = (($otherUsefulData & 0xf0) >> 4) + 1;
                 $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x8) >> 3);
                 $thisPacket['audio_block_frames'] = ($otherUsefulData & 0x7) >> 0;
                 $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
                 $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
                 $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
                 $thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
                 $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
                 $info['audio']['channels'] = $thisPacket['channels'];
                 $info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
                 $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
                 $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
                 break;
             case 'RG':
                 // Replay Gain
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 if ($thisPacket['replaygain_title_gain']) {
                     $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain'];
                 }
                 if ($thisPacket['replaygain_title_peak']) {
                     $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak'];
                 }
                 if ($thisPacket['replaygain_album_gain']) {
                     $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain'];
                 }
                 if ($thisPacket['replaygain_album_peak']) {
                     $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak'];
                 }
                 break;
             case 'EI':
                 // Encoder Info
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $quality_int = ($profile_pns & 0xf0) >> 4;
                 $quality_dec = ($profile_pns & 0xe) >> 3;
                 $thisPacket['quality'] = (double) $quality_int + $quality_dec / 8;
                 $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x1) >> 0);
                 $thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version'] = $thisPacket['version_major'] . '.' . $thisPacket['version_minor'] . '.' . $thisPacket['version_build'];
                 $info['audio']['encoder'] = 'MPC v' . $thisPacket['version'] . ' (' . ($thisPacket['version_minor'] % 2 ? 'unstable' : 'stable') . ')';
                 $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
                 //$thisfile_mpc_header['quality']         = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
                 $thisfile_mpc_header['quality'] = (double) ($thisPacket['quality'] - 5);
                 // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
                 break;
             case 'SO':
                 // Seek Table Offset
                 $packetLength = 0;
                 $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 break;
             case 'ST':
                 // Seek Table
             // Seek Table
             case 'SE':
                 // Stream End
             // Stream End
             case 'AP':
                 // Audio Data
                 // nothing useful here, just skip this packet
                 $thisPacket = array();
                 break;
             default:
                 $info['error'][] = 'Found unhandled key type "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
                 return false;
                 break;
         }
         if (!empty($thisPacket)) {
             $info['mpc']['packets'][] = $thisPacket;
         }
         fseek($this->getid3->fp, $offset);
     }
     $thisfile_mpc_header['size'] = $offset;
     return true;
 }
예제 #27
0
 function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed = false)
 {
     if ($ThisFileInfo['fileformat'] == 'riff') {
         return getid3_lib::LittleEndian2Int($byteword, $signed);
     }
     return getid3_lib::BigEndian2Int($byteword, false, $signed);
 }
 function readByte()
 {
     return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
 }
예제 #29
0
 /**
  * Create audio format reader
  *
  * @throws \RuntimeException
  *
  * @return \GravityMedia\Metadata\GetId3\Reader
  */
 public function createAudioFormatReader()
 {
     // detect data offset
     $offset = $this->info['avdataoffset'];
     if (isset($this->info['id3v2']['tag_offset_start'])) {
         $offset = max($offset, $this->info['id3v2']['tag_offset_end']);
     }
     // length of ID3 v2 tag in 10-byte header doesn't include 10-byte header length
     if (!isset($this->info['id3v2'])) {
         fseek($this->fp, 0);
         $header = fread($this->fp, 10);
         if ('ID3' === substr($header, 0, 3) && 10 == strlen($header)) {
             $offset += GetId3Lib::BigEndian2Int(substr($header, 6, 4), 1) + 10;
         }
     }
     // read 32 kb file data
     fseek($this->fp, $offset, SEEK_SET);
     $data = fread($this->fp, 32774);
     // detect format
     $format = GetId3::getInstance()->getGetId3()->GetFileFormat($data, $this->filename);
     // unable to detect format
     if (false === $format) {
         GetId3::getInstance()->close();
         throw new \RuntimeException(sprintf('Unable to determine file format of "%s".', $this->filename));
     }
     // check for illegal ID3 tags
     if ((isset($this->info['tags']['id3v1']) || isset($this->info['tags']['id3v2'])) && isset($format['fail_id3']) && 'ERROR' === $format['fail_id3']) {
         GetId3::getInstance()->close();
         throw new \RuntimeException('ID3 tags not allowed on this file type.');
     }
     // check for illegal APE tags
     if (isset($this->info['tags']['ape']) && isset($format['fail_ape']) && 'ERROR' === $format['fail_ape']) {
         GetId3::getInstance()->close();
         throw new \RuntimeException('APE tags not allowed on this file type.');
     }
     return $this->createReader($format['group'], $format['module'], 'audio');
 }
 function Handle_EMBL_ID_SIMPLETAG(&$offset, $sub_subelement_end)
 {
     $simpletag_entry = array();
     while ($offset < $sub_subelement_end) {
         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
         $sub_sub_subelement_offset = $offset;
         $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
         $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
         $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
         $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
         switch ($sub_sub_subelement_id) {
             case EBML_ID_TAGNAME:
             case EBML_ID_TAGLANGUAGE:
             case EBML_ID_TAGSTRING:
             case EBML_ID_TAGBINARY:
                 $simpletag_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
                 break;
             case EBML_ID_SIMPLETAG:
                 $simpletag_entry[$sub_sub_subelement_idname][] = $this->Handle_EMBL_ID_SIMPLETAG($offset, $sub_sub_subelement_end);
                 break;
             case EBML_ID_TAGDEFAULT:
                 $simpletag_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                 break;
             default:
                 $this->warnings[] = 'Unhandled tag.simpletag element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_subelement_id . '::' . $sub_sub_subelement_idname . ') at ' . $sub_sub_subelement_offset;
                 break;
         }
         $offset = $sub_sub_subelement_end;
     }
     return $simpletag_entry;
 }