File indexing completed on 2024-12-22 05:33:14
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 /// // 0011 // module.tag.id3v2.php // 0012 // module for analyzing ID3v2 tags // 0013 // dependencies: module.tag.id3v1.php // 0014 // /// 0015 ///////////////////////////////////////////////////////////////// 0016 0017 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); 0018 0019 class getid3_id3v2 extends getid3_handler 0020 { 0021 public $StartingOffset = 0; 0022 0023 public function Analyze() { 0024 $info = &$this->getid3->info; 0025 0026 // Overall tag structure: 0027 // +-----------------------------+ 0028 // | Header (10 bytes) | 0029 // +-----------------------------+ 0030 // | Extended Header | 0031 // | (variable length, OPTIONAL) | 0032 // +-----------------------------+ 0033 // | Frames (variable length) | 0034 // +-----------------------------+ 0035 // | Padding | 0036 // | (variable length, OPTIONAL) | 0037 // +-----------------------------+ 0038 // | Footer (10 bytes, OPTIONAL) | 0039 // +-----------------------------+ 0040 0041 // Header 0042 // ID3v2/file identifier "ID3" 0043 // ID3v2 version $04 00 0044 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) 0045 // ID3v2 size 4 * %0xxxxxxx 0046 0047 0048 // shortcuts 0049 $info['id3v2']['header'] = true; 0050 $thisfile_id3v2 = &$info['id3v2']; 0051 $thisfile_id3v2['flags'] = array(); 0052 $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; 0053 0054 0055 $this->fseek($this->StartingOffset); 0056 $header = $this->fread(10); 0057 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 0058 0059 $thisfile_id3v2['majorversion'] = ord($header{3}); 0060 $thisfile_id3v2['minorversion'] = ord($header{4}); 0061 0062 // shortcut 0063 $id3v2_majorversion = &$thisfile_id3v2['majorversion']; 0064 0065 } else { 0066 0067 unset($info['id3v2']); 0068 return false; 0069 0070 } 0071 0072 if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) 0073 0074 $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; 0075 return false; 0076 0077 } 0078 0079 $id3_flags = ord($header{5}); 0080 switch ($id3v2_majorversion) { 0081 case 2: 0082 // %ab000000 in v2.2 0083 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 0084 $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression 0085 break; 0086 0087 case 3: 0088 // %abc00000 in v2.3 0089 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 0090 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 0091 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 0092 break; 0093 0094 case 4: 0095 // %abcd0000 in v2.4 0096 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 0097 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 0098 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 0099 $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present 0100 break; 0101 } 0102 0103 $thisfile_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 0104 0105 $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset; 0106 $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; 0107 0108 0109 0110 // create 'encoding' key - used by getid3::HandleAllTags() 0111 // in ID3v2 every field can have it's own encoding type 0112 // so force everything to UTF-8 so it can be handled consistantly 0113 $thisfile_id3v2['encoding'] = 'UTF-8'; 0114 0115 0116 // Frames 0117 0118 // All ID3v2 frames consists of one frame header followed by one or more 0119 // fields containing the actual information. The header is always 10 0120 // bytes and laid out as follows: 0121 // 0122 // Frame ID $xx xx xx xx (four characters) 0123 // Size 4 * %0xxxxxxx 0124 // Flags $xx xx 0125 0126 $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header 0127 if (!empty($thisfile_id3v2['exthead']['length'])) { 0128 $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4); 0129 } 0130 if (!empty($thisfile_id3v2_flags['isfooter'])) { 0131 $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio 0132 } 0133 if ($sizeofframes > 0) { 0134 0135 $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable 0136 0137 // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) 0138 if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { 0139 $framedata = $this->DeUnsynchronise($framedata); 0140 } 0141 // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead 0142 // of on tag level, making it easier to skip frames, increasing the streamability 0143 // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that 0144 // there exists an unsynchronised frame, while the new unsynchronisation flag in 0145 // the frame header [S:4.1.2] indicates unsynchronisation. 0146 0147 0148 //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present) 0149 $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header 0150 0151 0152 // Extended Header 0153 if (!empty($thisfile_id3v2_flags['exthead'])) { 0154 $extended_header_offset = 0; 0155 0156 if ($id3v2_majorversion == 3) { 0157 0158 // v2.3 definition: 0159 //Extended header size $xx xx xx xx // 32-bit integer 0160 //Extended Flags $xx xx 0161 // %x0000000 %00000000 // v2.3 0162 // x - CRC data present 0163 //Size of padding $xx xx xx xx 0164 0165 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0); 0166 $extended_header_offset += 4; 0167 0168 $thisfile_id3v2['exthead']['flag_bytes'] = 2; 0169 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 0170 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 0171 0172 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000); 0173 0174 $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 0175 $extended_header_offset += 4; 0176 0177 if ($thisfile_id3v2['exthead']['flags']['crc']) { 0178 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 0179 $extended_header_offset += 4; 0180 } 0181 $extended_header_offset += $thisfile_id3v2['exthead']['padding_size']; 0182 0183 } elseif ($id3v2_majorversion == 4) { 0184 0185 // v2.4 definition: 0186 //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer 0187 //Number of flag bytes $01 0188 //Extended Flags $xx 0189 // %0bcd0000 // v2.4 0190 // b - Tag is an update 0191 // Flag data length $00 0192 // c - CRC data present 0193 // Flag data length $05 0194 // Total frame CRC 5 * %0xxxxxxx 0195 // d - Tag restrictions 0196 // Flag data length $01 0197 0198 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true); 0199 $extended_header_offset += 4; 0200 0201 $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1 0202 $extended_header_offset += 1; 0203 0204 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 0205 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 0206 0207 $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40); 0208 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20); 0209 $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10); 0210 0211 if ($thisfile_id3v2['exthead']['flags']['update']) { 0212 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0 0213 $extended_header_offset += 1; 0214 } 0215 0216 if ($thisfile_id3v2['exthead']['flags']['crc']) { 0217 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5 0218 $extended_header_offset += 1; 0219 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false); 0220 $extended_header_offset += $ext_header_chunk_length; 0221 } 0222 0223 if ($thisfile_id3v2['exthead']['flags']['restrictions']) { 0224 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1 0225 $extended_header_offset += 1; 0226 0227 // %ppqrrstt 0228 $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); 0229 $extended_header_offset += 1; 0230 $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions 0231 $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions 0232 $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions 0233 $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions 0234 $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions 0235 0236 $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']); 0237 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']); 0238 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']); 0239 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']); 0240 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']); 0241 } 0242 0243 if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) { 0244 $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')'; 0245 } 0246 } 0247 0248 $framedataoffset += $extended_header_offset; 0249 $framedata = substr($framedata, $extended_header_offset); 0250 } // end extended header 0251 0252 0253 while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse 0254 if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { 0255 // insufficient room left in ID3v2 header for actual data - must be padding 0256 $thisfile_id3v2['padding']['start'] = $framedataoffset; 0257 $thisfile_id3v2['padding']['length'] = strlen($framedata); 0258 $thisfile_id3v2['padding']['valid'] = true; 0259 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { 0260 if ($framedata{$i} != "\x00") { 0261 $thisfile_id3v2['padding']['valid'] = false; 0262 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 0263 $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; 0264 break; 0265 } 0266 } 0267 break; // skip rest of ID3v2 header 0268 } 0269 if ($id3v2_majorversion == 2) { 0270 // Frame ID $xx xx xx (three characters) 0271 // Size $xx xx xx (24-bit integer) 0272 // Flags $xx xx 0273 0274 $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header 0275 $framedata = substr($framedata, 6); // and leave the rest in $framedata 0276 $frame_name = substr($frame_header, 0, 3); 0277 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); 0278 $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs 0279 0280 } elseif ($id3v2_majorversion > 2) { 0281 0282 // Frame ID $xx xx xx xx (four characters) 0283 // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) 0284 // Flags $xx xx 0285 0286 $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header 0287 $framedata = substr($framedata, 10); // and leave the rest in $framedata 0288 0289 $frame_name = substr($frame_header, 0, 4); 0290 if ($id3v2_majorversion == 3) { 0291 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 0292 } else { // ID3v2.4+ 0293 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) 0294 } 0295 0296 if ($frame_size < (strlen($framedata) + 4)) { 0297 $nextFrameID = substr($framedata, $frame_size, 4); 0298 if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { 0299 // next frame is OK 0300 } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { 0301 // MP3ext known broken frames - "ok" for the purposes of this test 0302 } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { 0303 $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; 0304 $id3v2_majorversion = 3; 0305 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 0306 } 0307 } 0308 0309 0310 $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); 0311 } 0312 0313 if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { 0314 // padding encountered 0315 0316 $thisfile_id3v2['padding']['start'] = $framedataoffset; 0317 $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata); 0318 $thisfile_id3v2['padding']['valid'] = true; 0319 0320 $len = strlen($framedata); 0321 for ($i = 0; $i < $len; $i++) { 0322 if ($framedata{$i} != "\x00") { 0323 $thisfile_id3v2['padding']['valid'] = false; 0324 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 0325 $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; 0326 break; 0327 } 0328 } 0329 break; // skip rest of ID3v2 header 0330 } 0331 0332 if ($frame_name == 'COM ') { 0333 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; 0334 $frame_name = 'COMM'; 0335 } 0336 if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { 0337 0338 unset($parsedFrame); 0339 $parsedFrame['frame_name'] = $frame_name; 0340 $parsedFrame['frame_flags_raw'] = $frame_flags; 0341 $parsedFrame['data'] = substr($framedata, 0, $frame_size); 0342 $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); 0343 $parsedFrame['dataoffset'] = $framedataoffset; 0344 0345 $this->ParseID3v2Frame($parsedFrame); 0346 $thisfile_id3v2[$frame_name][] = $parsedFrame; 0347 0348 $framedata = substr($framedata, $frame_size); 0349 0350 } else { // invalid frame length or FrameID 0351 0352 if ($frame_size <= strlen($framedata)) { 0353 0354 if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { 0355 0356 // next frame is valid, just skip the current frame 0357 $framedata = substr($framedata, $frame_size); 0358 $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; 0359 0360 } else { 0361 0362 // next frame is invalid too, abort processing 0363 //unset($framedata); 0364 $framedata = null; 0365 $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; 0366 0367 } 0368 0369 } elseif ($frame_size == strlen($framedata)) { 0370 0371 // this is the last frame, just skip 0372 $info['warning'][] = 'This was the last ID3v2 frame.'; 0373 0374 } else { 0375 0376 // next frame is invalid too, abort processing 0377 //unset($framedata); 0378 $framedata = null; 0379 $info['warning'][] = 'Invalid ID3v2 frame size, aborting.'; 0380 0381 } 0382 if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { 0383 0384 switch ($frame_name) { 0385 case "\x00\x00".'MP': 0386 case "\x00".'MP3': 0387 case ' MP3': 0388 case 'MP3e': 0389 case "\x00".'MP': 0390 case ' MP': 0391 case 'MP3': 0392 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; 0393 break; 0394 0395 default: 0396 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; 0397 break; 0398 } 0399 0400 } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) { 0401 0402 $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).'; 0403 0404 } else { 0405 0406 $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; 0407 0408 } 0409 0410 } 0411 $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); 0412 0413 } 0414 0415 } 0416 0417 0418 // Footer 0419 0420 // The footer is a copy of the header, but with a different identifier. 0421 // ID3v2 identifier "3DI" 0422 // ID3v2 version $04 00 0423 // ID3v2 flags %abcd0000 0424 // ID3v2 size 4 * %0xxxxxxx 0425 0426 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { 0427 $footer = $this->fread(10); 0428 if (substr($footer, 0, 3) == '3DI') { 0429 $thisfile_id3v2['footer'] = true; 0430 $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); 0431 $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); 0432 } 0433 if ($thisfile_id3v2['majorversion_footer'] <= 4) { 0434 $id3_flags = ord(substr($footer{5})); 0435 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); 0436 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); 0437 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); 0438 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); 0439 0440 $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); 0441 } 0442 } // end footer 0443 0444 if (isset($thisfile_id3v2['comments']['genre'])) { 0445 foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { 0446 unset($thisfile_id3v2['comments']['genre'][$key]); 0447 $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value))); 0448 } 0449 } 0450 0451 if (isset($thisfile_id3v2['comments']['track'])) { 0452 foreach ($thisfile_id3v2['comments']['track'] as $key => $value) { 0453 if (strstr($value, '/')) { 0454 list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]); 0455 } 0456 } 0457 } 0458 0459 if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) { 0460 $thisfile_id3v2['comments']['year'] = array($matches[1]); 0461 } 0462 0463 0464 if (!empty($thisfile_id3v2['TXXX'])) { 0465 // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames 0466 foreach ($thisfile_id3v2['TXXX'] as $txxx_array) { 0467 switch ($txxx_array['description']) { 0468 case 'replaygain_track_gain': 0469 if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) { 0470 $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 0471 } 0472 break; 0473 case 'replaygain_track_peak': 0474 if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) { 0475 $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']); 0476 } 0477 break; 0478 case 'replaygain_album_gain': 0479 if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) { 0480 $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 0481 } 0482 break; 0483 } 0484 } 0485 } 0486 0487 0488 // Set avdataoffset 0489 $info['avdataoffset'] = $thisfile_id3v2['headerlength']; 0490 if (isset($thisfile_id3v2['footer'])) { 0491 $info['avdataoffset'] += 10; 0492 } 0493 0494 return true; 0495 } 0496 0497 0498 public function ParseID3v2GenreString($genrestring) { 0499 // Parse genres into arrays of genreName and genreID 0500 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' 0501 // ID3v2.4.x: '21' $00 'Eurodisco' $00 0502 $clean_genres = array(); 0503 if (strpos($genrestring, "\x00") === false) { 0504 $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring); 0505 } 0506 $genre_elements = explode("\x00", $genrestring); 0507 foreach ($genre_elements as $element) { 0508 $element = trim($element); 0509 if ($element) { 0510 if (preg_match('#^[0-9]{1,3}#', $element)) { 0511 $clean_genres[] = getid3_id3v1::LookupGenreName($element); 0512 } else { 0513 $clean_genres[] = str_replace('((', '(', $element); 0514 } 0515 } 0516 } 0517 return $clean_genres; 0518 } 0519 0520 0521 public function ParseID3v2Frame(&$parsedFrame) { 0522 0523 // shortcuts 0524 $info = &$this->getid3->info; 0525 $id3v2_majorversion = $info['id3v2']['majorversion']; 0526 0527 $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); 0528 if (empty($parsedFrame['framenamelong'])) { 0529 unset($parsedFrame['framenamelong']); 0530 } 0531 $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); 0532 if (empty($parsedFrame['framenameshort'])) { 0533 unset($parsedFrame['framenameshort']); 0534 } 0535 0536 if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard 0537 if ($id3v2_majorversion == 3) { 0538 // Frame Header Flags 0539 // %abc00000 %ijk00000 0540 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation 0541 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation 0542 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only 0543 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression 0544 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption 0545 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity 0546 0547 } elseif ($id3v2_majorversion == 4) { 0548 // Frame Header Flags 0549 // %0abc0000 %0h00kmnp 0550 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation 0551 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation 0552 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only 0553 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity 0554 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression 0555 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption 0556 $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation 0557 $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator 0558 0559 // Frame-level de-unsynchronisation - ID3v2.4 0560 if ($parsedFrame['flags']['Unsynchronisation']) { 0561 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); 0562 } 0563 0564 if ($parsedFrame['flags']['DataLengthIndicator']) { 0565 $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); 0566 $parsedFrame['data'] = substr($parsedFrame['data'], 4); 0567 } 0568 } 0569 0570 // Frame-level de-compression 0571 if ($parsedFrame['flags']['compression']) { 0572 $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); 0573 if (!function_exists('gzuncompress')) { 0574 $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; 0575 } else { 0576 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { 0577 //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { 0578 $parsedFrame['data'] = $decompresseddata; 0579 unset($decompresseddata); 0580 } else { 0581 $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; 0582 } 0583 } 0584 } 0585 } 0586 0587 if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { 0588 if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { 0589 $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data'; 0590 } 0591 } 0592 0593 if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { 0594 0595 $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; 0596 switch ($parsedFrame['frame_name']) { 0597 case 'WCOM': 0598 $warning .= ' (this is known to happen with files tagged by RioPort)'; 0599 break; 0600 0601 default: 0602 break; 0603 } 0604 $info['warning'][] = $warning; 0605 0606 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier 0607 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier 0608 // There may be more than one 'UFID' frame in a tag, 0609 // but only one with the same 'Owner identifier'. 0610 // <Header for 'Unique file identifier', ID: 'UFID'> 0611 // Owner identifier <text string> $00 0612 // Identifier <up to 64 bytes binary data> 0613 $exploded = explode("\x00", $parsedFrame['data'], 2); 0614 $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : ''); 0615 $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : ''); 0616 0617 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame 0618 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame 0619 // There may be more than one 'TXXX' frame in each tag, 0620 // but only one with the same description. 0621 // <Header for 'User defined text information frame', ID: 'TXXX'> 0622 // Text encoding $xx 0623 // Description <text string according to encoding> $00 (00) 0624 // Value <text string according to encoding> 0625 0626 $frame_offset = 0; 0627 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0628 0629 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 0630 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 0631 } 0632 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 0633 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 0634 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 0635 } 0636 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 0637 if (ord($frame_description) === 0) { 0638 $frame_description = ''; 0639 } 0640 $parsedFrame['encodingid'] = $frame_textencoding; 0641 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 0642 0643 $parsedFrame['description'] = $frame_description; 0644 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 0645 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 0646 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); 0647 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { 0648 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); 0649 } else { 0650 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); 0651 } 0652 } 0653 //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain 0654 0655 0656 } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame 0657 // There may only be one text information frame of its kind in an tag. 0658 // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', 0659 // excluding 'TXXX' described in 4.2.6.> 0660 // Text encoding $xx 0661 // Information <text string(s) according to encoding> 0662 0663 $frame_offset = 0; 0664 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0665 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 0666 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 0667 } 0668 0669 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 0670 0671 $parsedFrame['encodingid'] = $frame_textencoding; 0672 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 0673 0674 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 0675 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / 0676 // This of course breaks when an artist name contains slash character, e.g. "AC/DC" 0677 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense 0678 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user 0679 switch ($parsedFrame['encoding']) { 0680 case 'UTF-16': 0681 case 'UTF-16BE': 0682 case 'UTF-16LE': 0683 $wordsize = 2; 0684 break; 0685 case 'ISO-8859-1': 0686 case 'UTF-8': 0687 default: 0688 $wordsize = 1; 0689 break; 0690 } 0691 $Txxx_elements = array(); 0692 $Txxx_elements_start_offset = 0; 0693 for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) { 0694 if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) { 0695 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 0696 $Txxx_elements_start_offset = $i + $wordsize; 0697 } 0698 } 0699 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 0700 foreach ($Txxx_elements as $Txxx_element) { 0701 $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element); 0702 if (!empty($string)) { 0703 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; 0704 } 0705 } 0706 unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset); 0707 } 0708 0709 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame 0710 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame 0711 // There may be more than one 'WXXX' frame in each tag, 0712 // but only one with the same description 0713 // <Header for 'User defined URL link frame', ID: 'WXXX'> 0714 // Text encoding $xx 0715 // Description <text string according to encoding> $00 (00) 0716 // URL <text string> 0717 0718 $frame_offset = 0; 0719 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0720 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 0721 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 0722 } 0723 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 0724 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 0725 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 0726 } 0727 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 0728 0729 if (ord($frame_description) === 0) { 0730 $frame_description = ''; 0731 } 0732 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 0733 0734 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 0735 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 0736 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 0737 } 0738 if ($frame_terminatorpos) { 0739 // there are null bytes after the data - this is not according to spec 0740 // only use data up to first null byte 0741 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); 0742 } else { 0743 // no null bytes following data, just use all data 0744 $frame_urldata = (string) $parsedFrame['data']; 0745 } 0746 0747 $parsedFrame['encodingid'] = $frame_textencoding; 0748 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 0749 0750 $parsedFrame['url'] = $frame_urldata; 0751 $parsedFrame['description'] = $frame_description; 0752 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 0753 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); 0754 } 0755 unset($parsedFrame['data']); 0756 0757 0758 } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames 0759 // There may only be one URL link frame of its kind in a tag, 0760 // except when stated otherwise in the frame description 0761 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' 0762 // described in 4.3.2.> 0763 // URL <text string> 0764 0765 $parsedFrame['url'] = trim($parsedFrame['data']); 0766 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 0767 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; 0768 } 0769 unset($parsedFrame['data']); 0770 0771 0772 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) 0773 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) 0774 // http://id3.org/id3v2.3.0#sec4.4 0775 // There may only be one 'IPL' frame in each tag 0776 // <Header for 'User defined URL link frame', ID: 'IPL'> 0777 // Text encoding $xx 0778 // People list strings <textstrings> 0779 0780 $frame_offset = 0; 0781 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0782 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 0783 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 0784 } 0785 $parsedFrame['encodingid'] = $frame_textencoding; 0786 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); 0787 $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); 0788 0789 // http://www.getid3.org/phpBB3/viewtopic.php?t=1369 0790 // "this tag typically contains null terminated strings, which are associated in pairs" 0791 // "there are users that use the tag incorrectly" 0792 $IPLS_parts = array(); 0793 if (strpos($parsedFrame['data_raw'], "\x00") !== false) { 0794 $IPLS_parts_unsorted = array(); 0795 if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) { 0796 // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding 0797 $thisILPS = ''; 0798 for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) { 0799 $twobytes = substr($parsedFrame['data_raw'], $i, 2); 0800 if ($twobytes === "\x00\x00") { 0801 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 0802 $thisILPS = ''; 0803 } else { 0804 $thisILPS .= $twobytes; 0805 } 0806 } 0807 if (strlen($thisILPS) > 2) { // 2-byte BOM 0808 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 0809 } 0810 } else { 0811 // ISO-8859-1 or UTF-8 or other single-byte-null character set 0812 $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']); 0813 } 0814 if (count($IPLS_parts_unsorted) == 1) { 0815 // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson" 0816 foreach ($IPLS_parts_unsorted as $key => $value) { 0817 $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value); 0818 $position = ''; 0819 foreach ($IPLS_parts_sorted as $person) { 0820 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 0821 } 0822 } 0823 } elseif ((count($IPLS_parts_unsorted) % 2) == 0) { 0824 $position = ''; 0825 $person = ''; 0826 foreach ($IPLS_parts_unsorted as $key => $value) { 0827 if (($key % 2) == 0) { 0828 $position = $value; 0829 } else { 0830 $person = $value; 0831 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 0832 $position = ''; 0833 $person = ''; 0834 } 0835 } 0836 } else { 0837 foreach ($IPLS_parts_unsorted as $key => $value) { 0838 $IPLS_parts[] = array($value); 0839 } 0840 } 0841 0842 } else { 0843 $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']); 0844 } 0845 $parsedFrame['data'] = $IPLS_parts; 0846 0847 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 0848 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 0849 } 0850 0851 0852 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier 0853 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier 0854 // There may only be one 'MCDI' frame in each tag 0855 // <Header for 'Music CD identifier', ID: 'MCDI'> 0856 // CD TOC <binary data> 0857 0858 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 0859 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 0860 } 0861 0862 0863 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes 0864 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes 0865 // There may only be one 'ETCO' frame in each tag 0866 // <Header for 'Event timing codes', ID: 'ETCO'> 0867 // Time stamp format $xx 0868 // Where time stamp format is: 0869 // $01 (32-bit value) MPEG frames from beginning of file 0870 // $02 (32-bit value) milliseconds from beginning of file 0871 // Followed by a list of key events in the following format: 0872 // Type of event $xx 0873 // Time stamp $xx (xx ...) 0874 // The 'Time stamp' is set to zero if directly at the beginning of the sound 0875 // or after the previous event. All events MUST be sorted in chronological order. 0876 0877 $frame_offset = 0; 0878 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0879 0880 while ($frame_offset < strlen($parsedFrame['data'])) { 0881 $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); 0882 $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); 0883 $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 0884 $frame_offset += 4; 0885 } 0886 unset($parsedFrame['data']); 0887 0888 0889 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table 0890 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table 0891 // There may only be one 'MLLT' frame in each tag 0892 // <Header for 'Location lookup table', ID: 'MLLT'> 0893 // MPEG frames between reference $xx xx 0894 // Bytes between reference $xx xx xx 0895 // Milliseconds between reference $xx xx xx 0896 // Bits for bytes deviation $xx 0897 // Bits for milliseconds dev. $xx 0898 // Then for every reference the following data is included; 0899 // Deviation in bytes %xxx.... 0900 // Deviation in milliseconds %xxx.... 0901 0902 $frame_offset = 0; 0903 $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); 0904 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); 0905 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); 0906 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); 0907 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); 0908 $parsedFrame['data'] = substr($parsedFrame['data'], 10); 0909 while ($frame_offset < strlen($parsedFrame['data'])) { 0910 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 0911 } 0912 $reference_counter = 0; 0913 while (strlen($deviationbitstream) > 0) { 0914 $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); 0915 $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); 0916 $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); 0917 $reference_counter++; 0918 } 0919 unset($parsedFrame['data']); 0920 0921 0922 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes 0923 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes 0924 // There may only be one 'SYTC' frame in each tag 0925 // <Header for 'Synchronised tempo codes', ID: 'SYTC'> 0926 // Time stamp format $xx 0927 // Tempo data <binary data> 0928 // Where time stamp format is: 0929 // $01 (32-bit value) MPEG frames from beginning of file 0930 // $02 (32-bit value) milliseconds from beginning of file 0931 0932 $frame_offset = 0; 0933 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0934 $timestamp_counter = 0; 0935 while ($frame_offset < strlen($parsedFrame['data'])) { 0936 $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0937 if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { 0938 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0939 } 0940 $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 0941 $frame_offset += 4; 0942 $timestamp_counter++; 0943 } 0944 unset($parsedFrame['data']); 0945 0946 0947 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription 0948 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription 0949 // There may be more than one 'Unsynchronised lyrics/text transcription' frame 0950 // in each tag, but only one with the same language and content descriptor. 0951 // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> 0952 // Text encoding $xx 0953 // Language $xx xx xx 0954 // Content descriptor <text string according to encoding> $00 (00) 0955 // Lyrics/text <full text string according to encoding> 0956 0957 $frame_offset = 0; 0958 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 0959 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 0960 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 0961 } 0962 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 0963 $frame_offset += 3; 0964 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 0965 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 0966 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 0967 } 0968 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 0969 if (ord($frame_description) === 0) { 0970 $frame_description = ''; 0971 } 0972 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 0973 0974 $parsedFrame['encodingid'] = $frame_textencoding; 0975 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 0976 0977 $parsedFrame['data'] = $parsedFrame['data']; 0978 $parsedFrame['language'] = $frame_language; 0979 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 0980 $parsedFrame['description'] = $frame_description; 0981 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 0982 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 0983 } 0984 unset($parsedFrame['data']); 0985 0986 0987 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text 0988 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text 0989 // There may be more than one 'SYLT' frame in each tag, 0990 // but only one with the same language and content descriptor. 0991 // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> 0992 // Text encoding $xx 0993 // Language $xx xx xx 0994 // Time stamp format $xx 0995 // $01 (32-bit value) MPEG frames from beginning of file 0996 // $02 (32-bit value) milliseconds from beginning of file 0997 // Content type $xx 0998 // Content descriptor <text string according to encoding> $00 (00) 0999 // Terminated text to be synced (typically a syllable) 1000 // Sync identifier (terminator to above string) $00 (00) 1001 // Time stamp $xx (xx ...) 1002 1003 $frame_offset = 0; 1004 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1005 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1006 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1007 } 1008 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1009 $frame_offset += 3; 1010 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1011 $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1012 $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); 1013 $parsedFrame['encodingid'] = $frame_textencoding; 1014 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1015 1016 $parsedFrame['language'] = $frame_language; 1017 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1018 1019 $timestampindex = 0; 1020 $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); 1021 while (strlen($frame_remainingdata)) { 1022 $frame_offset = 0; 1023 $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); 1024 if ($frame_terminatorpos === false) { 1025 $frame_remainingdata = ''; 1026 } else { 1027 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1028 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1029 } 1030 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); 1031 1032 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1033 if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { 1034 // timestamp probably omitted for first data item 1035 } else { 1036 $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); 1037 $frame_remainingdata = substr($frame_remainingdata, 4); 1038 } 1039 $timestampindex++; 1040 } 1041 } 1042 unset($parsedFrame['data']); 1043 1044 1045 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments 1046 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments 1047 // There may be more than one comment frame in each tag, 1048 // but only one with the same language and content descriptor. 1049 // <Header for 'Comment', ID: 'COMM'> 1050 // Text encoding $xx 1051 // Language $xx xx xx 1052 // Short content descrip. <text string according to encoding> $00 (00) 1053 // The actual text <full text string according to encoding> 1054 1055 if (strlen($parsedFrame['data']) < 5) { 1056 1057 $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; 1058 1059 } else { 1060 1061 $frame_offset = 0; 1062 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1063 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1064 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1065 } 1066 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1067 $frame_offset += 3; 1068 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1069 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1070 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1071 } 1072 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1073 if (ord($frame_description) === 0) { 1074 $frame_description = ''; 1075 } 1076 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1077 1078 $parsedFrame['encodingid'] = $frame_textencoding; 1079 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1080 1081 $parsedFrame['language'] = $frame_language; 1082 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1083 $parsedFrame['description'] = $frame_description; 1084 $parsedFrame['data'] = $frame_text; 1085 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1086 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); 1087 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { 1088 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1089 } else { 1090 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1091 } 1092 } 1093 1094 } 1095 1096 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) 1097 // There may be more than one 'RVA2' frame in each tag, 1098 // but only one with the same identification string 1099 // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> 1100 // Identification <text string> $00 1101 // The 'identification' string is used to identify the situation and/or 1102 // device where this adjustment should apply. The following is then 1103 // repeated for every channel: 1104 // Type of channel $xx 1105 // Volume adjustment $xx xx 1106 // Bits representing peak $xx 1107 // Peak volume $xx (xx ...) 1108 1109 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); 1110 $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); 1111 if (ord($frame_idstring) === 0) { 1112 $frame_idstring = ''; 1113 } 1114 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1115 $parsedFrame['description'] = $frame_idstring; 1116 $RVA2channelcounter = 0; 1117 while (strlen($frame_remainingdata) >= 5) { 1118 $frame_offset = 0; 1119 $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1120 $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid; 1121 $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); 1122 $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed 1123 $frame_offset += 2; 1124 $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1125 if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) { 1126 $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'; 1127 break; 1128 } 1129 $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); 1130 $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); 1131 $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); 1132 $RVA2channelcounter++; 1133 } 1134 unset($parsedFrame['data']); 1135 1136 1137 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) 1138 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) 1139 // There may only be one 'RVA' frame in each tag 1140 // <Header for 'Relative volume adjustment', ID: 'RVA'> 1141 // ID3v2.2 => Increment/decrement %000000ba 1142 // ID3v2.3 => Increment/decrement %00fedcba 1143 // Bits used for volume descr. $xx 1144 // Relative volume change, right $xx xx (xx ...) // a 1145 // Relative volume change, left $xx xx (xx ...) // b 1146 // Peak volume right $xx xx (xx ...) 1147 // Peak volume left $xx xx (xx ...) 1148 // ID3v2.3 only, optional (not present in ID3v2.2): 1149 // Relative volume change, right back $xx xx (xx ...) // c 1150 // Relative volume change, left back $xx xx (xx ...) // d 1151 // Peak volume right back $xx xx (xx ...) 1152 // Peak volume left back $xx xx (xx ...) 1153 // ID3v2.3 only, optional (not present in ID3v2.2): 1154 // Relative volume change, center $xx xx (xx ...) // e 1155 // Peak volume center $xx xx (xx ...) 1156 // ID3v2.3 only, optional (not present in ID3v2.2): 1157 // Relative volume change, bass $xx xx (xx ...) // f 1158 // Peak volume bass $xx xx (xx ...) 1159 1160 $frame_offset = 0; 1161 $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1162 $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); 1163 $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); 1164 $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1165 $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); 1166 $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1167 if ($parsedFrame['incdec']['right'] === false) { 1168 $parsedFrame['volumechange']['right'] *= -1; 1169 } 1170 $frame_offset += $frame_bytesvolume; 1171 $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1172 if ($parsedFrame['incdec']['left'] === false) { 1173 $parsedFrame['volumechange']['left'] *= -1; 1174 } 1175 $frame_offset += $frame_bytesvolume; 1176 $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1177 $frame_offset += $frame_bytesvolume; 1178 $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1179 $frame_offset += $frame_bytesvolume; 1180 if ($id3v2_majorversion == 3) { 1181 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1182 if (strlen($parsedFrame['data']) > 0) { 1183 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); 1184 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); 1185 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1186 if ($parsedFrame['incdec']['rightrear'] === false) { 1187 $parsedFrame['volumechange']['rightrear'] *= -1; 1188 } 1189 $frame_offset += $frame_bytesvolume; 1190 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1191 if ($parsedFrame['incdec']['leftrear'] === false) { 1192 $parsedFrame['volumechange']['leftrear'] *= -1; 1193 } 1194 $frame_offset += $frame_bytesvolume; 1195 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1196 $frame_offset += $frame_bytesvolume; 1197 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1198 $frame_offset += $frame_bytesvolume; 1199 } 1200 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1201 if (strlen($parsedFrame['data']) > 0) { 1202 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); 1203 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1204 if ($parsedFrame['incdec']['center'] === false) { 1205 $parsedFrame['volumechange']['center'] *= -1; 1206 } 1207 $frame_offset += $frame_bytesvolume; 1208 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1209 $frame_offset += $frame_bytesvolume; 1210 } 1211 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1212 if (strlen($parsedFrame['data']) > 0) { 1213 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); 1214 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1215 if ($parsedFrame['incdec']['bass'] === false) { 1216 $parsedFrame['volumechange']['bass'] *= -1; 1217 } 1218 $frame_offset += $frame_bytesvolume; 1219 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1220 $frame_offset += $frame_bytesvolume; 1221 } 1222 } 1223 unset($parsedFrame['data']); 1224 1225 1226 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) 1227 // There may be more than one 'EQU2' frame in each tag, 1228 // but only one with the same identification string 1229 // <Header of 'Equalisation (2)', ID: 'EQU2'> 1230 // Interpolation method $xx 1231 // $00 Band 1232 // $01 Linear 1233 // Identification <text string> $00 1234 // The following is then repeated for every adjustment point 1235 // Frequency $xx xx 1236 // Volume adjustment $xx xx 1237 1238 $frame_offset = 0; 1239 $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1240 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1241 $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1242 if (ord($frame_idstring) === 0) { 1243 $frame_idstring = ''; 1244 } 1245 $parsedFrame['description'] = $frame_idstring; 1246 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1247 while (strlen($frame_remainingdata)) { 1248 $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; 1249 $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); 1250 $frame_remainingdata = substr($frame_remainingdata, 4); 1251 } 1252 $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; 1253 unset($parsedFrame['data']); 1254 1255 1256 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) 1257 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) 1258 // There may only be one 'EQUA' frame in each tag 1259 // <Header for 'Relative volume adjustment', ID: 'EQU'> 1260 // Adjustment bits $xx 1261 // This is followed by 2 bytes + ('adjustment bits' rounded up to the 1262 // nearest byte) for every equalisation band in the following format, 1263 // giving a frequency range of 0 - 32767Hz: 1264 // Increment/decrement %x (MSB of the Frequency) 1265 // Frequency (lower 15 bits) 1266 // Adjustment $xx (xx ...) 1267 1268 $frame_offset = 0; 1269 $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); 1270 $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); 1271 1272 $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); 1273 while (strlen($frame_remainingdata) > 0) { 1274 $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); 1275 $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); 1276 $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); 1277 $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; 1278 $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); 1279 if ($parsedFrame[$frame_frequency]['incdec'] === false) { 1280 $parsedFrame[$frame_frequency]['adjustment'] *= -1; 1281 } 1282 $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); 1283 } 1284 unset($parsedFrame['data']); 1285 1286 1287 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb 1288 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb 1289 // There may only be one 'RVRB' frame in each tag. 1290 // <Header for 'Reverb', ID: 'RVRB'> 1291 // Reverb left (ms) $xx xx 1292 // Reverb right (ms) $xx xx 1293 // Reverb bounces, left $xx 1294 // Reverb bounces, right $xx 1295 // Reverb feedback, left to left $xx 1296 // Reverb feedback, left to right $xx 1297 // Reverb feedback, right to right $xx 1298 // Reverb feedback, right to left $xx 1299 // Premix left to right $xx 1300 // Premix right to left $xx 1301 1302 $frame_offset = 0; 1303 $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1304 $frame_offset += 2; 1305 $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1306 $frame_offset += 2; 1307 $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1308 $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1309 $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1310 $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1311 $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1312 $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1313 $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1314 $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1315 unset($parsedFrame['data']); 1316 1317 1318 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture 1319 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture 1320 // There may be several pictures attached to one file, 1321 // each in their individual 'APIC' frame, but only one 1322 // with the same content descriptor 1323 // <Header for 'Attached picture', ID: 'APIC'> 1324 // Text encoding $xx 1325 // ID3v2.3+ => MIME type <text string> $00 1326 // ID3v2.2 => Image format $xx xx xx 1327 // Picture type $xx 1328 // Description <text string according to encoding> $00 (00) 1329 // Picture data <binary data> 1330 1331 $frame_offset = 0; 1332 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1333 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1334 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1335 } 1336 1337 if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { 1338 $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); 1339 if (strtolower($frame_imagetype) == 'ima') { 1340 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted 1341 // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) 1342 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1343 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1344 if (ord($frame_mimetype) === 0) { 1345 $frame_mimetype = ''; 1346 } 1347 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); 1348 if ($frame_imagetype == 'JPEG') { 1349 $frame_imagetype = 'JPG'; 1350 } 1351 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1352 } else { 1353 $frame_offset += 3; 1354 } 1355 } 1356 if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { 1357 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1358 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1359 if (ord($frame_mimetype) === 0) { 1360 $frame_mimetype = ''; 1361 } 1362 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1363 } 1364 1365 $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1366 1367 if ($frame_offset >= $parsedFrame['datalength']) { 1368 $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); 1369 } else { 1370 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1371 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1372 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1373 } 1374 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1375 if (ord($frame_description) === 0) { 1376 $frame_description = ''; 1377 } 1378 $parsedFrame['encodingid'] = $frame_textencoding; 1379 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1380 1381 if ($id3v2_majorversion == 2) { 1382 $parsedFrame['imagetype'] = $frame_imagetype; 1383 } else { 1384 $parsedFrame['mime'] = $frame_mimetype; 1385 } 1386 $parsedFrame['picturetypeid'] = $frame_picturetype; 1387 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); 1388 $parsedFrame['description'] = $frame_description; 1389 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1390 $parsedFrame['datalength'] = strlen($parsedFrame['data']); 1391 1392 $parsedFrame['image_mime'] = ''; 1393 $imageinfo = array(); 1394 $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); 1395 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { 1396 $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); 1397 if ($imagechunkcheck[0]) { 1398 $parsedFrame['image_width'] = $imagechunkcheck[0]; 1399 } 1400 if ($imagechunkcheck[1]) { 1401 $parsedFrame['image_height'] = $imagechunkcheck[1]; 1402 } 1403 } 1404 1405 do { 1406 if ($this->getid3->option_save_attachments === false) { 1407 // skip entirely 1408 unset($parsedFrame['data']); 1409 break; 1410 } 1411 if ($this->getid3->option_save_attachments === true) { 1412 // great 1413 /* 1414 } elseif (is_int($this->getid3->option_save_attachments)) { 1415 if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) { 1416 // too big, skip 1417 $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; 1418 unset($parsedFrame['data']); 1419 break; 1420 } 1421 */ 1422 } elseif (is_string($this->getid3->option_save_attachments)) { 1423 $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); 1424 if (!is_dir($dir) || !is_writable($dir)) { 1425 // cannot write, skip 1426 $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)'; 1427 unset($parsedFrame['data']); 1428 break; 1429 } 1430 } 1431 // if we get this far, must be OK 1432 if (is_string($this->getid3->option_save_attachments)) { 1433 $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; 1434 if (!file_exists($destination_filename) || is_writable($destination_filename)) { 1435 file_put_contents($destination_filename, $parsedFrame['data']); 1436 } else { 1437 $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'; 1438 } 1439 $parsedFrame['data_filename'] = $destination_filename; 1440 unset($parsedFrame['data']); 1441 } else { 1442 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1443 if (!isset($info['id3v2']['comments']['picture'])) { 1444 $info['id3v2']['comments']['picture'] = array(); 1445 } 1446 $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); 1447 } 1448 } 1449 } while (false); 1450 } 1451 1452 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object 1453 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object 1454 // There may be more than one 'GEOB' frame in each tag, 1455 // but only one with the same content descriptor 1456 // <Header for 'General encapsulated object', ID: 'GEOB'> 1457 // Text encoding $xx 1458 // MIME type <text string> $00 1459 // Filename <text string according to encoding> $00 (00) 1460 // Content description <text string according to encoding> $00 (00) 1461 // Encapsulated object <binary data> 1462 1463 $frame_offset = 0; 1464 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1465 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1466 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1467 } 1468 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1469 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1470 if (ord($frame_mimetype) === 0) { 1471 $frame_mimetype = ''; 1472 } 1473 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1474 1475 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1476 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1477 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1478 } 1479 $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1480 if (ord($frame_filename) === 0) { 1481 $frame_filename = ''; 1482 } 1483 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1484 1485 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1486 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1487 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1488 } 1489 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1490 if (ord($frame_description) === 0) { 1491 $frame_description = ''; 1492 } 1493 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1494 1495 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); 1496 $parsedFrame['encodingid'] = $frame_textencoding; 1497 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1498 1499 $parsedFrame['mime'] = $frame_mimetype; 1500 $parsedFrame['filename'] = $frame_filename; 1501 $parsedFrame['description'] = $frame_description; 1502 unset($parsedFrame['data']); 1503 1504 1505 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter 1506 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter 1507 // There may only be one 'PCNT' frame in each tag. 1508 // When the counter reaches all one's, one byte is inserted in 1509 // front of the counter thus making the counter eight bits bigger 1510 // <Header for 'Play counter', ID: 'PCNT'> 1511 // Counter $xx xx xx xx (xx ...) 1512 1513 $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); 1514 1515 1516 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter 1517 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter 1518 // There may be more than one 'POPM' frame in each tag, 1519 // but only one with the same email address 1520 // <Header for 'Popularimeter', ID: 'POPM'> 1521 // Email to user <text string> $00 1522 // Rating $xx 1523 // Counter $xx xx xx xx (xx ...) 1524 1525 $frame_offset = 0; 1526 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1527 $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1528 if (ord($frame_emailaddress) === 0) { 1529 $frame_emailaddress = ''; 1530 } 1531 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1532 $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1533 $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1534 $parsedFrame['email'] = $frame_emailaddress; 1535 $parsedFrame['rating'] = $frame_rating; 1536 unset($parsedFrame['data']); 1537 1538 1539 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size 1540 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size 1541 // There may only be one 'RBUF' frame in each tag 1542 // <Header for 'Recommended buffer size', ID: 'RBUF'> 1543 // Buffer size $xx xx xx 1544 // Embedded info flag %0000000x 1545 // Offset to next tag $xx xx xx xx 1546 1547 $frame_offset = 0; 1548 $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); 1549 $frame_offset += 3; 1550 1551 $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1552 $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); 1553 $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1554 unset($parsedFrame['data']); 1555 1556 1557 } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) 1558 // There may be more than one 'CRM' frame in a tag, 1559 // but only one with the same 'owner identifier' 1560 // <Header for 'Encrypted meta frame', ID: 'CRM'> 1561 // Owner identifier <textstring> $00 (00) 1562 // Content/explanation <textstring> $00 (00) 1563 // Encrypted datablock <binary data> 1564 1565 $frame_offset = 0; 1566 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1567 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1568 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1569 1570 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1571 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1572 if (ord($frame_description) === 0) { 1573 $frame_description = ''; 1574 } 1575 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1576 1577 $parsedFrame['ownerid'] = $frame_ownerid; 1578 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1579 $parsedFrame['description'] = $frame_description; 1580 unset($parsedFrame['data']); 1581 1582 1583 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption 1584 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption 1585 // There may be more than one 'AENC' frames in a tag, 1586 // but only one with the same 'Owner identifier' 1587 // <Header for 'Audio encryption', ID: 'AENC'> 1588 // Owner identifier <text string> $00 1589 // Preview start $xx xx 1590 // Preview length $xx xx 1591 // Encryption info <binary data> 1592 1593 $frame_offset = 0; 1594 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1595 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1596 if (ord($frame_ownerid) === 0) { 1597 $frame_ownerid == ''; 1598 } 1599 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1600 $parsedFrame['ownerid'] = $frame_ownerid; 1601 $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1602 $frame_offset += 2; 1603 $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1604 $frame_offset += 2; 1605 $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); 1606 unset($parsedFrame['data']); 1607 1608 1609 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information 1610 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information 1611 // There may be more than one 'LINK' frame in a tag, 1612 // but only one with the same contents 1613 // <Header for 'Linked information', ID: 'LINK'> 1614 // ID3v2.3+ => Frame identifier $xx xx xx xx 1615 // ID3v2.2 => Frame identifier $xx xx xx 1616 // URL <text string> $00 1617 // ID and additional data <text string(s)> 1618 1619 $frame_offset = 0; 1620 if ($id3v2_majorversion == 2) { 1621 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); 1622 $frame_offset += 3; 1623 } else { 1624 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); 1625 $frame_offset += 4; 1626 } 1627 1628 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1629 $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1630 if (ord($frame_url) === 0) { 1631 $frame_url = ''; 1632 } 1633 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1634 $parsedFrame['url'] = $frame_url; 1635 1636 $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); 1637 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 1638 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); 1639 } 1640 unset($parsedFrame['data']); 1641 1642 1643 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) 1644 // There may only be one 'POSS' frame in each tag 1645 // <Head for 'Position synchronisation', ID: 'POSS'> 1646 // Time stamp format $xx 1647 // Position $xx (xx ...) 1648 1649 $frame_offset = 0; 1650 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1651 $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1652 unset($parsedFrame['data']); 1653 1654 1655 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) 1656 // There may be more than one 'Terms of use' frame in a tag, 1657 // but only one with the same 'Language' 1658 // <Header for 'Terms of use frame', ID: 'USER'> 1659 // Text encoding $xx 1660 // Language $xx xx xx 1661 // The actual text <text string according to encoding> 1662 1663 $frame_offset = 0; 1664 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1665 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1666 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1667 } 1668 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1669 $frame_offset += 3; 1670 $parsedFrame['language'] = $frame_language; 1671 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1672 $parsedFrame['encodingid'] = $frame_textencoding; 1673 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1674 1675 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1676 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1677 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1678 } 1679 unset($parsedFrame['data']); 1680 1681 1682 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) 1683 // There may only be one 'OWNE' frame in a tag 1684 // <Header for 'Ownership frame', ID: 'OWNE'> 1685 // Text encoding $xx 1686 // Price paid <text string> $00 1687 // Date of purch. <text string> 1688 // Seller <text string according to encoding> 1689 1690 $frame_offset = 0; 1691 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1692 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1693 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1694 } 1695 $parsedFrame['encodingid'] = $frame_textencoding; 1696 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1697 1698 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1699 $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1700 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1701 1702 $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); 1703 $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); 1704 $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); 1705 1706 $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); 1707 if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { 1708 $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); 1709 } 1710 $frame_offset += 8; 1711 1712 $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); 1713 unset($parsedFrame['data']); 1714 1715 1716 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) 1717 // There may be more than one 'commercial frame' in a tag, 1718 // but no two may be identical 1719 // <Header for 'Commercial frame', ID: 'COMR'> 1720 // Text encoding $xx 1721 // Price string <text string> $00 1722 // Valid until <text string> 1723 // Contact URL <text string> $00 1724 // Received as $xx 1725 // Name of seller <text string according to encoding> $00 (00) 1726 // Description <text string according to encoding> $00 (00) 1727 // Picture MIME type <string> $00 1728 // Seller logo <binary data> 1729 1730 $frame_offset = 0; 1731 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1732 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1733 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1734 } 1735 1736 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1737 $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1738 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1739 $frame_rawpricearray = explode('/', $frame_pricestring); 1740 foreach ($frame_rawpricearray as $key => $val) { 1741 $frame_currencyid = substr($val, 0, 3); 1742 $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); 1743 $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); 1744 } 1745 1746 $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); 1747 $frame_offset += 8; 1748 1749 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1750 $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1751 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1752 1753 $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1754 1755 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1756 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1757 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1758 } 1759 $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1760 if (ord($frame_sellername) === 0) { 1761 $frame_sellername = ''; 1762 } 1763 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1764 1765 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1766 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1767 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1768 } 1769 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1770 if (ord($frame_description) === 0) { 1771 $frame_description = ''; 1772 } 1773 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1774 1775 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1776 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1777 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1778 1779 $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); 1780 1781 $parsedFrame['encodingid'] = $frame_textencoding; 1782 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1783 1784 $parsedFrame['pricevaliduntil'] = $frame_datestring; 1785 $parsedFrame['contacturl'] = $frame_contacturl; 1786 $parsedFrame['receivedasid'] = $frame_receivedasid; 1787 $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); 1788 $parsedFrame['sellername'] = $frame_sellername; 1789 $parsedFrame['description'] = $frame_description; 1790 $parsedFrame['mime'] = $frame_mimetype; 1791 $parsedFrame['logo'] = $frame_sellerlogo; 1792 unset($parsedFrame['data']); 1793 1794 1795 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) 1796 // There may be several 'ENCR' frames in a tag, 1797 // but only one containing the same symbol 1798 // and only one containing the same owner identifier 1799 // <Header for 'Encryption method registration', ID: 'ENCR'> 1800 // Owner identifier <text string> $00 1801 // Method symbol $xx 1802 // Encryption data <binary data> 1803 1804 $frame_offset = 0; 1805 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1806 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1807 if (ord($frame_ownerid) === 0) { 1808 $frame_ownerid = ''; 1809 } 1810 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1811 1812 $parsedFrame['ownerid'] = $frame_ownerid; 1813 $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1814 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1815 1816 1817 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) 1818 1819 // There may be several 'GRID' frames in a tag, 1820 // but only one containing the same symbol 1821 // and only one containing the same owner identifier 1822 // <Header for 'Group ID registration', ID: 'GRID'> 1823 // Owner identifier <text string> $00 1824 // Group symbol $xx 1825 // Group dependent data <binary data> 1826 1827 $frame_offset = 0; 1828 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1829 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1830 if (ord($frame_ownerid) === 0) { 1831 $frame_ownerid = ''; 1832 } 1833 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1834 1835 $parsedFrame['ownerid'] = $frame_ownerid; 1836 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1837 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1838 1839 1840 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) 1841 // The tag may contain more than one 'PRIV' frame 1842 // but only with different contents 1843 // <Header for 'Private frame', ID: 'PRIV'> 1844 // Owner identifier <text string> $00 1845 // The private data <binary data> 1846 1847 $frame_offset = 0; 1848 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1849 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1850 if (ord($frame_ownerid) === 0) { 1851 $frame_ownerid = ''; 1852 } 1853 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1854 1855 $parsedFrame['ownerid'] = $frame_ownerid; 1856 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1857 1858 1859 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) 1860 // There may be more than one 'signature frame' in a tag, 1861 // but no two may be identical 1862 // <Header for 'Signature frame', ID: 'SIGN'> 1863 // Group symbol $xx 1864 // Signature <binary data> 1865 1866 $frame_offset = 0; 1867 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1868 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1869 1870 1871 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) 1872 // There may only be one 'seek frame' in a tag 1873 // <Header for 'Seek frame', ID: 'SEEK'> 1874 // Minimum offset to next tag $xx xx xx xx 1875 1876 $frame_offset = 0; 1877 $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1878 1879 1880 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) 1881 // There may only be one 'audio seek point index' frame in a tag 1882 // <Header for 'Seek Point Index', ID: 'ASPI'> 1883 // Indexed data start (S) $xx xx xx xx 1884 // Indexed data length (L) $xx xx xx xx 1885 // Number of index points (N) $xx xx 1886 // Bits per index point (b) $xx 1887 // Then for every index point the following data is included: 1888 // Fraction at index (Fi) $xx (xx) 1889 1890 $frame_offset = 0; 1891 $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1892 $frame_offset += 4; 1893 $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1894 $frame_offset += 4; 1895 $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1896 $frame_offset += 2; 1897 $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1898 $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); 1899 for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) { 1900 $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); 1901 $frame_offset += $frame_bytesperpoint; 1902 } 1903 unset($parsedFrame['data']); 1904 1905 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment 1906 // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 1907 // There may only be one 'RGAD' frame in a tag 1908 // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> 1909 // Peak Amplitude $xx $xx $xx $xx 1910 // Radio Replay Gain Adjustment %aaabbbcd %dddddddd 1911 // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd 1912 // a - name code 1913 // b - originator code 1914 // c - sign bit 1915 // d - replay gain adjustment 1916 1917 $frame_offset = 0; 1918 $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); 1919 $frame_offset += 4; 1920 $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); 1921 $frame_offset += 2; 1922 $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); 1923 $frame_offset += 2; 1924 $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); 1925 $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); 1926 $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); 1927 $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); 1928 $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); 1929 $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); 1930 $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); 1931 $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); 1932 $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); 1933 $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); 1934 $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); 1935 $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); 1936 $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); 1937 $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); 1938 1939 $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; 1940 $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; 1941 $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; 1942 $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; 1943 $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; 1944 1945 unset($parsedFrame['data']); 1946 1947 } 1948 1949 return true; 1950 } 1951 1952 1953 public function DeUnsynchronise($data) { 1954 return str_replace("\xFF\x00", "\xFF", $data); 1955 } 1956 1957 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { 1958 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( 1959 0x00 => 'No more than 128 frames and 1 MB total tag size', 1960 0x01 => 'No more than 64 frames and 128 KB total tag size', 1961 0x02 => 'No more than 32 frames and 40 KB total tag size', 1962 0x03 => 'No more than 32 frames and 4 KB total tag size', 1963 ); 1964 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); 1965 } 1966 1967 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { 1968 static $LookupExtendedHeaderRestrictionsTextEncodings = array( 1969 0x00 => 'No restrictions', 1970 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', 1971 ); 1972 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); 1973 } 1974 1975 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { 1976 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( 1977 0x00 => 'No restrictions', 1978 0x01 => 'No string is longer than 1024 characters', 1979 0x02 => 'No string is longer than 128 characters', 1980 0x03 => 'No string is longer than 30 characters', 1981 ); 1982 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); 1983 } 1984 1985 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { 1986 static $LookupExtendedHeaderRestrictionsImageEncoding = array( 1987 0x00 => 'No restrictions', 1988 0x01 => 'Images are encoded only with PNG or JPEG', 1989 ); 1990 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); 1991 } 1992 1993 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { 1994 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( 1995 0x00 => 'No restrictions', 1996 0x01 => 'All images are 256x256 pixels or smaller', 1997 0x02 => 'All images are 64x64 pixels or smaller', 1998 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', 1999 ); 2000 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); 2001 } 2002 2003 public function LookupCurrencyUnits($currencyid) { 2004 2005 $begin = __LINE__; 2006 2007 /** This is not a comment! 2008 2009 2010 AED Dirhams 2011 AFA Afghanis 2012 ALL Leke 2013 AMD Drams 2014 ANG Guilders 2015 AOA Kwanza 2016 ARS Pesos 2017 ATS Schillings 2018 AUD Dollars 2019 AWG Guilders 2020 AZM Manats 2021 BAM Convertible Marka 2022 BBD Dollars 2023 BDT Taka 2024 BEF Francs 2025 BGL Leva 2026 BHD Dinars 2027 BIF Francs 2028 BMD Dollars 2029 BND Dollars 2030 BOB Bolivianos 2031 BRL Brazil Real 2032 BSD Dollars 2033 BTN Ngultrum 2034 BWP Pulas 2035 BYR Rubles 2036 BZD Dollars 2037 CAD Dollars 2038 CDF Congolese Francs 2039 CHF Francs 2040 CLP Pesos 2041 CNY Yuan Renminbi 2042 COP Pesos 2043 CRC Colones 2044 CUP Pesos 2045 CVE Escudos 2046 CYP Pounds 2047 CZK Koruny 2048 DEM Deutsche Marks 2049 DJF Francs 2050 DKK Kroner 2051 DOP Pesos 2052 DZD Algeria Dinars 2053 EEK Krooni 2054 EGP Pounds 2055 ERN Nakfa 2056 ESP Pesetas 2057 ETB Birr 2058 EUR Euro 2059 FIM Markkaa 2060 FJD Dollars 2061 FKP Pounds 2062 FRF Francs 2063 GBP Pounds 2064 GEL Lari 2065 GGP Pounds 2066 GHC Cedis 2067 GIP Pounds 2068 GMD Dalasi 2069 GNF Francs 2070 GRD Drachmae 2071 GTQ Quetzales 2072 GYD Dollars 2073 HKD Dollars 2074 HNL Lempiras 2075 HRK Kuna 2076 HTG Gourdes 2077 HUF Forints 2078 IDR Rupiahs 2079 IEP Pounds 2080 ILS New Shekels 2081 IMP Pounds 2082 INR Rupees 2083 IQD Dinars 2084 IRR Rials 2085 ISK Kronur 2086 ITL Lire 2087 JEP Pounds 2088 JMD Dollars 2089 JOD Dinars 2090 JPY Yen 2091 KES Shillings 2092 KGS Soms 2093 KHR Riels 2094 KMF Francs 2095 KPW Won 2096 KWD Dinars 2097 KYD Dollars 2098 KZT Tenge 2099 LAK Kips 2100 LBP Pounds 2101 LKR Rupees 2102 LRD Dollars 2103 LSL Maloti 2104 LTL Litai 2105 LUF Francs 2106 LVL Lati 2107 LYD Dinars 2108 MAD Dirhams 2109 MDL Lei 2110 MGF Malagasy Francs 2111 MKD Denars 2112 MMK Kyats 2113 MNT Tugriks 2114 MOP Patacas 2115 MRO Ouguiyas 2116 MTL Liri 2117 MUR Rupees 2118 MVR Rufiyaa 2119 MWK Kwachas 2120 MXN Pesos 2121 MYR Ringgits 2122 MZM Meticais 2123 NAD Dollars 2124 NGN Nairas 2125 NIO Gold Cordobas 2126 NLG Guilders 2127 NOK Krone 2128 NPR Nepal Rupees 2129 NZD Dollars 2130 OMR Rials 2131 PAB Balboa 2132 PEN Nuevos Soles 2133 PGK Kina 2134 PHP Pesos 2135 PKR Rupees 2136 PLN Zlotych 2137 PTE Escudos 2138 PYG Guarani 2139 QAR Rials 2140 ROL Lei 2141 RUR Rubles 2142 RWF Rwanda Francs 2143 SAR Riyals 2144 SBD Dollars 2145 SCR Rupees 2146 SDD Dinars 2147 SEK Kronor 2148 SGD Dollars 2149 SHP Pounds 2150 SIT Tolars 2151 SKK Koruny 2152 SLL Leones 2153 SOS Shillings 2154 SPL Luigini 2155 SRG Guilders 2156 STD Dobras 2157 SVC Colones 2158 SYP Pounds 2159 SZL Emalangeni 2160 THB Baht 2161 TJR Rubles 2162 TMM Manats 2163 TND Dinars 2164 TOP Pa'anga 2165 TRL Liras 2166 TTD Dollars 2167 TVD Tuvalu Dollars 2168 TWD New Dollars 2169 TZS Shillings 2170 UAH Hryvnia 2171 UGX Shillings 2172 USD Dollars 2173 UYU Pesos 2174 UZS Sums 2175 VAL Lire 2176 VEB Bolivares 2177 VND Dong 2178 VUV Vatu 2179 WST Tala 2180 XAF Francs 2181 XAG Ounces 2182 XAU Ounces 2183 XCD Dollars 2184 XDR Special Drawing Rights 2185 XPD Ounces 2186 XPF Francs 2187 XPT Ounces 2188 YER Rials 2189 YUM New Dinars 2190 ZAR Rand 2191 ZMK Kwacha 2192 ZWD Zimbabwe Dollars 2193 2194 */ 2195 2196 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); 2197 } 2198 2199 2200 public function LookupCurrencyCountry($currencyid) { 2201 2202 $begin = __LINE__; 2203 2204 /** This is not a comment! 2205 2206 AED United Arab Emirates 2207 AFA Afghanistan 2208 ALL Albania 2209 AMD Armenia 2210 ANG Netherlands Antilles 2211 AOA Angola 2212 ARS Argentina 2213 ATS Austria 2214 AUD Australia 2215 AWG Aruba 2216 AZM Azerbaijan 2217 BAM Bosnia and Herzegovina 2218 BBD Barbados 2219 BDT Bangladesh 2220 BEF Belgium 2221 BGL Bulgaria 2222 BHD Bahrain 2223 BIF Burundi 2224 BMD Bermuda 2225 BND Brunei Darussalam 2226 BOB Bolivia 2227 BRL Brazil 2228 BSD Bahamas 2229 BTN Bhutan 2230 BWP Botswana 2231 BYR Belarus 2232 BZD Belize 2233 CAD Canada 2234 CDF Congo/Kinshasa 2235 CHF Switzerland 2236 CLP Chile 2237 CNY China 2238 COP Colombia 2239 CRC Costa Rica 2240 CUP Cuba 2241 CVE Cape Verde 2242 CYP Cyprus 2243 CZK Czech Republic 2244 DEM Germany 2245 DJF Djibouti 2246 DKK Denmark 2247 DOP Dominican Republic 2248 DZD Algeria 2249 EEK Estonia 2250 EGP Egypt 2251 ERN Eritrea 2252 ESP Spain 2253 ETB Ethiopia 2254 EUR Euro Member Countries 2255 FIM Finland 2256 FJD Fiji 2257 FKP Falkland Islands (Malvinas) 2258 FRF France 2259 GBP United Kingdom 2260 GEL Georgia 2261 GGP Guernsey 2262 GHC Ghana 2263 GIP Gibraltar 2264 GMD Gambia 2265 GNF Guinea 2266 GRD Greece 2267 GTQ Guatemala 2268 GYD Guyana 2269 HKD Hong Kong 2270 HNL Honduras 2271 HRK Croatia 2272 HTG Haiti 2273 HUF Hungary 2274 IDR Indonesia 2275 IEP Ireland (Eire) 2276 ILS Israel 2277 IMP Isle of Man 2278 INR India 2279 IQD Iraq 2280 IRR Iran 2281 ISK Iceland 2282 ITL Italy 2283 JEP Jersey 2284 JMD Jamaica 2285 JOD Jordan 2286 JPY Japan 2287 KES Kenya 2288 KGS Kyrgyzstan 2289 KHR Cambodia 2290 KMF Comoros 2291 KPW Korea 2292 KWD Kuwait 2293 KYD Cayman Islands 2294 KZT Kazakstan 2295 LAK Laos 2296 LBP Lebanon 2297 LKR Sri Lanka 2298 LRD Liberia 2299 LSL Lesotho 2300 LTL Lithuania 2301 LUF Luxembourg 2302 LVL Latvia 2303 LYD Libya 2304 MAD Morocco 2305 MDL Moldova 2306 MGF Madagascar 2307 MKD Macedonia 2308 MMK Myanmar (Burma) 2309 MNT Mongolia 2310 MOP Macau 2311 MRO Mauritania 2312 MTL Malta 2313 MUR Mauritius 2314 MVR Maldives (Maldive Islands) 2315 MWK Malawi 2316 MXN Mexico 2317 MYR Malaysia 2318 MZM Mozambique 2319 NAD Namibia 2320 NGN Nigeria 2321 NIO Nicaragua 2322 NLG Netherlands (Holland) 2323 NOK Norway 2324 NPR Nepal 2325 NZD New Zealand 2326 OMR Oman 2327 PAB Panama 2328 PEN Peru 2329 PGK Papua New Guinea 2330 PHP Philippines 2331 PKR Pakistan 2332 PLN Poland 2333 PTE Portugal 2334 PYG Paraguay 2335 QAR Qatar 2336 ROL Romania 2337 RUR Russia 2338 RWF Rwanda 2339 SAR Saudi Arabia 2340 SBD Solomon Islands 2341 SCR Seychelles 2342 SDD Sudan 2343 SEK Sweden 2344 SGD Singapore 2345 SHP Saint Helena 2346 SIT Slovenia 2347 SKK Slovakia 2348 SLL Sierra Leone 2349 SOS Somalia 2350 SPL Seborga 2351 SRG Suriname 2352 STD São Tome and Principe 2353 SVC El Salvador 2354 SYP Syria 2355 SZL Swaziland 2356 THB Thailand 2357 TJR Tajikistan 2358 TMM Turkmenistan 2359 TND Tunisia 2360 TOP Tonga 2361 TRL Turkey 2362 TTD Trinidad and Tobago 2363 TVD Tuvalu 2364 TWD Taiwan 2365 TZS Tanzania 2366 UAH Ukraine 2367 UGX Uganda 2368 USD United States of America 2369 UYU Uruguay 2370 UZS Uzbekistan 2371 VAL Vatican City 2372 VEB Venezuela 2373 VND Viet Nam 2374 VUV Vanuatu 2375 WST Samoa 2376 XAF Communauté Financière Africaine 2377 XAG Silver 2378 XAU Gold 2379 XCD East Caribbean 2380 XDR International Monetary Fund 2381 XPD Palladium 2382 XPF Comptoirs Français du Pacifique 2383 XPT Platinum 2384 YER Yemen 2385 YUM Yugoslavia 2386 ZAR South Africa 2387 ZMK Zambia 2388 ZWD Zimbabwe 2389 2390 */ 2391 2392 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); 2393 } 2394 2395 2396 2397 public static function LanguageLookup($languagecode, $casesensitive=false) { 2398 2399 if (!$casesensitive) { 2400 $languagecode = strtolower($languagecode); 2401 } 2402 2403 // http://www.id3.org/id3v2.4.0-structure.txt 2404 // [4. ID3v2 frame overview] 2405 // The three byte language field, present in several frames, is used to 2406 // describe the language of the frame's content, according to ISO-639-2 2407 // [ISO-639-2]. The language should be represented in lower case. If the 2408 // language is not known the string "XXX" should be used. 2409 2410 2411 // ISO 639-2 - http://www.id3.org/iso639-2.html 2412 2413 $begin = __LINE__; 2414 2415 /** This is not a comment! 2416 2417 XXX unknown 2418 xxx unknown 2419 aar Afar 2420 abk Abkhazian 2421 ace Achinese 2422 ach Acoli 2423 ada Adangme 2424 afa Afro-Asiatic (Other) 2425 afh Afrihili 2426 afr Afrikaans 2427 aka Akan 2428 akk Akkadian 2429 alb Albanian 2430 ale Aleut 2431 alg Algonquian Languages 2432 amh Amharic 2433 ang English, Old (ca. 450-1100) 2434 apa Apache Languages 2435 ara Arabic 2436 arc Aramaic 2437 arm Armenian 2438 arn Araucanian 2439 arp Arapaho 2440 art Artificial (Other) 2441 arw Arawak 2442 asm Assamese 2443 ath Athapascan Languages 2444 ava Avaric 2445 ave Avestan 2446 awa Awadhi 2447 aym Aymara 2448 aze Azerbaijani 2449 bad Banda 2450 bai Bamileke Languages 2451 bak Bashkir 2452 bal Baluchi 2453 bam Bambara 2454 ban Balinese 2455 baq Basque 2456 bas Basa 2457 bat Baltic (Other) 2458 bej Beja 2459 bel Byelorussian 2460 bem Bemba 2461 ben Bengali 2462 ber Berber (Other) 2463 bho Bhojpuri 2464 bih Bihari 2465 bik Bikol 2466 bin Bini 2467 bis Bislama 2468 bla Siksika 2469 bnt Bantu (Other) 2470 bod Tibetan 2471 bra Braj 2472 bre Breton 2473 bua Buriat 2474 bug Buginese 2475 bul Bulgarian 2476 bur Burmese 2477 cad Caddo 2478 cai Central American Indian (Other) 2479 car Carib 2480 cat Catalan 2481 cau Caucasian (Other) 2482 ceb Cebuano 2483 cel Celtic (Other) 2484 ces Czech 2485 cha Chamorro 2486 chb Chibcha 2487 che Chechen 2488 chg Chagatai 2489 chi Chinese 2490 chm Mari 2491 chn Chinook jargon 2492 cho Choctaw 2493 chr Cherokee 2494 chu Church Slavic 2495 chv Chuvash 2496 chy Cheyenne 2497 cop Coptic 2498 cor Cornish 2499 cos Corsican 2500 cpe Creoles and Pidgins, English-based (Other) 2501 cpf Creoles and Pidgins, French-based (Other) 2502 cpp Creoles and Pidgins, Portuguese-based (Other) 2503 cre Cree 2504 crp Creoles and Pidgins (Other) 2505 cus Cushitic (Other) 2506 cym Welsh 2507 cze Czech 2508 dak Dakota 2509 dan Danish 2510 del Delaware 2511 deu German 2512 din Dinka 2513 div Divehi 2514 doi Dogri 2515 dra Dravidian (Other) 2516 dua Duala 2517 dum Dutch, Middle (ca. 1050-1350) 2518 dut Dutch 2519 dyu Dyula 2520 dzo Dzongkha 2521 efi Efik 2522 egy Egyptian (Ancient) 2523 eka Ekajuk 2524 ell Greek, Modern (1453-) 2525 elx Elamite 2526 eng English 2527 enm English, Middle (ca. 1100-1500) 2528 epo Esperanto 2529 esk Eskimo (Other) 2530 esl Spanish 2531 est Estonian 2532 eus Basque 2533 ewe Ewe 2534 ewo Ewondo 2535 fan Fang 2536 fao Faroese 2537 fas Persian 2538 fat Fanti 2539 fij Fijian 2540 fin Finnish 2541 fiu Finno-Ugrian (Other) 2542 fon Fon 2543 fra French 2544 fre French 2545 frm French, Middle (ca. 1400-1600) 2546 fro French, Old (842- ca. 1400) 2547 fry Frisian 2548 ful Fulah 2549 gaa Ga 2550 gae Gaelic (Scots) 2551 gai Irish 2552 gay Gayo 2553 gdh Gaelic (Scots) 2554 gem Germanic (Other) 2555 geo Georgian 2556 ger German 2557 gez Geez 2558 gil Gilbertese 2559 glg Gallegan 2560 gmh German, Middle High (ca. 1050-1500) 2561 goh German, Old High (ca. 750-1050) 2562 gon Gondi 2563 got Gothic 2564 grb Grebo 2565 grc Greek, Ancient (to 1453) 2566 gre Greek, Modern (1453-) 2567 grn Guarani 2568 guj Gujarati 2569 hai Haida 2570 hau Hausa 2571 haw Hawaiian 2572 heb Hebrew 2573 her Herero 2574 hil Hiligaynon 2575 him Himachali 2576 hin Hindi 2577 hmo Hiri Motu 2578 hun Hungarian 2579 hup Hupa 2580 hye Armenian 2581 iba Iban 2582 ibo Igbo 2583 ice Icelandic 2584 ijo Ijo 2585 iku Inuktitut 2586 ilo Iloko 2587 ina Interlingua (International Auxiliary language Association) 2588 inc Indic (Other) 2589 ind Indonesian 2590 ine Indo-European (Other) 2591 ine Interlingue 2592 ipk Inupiak 2593 ira Iranian (Other) 2594 iri Irish 2595 iro Iroquoian uages 2596 isl Icelandic 2597 ita Italian 2598 jav Javanese 2599 jaw Javanese 2600 jpn Japanese 2601 jpr Judeo-Persian 2602 jrb Judeo-Arabic 2603 kaa Kara-Kalpak 2604 kab Kabyle 2605 kac Kachin 2606 kal Greenlandic 2607 kam Kamba 2608 kan Kannada 2609 kar Karen 2610 kas Kashmiri 2611 kat Georgian 2612 kau Kanuri 2613 kaw Kawi 2614 kaz Kazakh 2615 kha Khasi 2616 khi Khoisan (Other) 2617 khm Khmer 2618 kho Khotanese 2619 kik Kikuyu 2620 kin Kinyarwanda 2621 kir Kirghiz 2622 kok Konkani 2623 kom Komi 2624 kon Kongo 2625 kor Korean 2626 kpe Kpelle 2627 kro Kru 2628 kru Kurukh 2629 kua Kuanyama 2630 kum Kumyk 2631 kur Kurdish 2632 kus Kusaie 2633 kut Kutenai 2634 lad Ladino 2635 lah Lahnda 2636 lam Lamba 2637 lao Lao 2638 lat Latin 2639 lav Latvian 2640 lez Lezghian 2641 lin Lingala 2642 lit Lithuanian 2643 lol Mongo 2644 loz Lozi 2645 ltz Letzeburgesch 2646 lub Luba-Katanga 2647 lug Ganda 2648 lui Luiseno 2649 lun Lunda 2650 luo Luo (Kenya and Tanzania) 2651 mac Macedonian 2652 mad Madurese 2653 mag Magahi 2654 mah Marshall 2655 mai Maithili 2656 mak Macedonian 2657 mak Makasar 2658 mal Malayalam 2659 man Mandingo 2660 mao Maori 2661 map Austronesian (Other) 2662 mar Marathi 2663 mas Masai 2664 max Manx 2665 may Malay 2666 men Mende 2667 mga Irish, Middle (900 - 1200) 2668 mic Micmac 2669 min Minangkabau 2670 mis Miscellaneous (Other) 2671 mkh Mon-Kmer (Other) 2672 mlg Malagasy 2673 mlt Maltese 2674 mni Manipuri 2675 mno Manobo Languages 2676 moh Mohawk 2677 mol Moldavian 2678 mon Mongolian 2679 mos Mossi 2680 mri Maori 2681 msa Malay 2682 mul Multiple Languages 2683 mun Munda Languages 2684 mus Creek 2685 mwr Marwari 2686 mya Burmese 2687 myn Mayan Languages 2688 nah Aztec 2689 nai North American Indian (Other) 2690 nau Nauru 2691 nav Navajo 2692 nbl Ndebele, South 2693 nde Ndebele, North 2694 ndo Ndongo 2695 nep Nepali 2696 new Newari 2697 nic Niger-Kordofanian (Other) 2698 niu Niuean 2699 nla Dutch 2700 nno Norwegian (Nynorsk) 2701 non Norse, Old 2702 nor Norwegian 2703 nso Sotho, Northern 2704 nub Nubian Languages 2705 nya Nyanja 2706 nym Nyamwezi 2707 nyn Nyankole 2708 nyo Nyoro 2709 nzi Nzima 2710 oci Langue d'Oc (post 1500) 2711 oji Ojibwa 2712 ori Oriya 2713 orm Oromo 2714 osa Osage 2715 oss Ossetic 2716 ota Turkish, Ottoman (1500 - 1928) 2717 oto Otomian Languages 2718 paa Papuan-Australian (Other) 2719 pag Pangasinan 2720 pal Pahlavi 2721 pam Pampanga 2722 pan Panjabi 2723 pap Papiamento 2724 pau Palauan 2725 peo Persian, Old (ca 600 - 400 B.C.) 2726 per Persian 2727 phn Phoenician 2728 pli Pali 2729 pol Polish 2730 pon Ponape 2731 por Portuguese 2732 pra Prakrit uages 2733 pro Provencal, Old (to 1500) 2734 pus Pushto 2735 que Quechua 2736 raj Rajasthani 2737 rar Rarotongan 2738 roa Romance (Other) 2739 roh Rhaeto-Romance 2740 rom Romany 2741 ron Romanian 2742 rum Romanian 2743 run Rundi 2744 rus Russian 2745 sad Sandawe 2746 sag Sango 2747 sah Yakut 2748 sai South American Indian (Other) 2749 sal Salishan Languages 2750 sam Samaritan Aramaic 2751 san Sanskrit 2752 sco Scots 2753 scr Serbo-Croatian 2754 sel Selkup 2755 sem Semitic (Other) 2756 sga Irish, Old (to 900) 2757 shn Shan 2758 sid Sidamo 2759 sin Singhalese 2760 sio Siouan Languages 2761 sit Sino-Tibetan (Other) 2762 sla Slavic (Other) 2763 slk Slovak 2764 slo Slovak 2765 slv Slovenian 2766 smi Sami Languages 2767 smo Samoan 2768 sna Shona 2769 snd Sindhi 2770 sog Sogdian 2771 som Somali 2772 son Songhai 2773 sot Sotho, Southern 2774 spa Spanish 2775 sqi Albanian 2776 srd Sardinian 2777 srr Serer 2778 ssa Nilo-Saharan (Other) 2779 ssw Siswant 2780 ssw Swazi 2781 suk Sukuma 2782 sun Sudanese 2783 sus Susu 2784 sux Sumerian 2785 sve Swedish 2786 swa Swahili 2787 swe Swedish 2788 syr Syriac 2789 tah Tahitian 2790 tam Tamil 2791 tat Tatar 2792 tel Telugu 2793 tem Timne 2794 ter Tereno 2795 tgk Tajik 2796 tgl Tagalog 2797 tha Thai 2798 tib Tibetan 2799 tig Tigre 2800 tir Tigrinya 2801 tiv Tivi 2802 tli Tlingit 2803 tmh Tamashek 2804 tog Tonga (Nyasa) 2805 ton Tonga (Tonga Islands) 2806 tru Truk 2807 tsi Tsimshian 2808 tsn Tswana 2809 tso Tsonga 2810 tuk Turkmen 2811 tum Tumbuka 2812 tur Turkish 2813 tut Altaic (Other) 2814 twi Twi 2815 tyv Tuvinian 2816 uga Ugaritic 2817 uig Uighur 2818 ukr Ukrainian 2819 umb Umbundu 2820 und Undetermined 2821 urd Urdu 2822 uzb Uzbek 2823 vai Vai 2824 ven Venda 2825 vie Vietnamese 2826 vol Volapük 2827 vot Votic 2828 wak Wakashan Languages 2829 wal Walamo 2830 war Waray 2831 was Washo 2832 wel Welsh 2833 wen Sorbian Languages 2834 wol Wolof 2835 xho Xhosa 2836 yao Yao 2837 yap Yap 2838 yid Yiddish 2839 yor Yoruba 2840 zap Zapotec 2841 zen Zenaga 2842 zha Zhuang 2843 zho Chinese 2844 zul Zulu 2845 zun Zuni 2846 2847 */ 2848 2849 return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); 2850 } 2851 2852 2853 public static function ETCOEventLookup($index) { 2854 if (($index >= 0x17) && ($index <= 0xDF)) { 2855 return 'reserved for future use'; 2856 } 2857 if (($index >= 0xE0) && ($index <= 0xEF)) { 2858 return 'not predefined synch 0-F'; 2859 } 2860 if (($index >= 0xF0) && ($index <= 0xFC)) { 2861 return 'reserved for future use'; 2862 } 2863 2864 static $EventLookup = array( 2865 0x00 => 'padding (has no meaning)', 2866 0x01 => 'end of initial silence', 2867 0x02 => 'intro start', 2868 0x03 => 'main part start', 2869 0x04 => 'outro start', 2870 0x05 => 'outro end', 2871 0x06 => 'verse start', 2872 0x07 => 'refrain start', 2873 0x08 => 'interlude start', 2874 0x09 => 'theme start', 2875 0x0A => 'variation start', 2876 0x0B => 'key change', 2877 0x0C => 'time change', 2878 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', 2879 0x0E => 'sustained noise', 2880 0x0F => 'sustained noise end', 2881 0x10 => 'intro end', 2882 0x11 => 'main part end', 2883 0x12 => 'verse end', 2884 0x13 => 'refrain end', 2885 0x14 => 'theme end', 2886 0x15 => 'profanity', 2887 0x16 => 'profanity end', 2888 0xFD => 'audio end (start of silence)', 2889 0xFE => 'audio file ends', 2890 0xFF => 'one more byte of events follows' 2891 ); 2892 2893 return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); 2894 } 2895 2896 public static function SYTLContentTypeLookup($index) { 2897 static $SYTLContentTypeLookup = array( 2898 0x00 => 'other', 2899 0x01 => 'lyrics', 2900 0x02 => 'text transcription', 2901 0x03 => 'movement/part name', // (e.g. 'Adagio') 2902 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') 2903 0x05 => 'chord', // (e.g. 'Bb F Fsus') 2904 0x06 => 'trivia/\'pop up\' information', 2905 0x07 => 'URLs to webpages', 2906 0x08 => 'URLs to images' 2907 ); 2908 2909 return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); 2910 } 2911 2912 public static function APICPictureTypeLookup($index, $returnarray=false) { 2913 static $APICPictureTypeLookup = array( 2914 0x00 => 'Other', 2915 0x01 => '32x32 pixels \'file icon\' (PNG only)', 2916 0x02 => 'Other file icon', 2917 0x03 => 'Cover (front)', 2918 0x04 => 'Cover (back)', 2919 0x05 => 'Leaflet page', 2920 0x06 => 'Media (e.g. label side of CD)', 2921 0x07 => 'Lead artist/lead performer/soloist', 2922 0x08 => 'Artist/performer', 2923 0x09 => 'Conductor', 2924 0x0A => 'Band/Orchestra', 2925 0x0B => 'Composer', 2926 0x0C => 'Lyricist/text writer', 2927 0x0D => 'Recording Location', 2928 0x0E => 'During recording', 2929 0x0F => 'During performance', 2930 0x10 => 'Movie/video screen capture', 2931 0x11 => 'A bright coloured fish', 2932 0x12 => 'Illustration', 2933 0x13 => 'Band/artist logotype', 2934 0x14 => 'Publisher/Studio logotype' 2935 ); 2936 if ($returnarray) { 2937 return $APICPictureTypeLookup; 2938 } 2939 return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); 2940 } 2941 2942 public static function COMRReceivedAsLookup($index) { 2943 static $COMRReceivedAsLookup = array( 2944 0x00 => 'Other', 2945 0x01 => 'Standard CD album with other songs', 2946 0x02 => 'Compressed audio on CD', 2947 0x03 => 'File over the Internet', 2948 0x04 => 'Stream over the Internet', 2949 0x05 => 'As note sheets', 2950 0x06 => 'As note sheets in a book with other sheets', 2951 0x07 => 'Music on other media', 2952 0x08 => 'Non-musical merchandise' 2953 ); 2954 2955 return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); 2956 } 2957 2958 public static function RVA2ChannelTypeLookup($index) { 2959 static $RVA2ChannelTypeLookup = array( 2960 0x00 => 'Other', 2961 0x01 => 'Master volume', 2962 0x02 => 'Front right', 2963 0x03 => 'Front left', 2964 0x04 => 'Back right', 2965 0x05 => 'Back left', 2966 0x06 => 'Front centre', 2967 0x07 => 'Back centre', 2968 0x08 => 'Subwoofer' 2969 ); 2970 2971 return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); 2972 } 2973 2974 public static function FrameNameLongLookup($framename) { 2975 2976 $begin = __LINE__; 2977 2978 /** This is not a comment! 2979 2980 AENC Audio encryption 2981 APIC Attached picture 2982 ASPI Audio seek point index 2983 BUF Recommended buffer size 2984 CNT Play counter 2985 COM Comments 2986 COMM Comments 2987 COMR Commercial frame 2988 CRA Audio encryption 2989 CRM Encrypted meta frame 2990 ENCR Encryption method registration 2991 EQU Equalisation 2992 EQU2 Equalisation (2) 2993 EQUA Equalisation 2994 ETC Event timing codes 2995 ETCO Event timing codes 2996 GEO General encapsulated object 2997 GEOB General encapsulated object 2998 GRID Group identification registration 2999 IPL Involved people list 3000 IPLS Involved people list 3001 LINK Linked information 3002 LNK Linked information 3003 MCDI Music CD identifier 3004 MCI Music CD Identifier 3005 MLL MPEG location lookup table 3006 MLLT MPEG location lookup table 3007 OWNE Ownership frame 3008 PCNT Play counter 3009 PIC Attached picture 3010 POP Popularimeter 3011 POPM Popularimeter 3012 POSS Position synchronisation frame 3013 PRIV Private frame 3014 RBUF Recommended buffer size 3015 REV Reverb 3016 RVA Relative volume adjustment 3017 RVA2 Relative volume adjustment (2) 3018 RVAD Relative volume adjustment 3019 RVRB Reverb 3020 SEEK Seek frame 3021 SIGN Signature frame 3022 SLT Synchronised lyric/text 3023 STC Synced tempo codes 3024 SYLT Synchronised lyric/text 3025 SYTC Synchronised tempo codes 3026 TAL Album/Movie/Show title 3027 TALB Album/Movie/Show title 3028 TBP BPM (Beats Per Minute) 3029 TBPM BPM (beats per minute) 3030 TCM Composer 3031 TCMP Part of a compilation 3032 TCO Content type 3033 TCOM Composer 3034 TCON Content type 3035 TCOP Copyright message 3036 TCP Part of a compilation 3037 TCR Copyright message 3038 TDA Date 3039 TDAT Date 3040 TDEN Encoding time 3041 TDLY Playlist delay 3042 TDOR Original release time 3043 TDRC Recording time 3044 TDRL Release time 3045 TDTG Tagging time 3046 TDY Playlist delay 3047 TEN Encoded by 3048 TENC Encoded by 3049 TEXT Lyricist/Text writer 3050 TFLT File type 3051 TFT File type 3052 TIM Time 3053 TIME Time 3054 TIPL Involved people list 3055 TIT1 Content group description 3056 TIT2 Title/songname/content description 3057 TIT3 Subtitle/Description refinement 3058 TKE Initial key 3059 TKEY Initial key 3060 TLA Language(s) 3061 TLAN Language(s) 3062 TLE Length 3063 TLEN Length 3064 TMCL Musician credits list 3065 TMED Media type 3066 TMOO Mood 3067 TMT Media type 3068 TOA Original artist(s)/performer(s) 3069 TOAL Original album/movie/show title 3070 TOF Original filename 3071 TOFN Original filename 3072 TOL Original Lyricist(s)/text writer(s) 3073 TOLY Original lyricist(s)/text writer(s) 3074 TOPE Original artist(s)/performer(s) 3075 TOR Original release year 3076 TORY Original release year 3077 TOT Original album/Movie/Show title 3078 TOWN File owner/licensee 3079 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group 3080 TP2 Band/Orchestra/Accompaniment 3081 TP3 Conductor/Performer refinement 3082 TP4 Interpreted, remixed, or otherwise modified by 3083 TPA Part of a set 3084 TPB Publisher 3085 TPE1 Lead performer(s)/Soloist(s) 3086 TPE2 Band/orchestra/accompaniment 3087 TPE3 Conductor/performer refinement 3088 TPE4 Interpreted, remixed, or otherwise modified by 3089 TPOS Part of a set 3090 TPRO Produced notice 3091 TPUB Publisher 3092 TRC ISRC (International Standard Recording Code) 3093 TRCK Track number/Position in set 3094 TRD Recording dates 3095 TRDA Recording dates 3096 TRK Track number/Position in set 3097 TRSN Internet radio station name 3098 TRSO Internet radio station owner 3099 TS2 Album-Artist sort order 3100 TSA Album sort order 3101 TSC Composer sort order 3102 TSI Size 3103 TSIZ Size 3104 TSO2 Album-Artist sort order 3105 TSOA Album sort order 3106 TSOC Composer sort order 3107 TSOP Performer sort order 3108 TSOT Title sort order 3109 TSP Performer sort order 3110 TSRC ISRC (international standard recording code) 3111 TSS Software/hardware and settings used for encoding 3112 TSSE Software/Hardware and settings used for encoding 3113 TSST Set subtitle 3114 TST Title sort order 3115 TT1 Content group description 3116 TT2 Title/Songname/Content description 3117 TT3 Subtitle/Description refinement 3118 TXT Lyricist/text writer 3119 TXX User defined text information frame 3120 TXXX User defined text information frame 3121 TYE Year 3122 TYER Year 3123 UFI Unique file identifier 3124 UFID Unique file identifier 3125 ULT Unsychronised lyric/text transcription 3126 USER Terms of use 3127 USLT Unsynchronised lyric/text transcription 3128 WAF Official audio file webpage 3129 WAR Official artist/performer webpage 3130 WAS Official audio source webpage 3131 WCM Commercial information 3132 WCOM Commercial information 3133 WCOP Copyright/Legal information 3134 WCP Copyright/Legal information 3135 WOAF Official audio file webpage 3136 WOAR Official artist/performer webpage 3137 WOAS Official audio source webpage 3138 WORS Official Internet radio station homepage 3139 WPAY Payment 3140 WPB Publishers official webpage 3141 WPUB Publishers official webpage 3142 WXX User defined URL link frame 3143 WXXX User defined URL link frame 3144 TFEA Featured Artist 3145 TSTU Recording Studio 3146 rgad Replay Gain Adjustment 3147 3148 */ 3149 3150 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); 3151 3152 // Last three: 3153 // from Helium2 [www.helium2.com] 3154 // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 3155 } 3156 3157 3158 public static function FrameNameShortLookup($framename) { 3159 3160 $begin = __LINE__; 3161 3162 /** This is not a comment! 3163 3164 AENC audio_encryption 3165 APIC attached_picture 3166 ASPI audio_seek_point_index 3167 BUF recommended_buffer_size 3168 CNT play_counter 3169 COM comment 3170 COMM comment 3171 COMR commercial_frame 3172 CRA audio_encryption 3173 CRM encrypted_meta_frame 3174 ENCR encryption_method_registration 3175 EQU equalisation 3176 EQU2 equalisation 3177 EQUA equalisation 3178 ETC event_timing_codes 3179 ETCO event_timing_codes 3180 GEO general_encapsulated_object 3181 GEOB general_encapsulated_object 3182 GRID group_identification_registration 3183 IPL involved_people_list 3184 IPLS involved_people_list 3185 LINK linked_information 3186 LNK linked_information 3187 MCDI music_cd_identifier 3188 MCI music_cd_identifier 3189 MLL mpeg_location_lookup_table 3190 MLLT mpeg_location_lookup_table 3191 OWNE ownership_frame 3192 PCNT play_counter 3193 PIC attached_picture 3194 POP popularimeter 3195 POPM popularimeter 3196 POSS position_synchronisation_frame 3197 PRIV private_frame 3198 RBUF recommended_buffer_size 3199 REV reverb 3200 RVA relative_volume_adjustment 3201 RVA2 relative_volume_adjustment 3202 RVAD relative_volume_adjustment 3203 RVRB reverb 3204 SEEK seek_frame 3205 SIGN signature_frame 3206 SLT synchronised_lyric 3207 STC synced_tempo_codes 3208 SYLT synchronised_lyric 3209 SYTC synchronised_tempo_codes 3210 TAL album 3211 TALB album 3212 TBP bpm 3213 TBPM bpm 3214 TCM composer 3215 TCMP part_of_a_compilation 3216 TCO genre 3217 TCOM composer 3218 TCON genre 3219 TCOP copyright_message 3220 TCP part_of_a_compilation 3221 TCR copyright_message 3222 TDA date 3223 TDAT date 3224 TDEN encoding_time 3225 TDLY playlist_delay 3226 TDOR original_release_time 3227 TDRC recording_time 3228 TDRL release_time 3229 TDTG tagging_time 3230 TDY playlist_delay 3231 TEN encoded_by 3232 TENC encoded_by 3233 TEXT lyricist 3234 TFLT file_type 3235 TFT file_type 3236 TIM time 3237 TIME time 3238 TIPL involved_people_list 3239 TIT1 content_group_description 3240 TIT2 title 3241 TIT3 subtitle 3242 TKE initial_key 3243 TKEY initial_key 3244 TLA language 3245 TLAN language 3246 TLE length 3247 TLEN length 3248 TMCL musician_credits_list 3249 TMED media_type 3250 TMOO mood 3251 TMT media_type 3252 TOA original_artist 3253 TOAL original_album 3254 TOF original_filename 3255 TOFN original_filename 3256 TOL original_lyricist 3257 TOLY original_lyricist 3258 TOPE original_artist 3259 TOR original_year 3260 TORY original_year 3261 TOT original_album 3262 TOWN file_owner 3263 TP1 artist 3264 TP2 band 3265 TP3 conductor 3266 TP4 remixer 3267 TPA part_of_a_set 3268 TPB publisher 3269 TPE1 artist 3270 TPE2 band 3271 TPE3 conductor 3272 TPE4 remixer 3273 TPOS part_of_a_set 3274 TPRO produced_notice 3275 TPUB publisher 3276 TRC isrc 3277 TRCK track_number 3278 TRD recording_dates 3279 TRDA recording_dates 3280 TRK track_number 3281 TRSN internet_radio_station_name 3282 TRSO internet_radio_station_owner 3283 TS2 album_artist_sort_order 3284 TSA album_sort_order 3285 TSC composer_sort_order 3286 TSI size 3287 TSIZ size 3288 TSO2 album_artist_sort_order 3289 TSOA album_sort_order 3290 TSOC composer_sort_order 3291 TSOP performer_sort_order 3292 TSOT title_sort_order 3293 TSP performer_sort_order 3294 TSRC isrc 3295 TSS encoder_settings 3296 TSSE encoder_settings 3297 TSST set_subtitle 3298 TST title_sort_order 3299 TT1 content_group_description 3300 TT2 title 3301 TT3 subtitle 3302 TXT lyricist 3303 TXX text 3304 TXXX text 3305 TYE year 3306 TYER year 3307 UFI unique_file_identifier 3308 UFID unique_file_identifier 3309 ULT unsychronised_lyric 3310 USER terms_of_use 3311 USLT unsynchronised_lyric 3312 WAF url_file 3313 WAR url_artist 3314 WAS url_source 3315 WCM commercial_information 3316 WCOM commercial_information 3317 WCOP copyright 3318 WCP copyright 3319 WOAF url_file 3320 WOAR url_artist 3321 WOAS url_source 3322 WORS url_station 3323 WPAY url_payment 3324 WPB url_publisher 3325 WPUB url_publisher 3326 WXX url_user 3327 WXXX url_user 3328 TFEA featured_artist 3329 TSTU recording_studio 3330 rgad replay_gain_adjustment 3331 3332 */ 3333 3334 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); 3335 } 3336 3337 public static function TextEncodingTerminatorLookup($encoding) { 3338 // http://www.id3.org/id3v2.4.0-structure.txt 3339 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3340 static $TextEncodingTerminatorLookup = array( 3341 0 => "\x00", // $00 ISO-8859-1. Terminated with $00. 3342 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. 3343 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3344 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. 3345 255 => "\x00\x00" 3346 ); 3347 return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : ''); 3348 } 3349 3350 public static function TextEncodingNameLookup($encoding) { 3351 // http://www.id3.org/id3v2.4.0-structure.txt 3352 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3353 static $TextEncodingNameLookup = array( 3354 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00. 3355 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. 3356 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3357 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00. 3358 255 => 'UTF-16BE' 3359 ); 3360 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); 3361 } 3362 3363 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { 3364 switch ($id3v2majorversion) { 3365 case 2: 3366 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); 3367 break; 3368 3369 case 3: 3370 case 4: 3371 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); 3372 break; 3373 } 3374 return false; 3375 } 3376 3377 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { 3378 for ($i = 0; $i < strlen($numberstring); $i++) { 3379 if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { 3380 if (($numberstring{$i} == '.') && $allowdecimal) { 3381 // allowed 3382 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { 3383 // allowed 3384 } else { 3385 return false; 3386 } 3387 } 3388 } 3389 return true; 3390 } 3391 3392 public static function IsValidDateStampString($datestamp) { 3393 if (strlen($datestamp) != 8) { 3394 return false; 3395 } 3396 if (!self::IsANumber($datestamp, false)) { 3397 return false; 3398 } 3399 $year = substr($datestamp, 0, 4); 3400 $month = substr($datestamp, 4, 2); 3401 $day = substr($datestamp, 6, 2); 3402 if (($year == 0) || ($month == 0) || ($day == 0)) { 3403 return false; 3404 } 3405 if ($month > 12) { 3406 return false; 3407 } 3408 if ($day > 31) { 3409 return false; 3410 } 3411 if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { 3412 return false; 3413 } 3414 if (($day > 29) && ($month == 2)) { 3415 return false; 3416 } 3417 return true; 3418 } 3419 3420 public static function ID3v2HeaderLength($majorversion) { 3421 return (($majorversion == 2) ? 6 : 10); 3422 } 3423 3424 } 3425