[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 <?php 2 ///////////////////////////////////////////////////////////////// 3 /// getID3() by James Heinrich <info@getid3.org> // 4 // available at http://getid3.sourceforge.net // 5 // or http://www.getid3.org // 6 ///////////////////////////////////////////////////////////////// 7 // See readme.txt for more details // 8 ///////////////////////////////////////////////////////////////// 9 /// // 10 // module.tag.id3v2.php // 11 // module for analyzing ID3v2 tags // 12 // dependencies: module.tag.id3v1.php // 13 // /// 14 ///////////////////////////////////////////////////////////////// 15 16 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); 17 18 class getid3_id3v2 extends getid3_handler 19 { 20 public $StartingOffset = 0; 21 22 public function Analyze() { 23 $info = &$this->getid3->info; 24 25 // Overall tag structure: 26 // +-----------------------------+ 27 // | Header (10 bytes) | 28 // +-----------------------------+ 29 // | Extended Header | 30 // | (variable length, OPTIONAL) | 31 // +-----------------------------+ 32 // | Frames (variable length) | 33 // +-----------------------------+ 34 // | Padding | 35 // | (variable length, OPTIONAL) | 36 // +-----------------------------+ 37 // | Footer (10 bytes, OPTIONAL) | 38 // +-----------------------------+ 39 40 // Header 41 // ID3v2/file identifier "ID3" 42 // ID3v2 version $04 00 43 // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) 44 // ID3v2 size 4 * %0xxxxxxx 45 46 47 // shortcuts 48 $info['id3v2']['header'] = true; 49 $thisfile_id3v2 = &$info['id3v2']; 50 $thisfile_id3v2['flags'] = array(); 51 $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; 52 53 54 fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET); 55 $header = fread($this->getid3->fp, 10); 56 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 57 58 $thisfile_id3v2['majorversion'] = ord($header{3}); 59 $thisfile_id3v2['minorversion'] = ord($header{4}); 60 61 // shortcut 62 $id3v2_majorversion = &$thisfile_id3v2['majorversion']; 63 64 } else { 65 66 unset($info['id3v2']); 67 return false; 68 69 } 70 71 if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) 72 73 $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; 74 return false; 75 76 } 77 78 $id3_flags = ord($header{5}); 79 switch ($id3v2_majorversion) { 80 case 2: 81 // %ab000000 in v2.2 82 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 83 $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression 84 break; 85 86 case 3: 87 // %abc00000 in v2.3 88 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 89 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 90 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 91 break; 92 93 case 4: 94 // %abcd0000 in v2.4 95 $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation 96 $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header 97 $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator 98 $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present 99 break; 100 } 101 102 $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 103 104 $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset; 105 $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; 106 107 108 109 // create 'encoding' key - used by getid3::HandleAllTags() 110 // in ID3v2 every field can have it's own encoding type 111 // so force everything to UTF-8 so it can be handled consistantly 112 $thisfile_id3v2['encoding'] = 'UTF-8'; 113 114 115 // Frames 116 117 // All ID3v2 frames consists of one frame header followed by one or more 118 // fields containing the actual information. The header is always 10 119 // bytes and laid out as follows: 120 // 121 // Frame ID $xx xx xx xx (four characters) 122 // Size 4 * %0xxxxxxx 123 // Flags $xx xx 124 125 $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header 126 if (!empty($thisfile_id3v2['exthead']['length'])) { 127 $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4); 128 } 129 if (!empty($thisfile_id3v2_flags['isfooter'])) { 130 $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio 131 } 132 if ($sizeofframes > 0) { 133 134 $framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable 135 136 // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) 137 if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { 138 $framedata = $this->DeUnsynchronise($framedata); 139 } 140 // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead 141 // of on tag level, making it easier to skip frames, increasing the streamability 142 // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that 143 // there exists an unsynchronised frame, while the new unsynchronisation flag in 144 // the frame header [S:4.1.2] indicates unsynchronisation. 145 146 147 //$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) 148 $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header 149 150 151 // Extended Header 152 if (!empty($thisfile_id3v2_flags['exthead'])) { 153 $extended_header_offset = 0; 154 155 if ($id3v2_majorversion == 3) { 156 157 // v2.3 definition: 158 //Extended header size $xx xx xx xx // 32-bit integer 159 //Extended Flags $xx xx 160 // %x0000000 %00000000 // v2.3 161 // x - CRC data present 162 //Size of padding $xx xx xx xx 163 164 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0); 165 $extended_header_offset += 4; 166 167 $thisfile_id3v2['exthead']['flag_bytes'] = 2; 168 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 169 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 170 171 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000); 172 173 $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 174 $extended_header_offset += 4; 175 176 if ($thisfile_id3v2['exthead']['flags']['crc']) { 177 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); 178 $extended_header_offset += 4; 179 } 180 $extended_header_offset += $thisfile_id3v2['exthead']['padding_size']; 181 182 } elseif ($id3v2_majorversion == 4) { 183 184 // v2.4 definition: 185 //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer 186 //Number of flag bytes $01 187 //Extended Flags $xx 188 // %0bcd0000 // v2.4 189 // b - Tag is an update 190 // Flag data length $00 191 // c - CRC data present 192 // Flag data length $05 193 // Total frame CRC 5 * %0xxxxxxx 194 // d - Tag restrictions 195 // Flag data length $01 196 197 $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true); 198 $extended_header_offset += 4; 199 200 $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1 201 $extended_header_offset += 1; 202 203 $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); 204 $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; 205 206 $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40); 207 $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20); 208 $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10); 209 210 if ($thisfile_id3v2['exthead']['flags']['update']) { 211 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0 212 $extended_header_offset += 1; 213 } 214 215 if ($thisfile_id3v2['exthead']['flags']['crc']) { 216 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5 217 $extended_header_offset += 1; 218 $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false); 219 $extended_header_offset += $ext_header_chunk_length; 220 } 221 222 if ($thisfile_id3v2['exthead']['flags']['restrictions']) { 223 $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1 224 $extended_header_offset += 1; 225 226 // %ppqrrstt 227 $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); 228 $extended_header_offset += 1; 229 $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions 230 $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions 231 $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions 232 $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions 233 $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions 234 235 $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']); 236 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']); 237 $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']); 238 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']); 239 $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']); 240 } 241 242 if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) { 243 $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')'; 244 } 245 } 246 247 $framedataoffset += $extended_header_offset; 248 $framedata = substr($framedata, $extended_header_offset); 249 } // end extended header 250 251 252 while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse 253 if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { 254 // insufficient room left in ID3v2 header for actual data - must be padding 255 $thisfile_id3v2['padding']['start'] = $framedataoffset; 256 $thisfile_id3v2['padding']['length'] = strlen($framedata); 257 $thisfile_id3v2['padding']['valid'] = true; 258 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { 259 if ($framedata{$i} != "\x00") { 260 $thisfile_id3v2['padding']['valid'] = false; 261 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 262 $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; 263 break; 264 } 265 } 266 break; // skip rest of ID3v2 header 267 } 268 if ($id3v2_majorversion == 2) { 269 // Frame ID $xx xx xx (three characters) 270 // Size $xx xx xx (24-bit integer) 271 // Flags $xx xx 272 273 $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header 274 $framedata = substr($framedata, 6); // and leave the rest in $framedata 275 $frame_name = substr($frame_header, 0, 3); 276 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); 277 $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs 278 279 } elseif ($id3v2_majorversion > 2) { 280 281 // Frame ID $xx xx xx xx (four characters) 282 // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) 283 // Flags $xx xx 284 285 $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header 286 $framedata = substr($framedata, 10); // and leave the rest in $framedata 287 288 $frame_name = substr($frame_header, 0, 4); 289 if ($id3v2_majorversion == 3) { 290 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 291 } else { // ID3v2.4+ 292 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) 293 } 294 295 if ($frame_size < (strlen($framedata) + 4)) { 296 $nextFrameID = substr($framedata, $frame_size, 4); 297 if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { 298 // next frame is OK 299 } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { 300 // MP3ext known broken frames - "ok" for the purposes of this test 301 } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { 302 $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'; 303 $id3v2_majorversion = 3; 304 $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer 305 } 306 } 307 308 309 $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); 310 } 311 312 if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { 313 // padding encountered 314 315 $thisfile_id3v2['padding']['start'] = $framedataoffset; 316 $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata); 317 $thisfile_id3v2['padding']['valid'] = true; 318 319 $len = strlen($framedata); 320 for ($i = 0; $i < $len; $i++) { 321 if ($framedata{$i} != "\x00") { 322 $thisfile_id3v2['padding']['valid'] = false; 323 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; 324 $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; 325 break; 326 } 327 } 328 break; // skip rest of ID3v2 header 329 } 330 331 if ($frame_name == 'COM ') { 332 $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)]'; 333 $frame_name = 'COMM'; 334 } 335 if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { 336 337 unset($parsedFrame); 338 $parsedFrame['frame_name'] = $frame_name; 339 $parsedFrame['frame_flags_raw'] = $frame_flags; 340 $parsedFrame['data'] = substr($framedata, 0, $frame_size); 341 $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); 342 $parsedFrame['dataoffset'] = $framedataoffset; 343 344 $this->ParseID3v2Frame($parsedFrame); 345 $thisfile_id3v2[$frame_name][] = $parsedFrame; 346 347 $framedata = substr($framedata, $frame_size); 348 349 } else { // invalid frame length or FrameID 350 351 if ($frame_size <= strlen($framedata)) { 352 353 if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { 354 355 // next frame is valid, just skip the current frame 356 $framedata = substr($framedata, $frame_size); 357 $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; 358 359 } else { 360 361 // next frame is invalid too, abort processing 362 //unset($framedata); 363 $framedata = null; 364 $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; 365 366 } 367 368 } elseif ($frame_size == strlen($framedata)) { 369 370 // this is the last frame, just skip 371 $info['warning'][] = 'This was the last ID3v2 frame.'; 372 373 } else { 374 375 // next frame is invalid too, abort processing 376 //unset($framedata); 377 $framedata = null; 378 $info['warning'][] = 'Invalid ID3v2 frame size, aborting.'; 379 380 } 381 if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { 382 383 switch ($frame_name) { 384 case "\x00\x00".'MP': 385 case "\x00".'MP3': 386 case ' MP3': 387 case 'MP3e': 388 case "\x00".'MP': 389 case ' MP': 390 case 'MP3': 391 $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/)"]'; 392 break; 393 394 default: 395 $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; 396 break; 397 } 398 399 } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) { 400 401 $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').')).'; 402 403 } else { 404 405 $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; 406 407 } 408 409 } 410 $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); 411 412 } 413 414 } 415 416 417 // Footer 418 419 // The footer is a copy of the header, but with a different identifier. 420 // ID3v2 identifier "3DI" 421 // ID3v2 version $04 00 422 // ID3v2 flags %abcd0000 423 // ID3v2 size 4 * %0xxxxxxx 424 425 if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { 426 $footer = fread($this->getid3->fp, 10); 427 if (substr($footer, 0, 3) == '3DI') { 428 $thisfile_id3v2['footer'] = true; 429 $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); 430 $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); 431 } 432 if ($thisfile_id3v2['majorversion_footer'] <= 4) { 433 $id3_flags = ord(substr($footer{5})); 434 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); 435 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); 436 $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); 437 $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); 438 439 $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); 440 } 441 } // end footer 442 443 if (isset($thisfile_id3v2['comments']['genre'])) { 444 foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { 445 unset($thisfile_id3v2['comments']['genre'][$key]); 446 $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value))); 447 } 448 } 449 450 if (isset($thisfile_id3v2['comments']['track'])) { 451 foreach ($thisfile_id3v2['comments']['track'] as $key => $value) { 452 if (strstr($value, '/')) { 453 list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]); 454 } 455 } 456 } 457 458 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)) { 459 $thisfile_id3v2['comments']['year'] = array($matches[1]); 460 } 461 462 463 if (!empty($thisfile_id3v2['TXXX'])) { 464 // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames 465 foreach ($thisfile_id3v2['TXXX'] as $txxx_array) { 466 switch ($txxx_array['description']) { 467 case 'replaygain_track_gain': 468 if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) { 469 $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 470 } 471 break; 472 case 'replaygain_track_peak': 473 if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) { 474 $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']); 475 } 476 break; 477 case 'replaygain_album_gain': 478 if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) { 479 $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); 480 } 481 break; 482 } 483 } 484 } 485 486 487 // Set avdataoffset 488 $info['avdataoffset'] = $thisfile_id3v2['headerlength']; 489 if (isset($thisfile_id3v2['footer'])) { 490 $info['avdataoffset'] += 10; 491 } 492 493 return true; 494 } 495 496 497 public function ParseID3v2GenreString($genrestring) { 498 // Parse genres into arrays of genreName and genreID 499 // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' 500 // ID3v2.4.x: '21' $00 'Eurodisco' $00 501 $clean_genres = array(); 502 if (strpos($genrestring, "\x00") === false) { 503 $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring); 504 } 505 $genre_elements = explode("\x00", $genrestring); 506 foreach ($genre_elements as $element) { 507 $element = trim($element); 508 if ($element) { 509 if (preg_match('#^[0-9]{1,3}#', $element)) { 510 $clean_genres[] = getid3_id3v1::LookupGenreName($element); 511 } else { 512 $clean_genres[] = str_replace('((', '(', $element); 513 } 514 } 515 } 516 return $clean_genres; 517 } 518 519 520 public function ParseID3v2Frame(&$parsedFrame) { 521 522 // shortcuts 523 $info = &$this->getid3->info; 524 $id3v2_majorversion = $info['id3v2']['majorversion']; 525 526 $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); 527 if (empty($parsedFrame['framenamelong'])) { 528 unset($parsedFrame['framenamelong']); 529 } 530 $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); 531 if (empty($parsedFrame['framenameshort'])) { 532 unset($parsedFrame['framenameshort']); 533 } 534 535 if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard 536 if ($id3v2_majorversion == 3) { 537 // Frame Header Flags 538 // %abc00000 %ijk00000 539 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation 540 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation 541 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only 542 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression 543 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption 544 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity 545 546 } elseif ($id3v2_majorversion == 4) { 547 // Frame Header Flags 548 // %0abc0000 %0h00kmnp 549 $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation 550 $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation 551 $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only 552 $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity 553 $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression 554 $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption 555 $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation 556 $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator 557 558 // Frame-level de-unsynchronisation - ID3v2.4 559 if ($parsedFrame['flags']['Unsynchronisation']) { 560 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); 561 } 562 563 if ($parsedFrame['flags']['DataLengthIndicator']) { 564 $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); 565 $parsedFrame['data'] = substr($parsedFrame['data'], 4); 566 } 567 } 568 569 // Frame-level de-compression 570 if ($parsedFrame['flags']['compression']) { 571 $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); 572 if (!function_exists('gzuncompress')) { 573 $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; 574 } else { 575 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { 576 //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { 577 $parsedFrame['data'] = $decompresseddata; 578 unset($decompresseddata); 579 } else { 580 $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; 581 } 582 } 583 } 584 } 585 586 if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { 587 if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { 588 $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'; 589 } 590 } 591 592 if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { 593 594 $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; 595 switch ($parsedFrame['frame_name']) { 596 case 'WCOM': 597 $warning .= ' (this is known to happen with files tagged by RioPort)'; 598 break; 599 600 default: 601 break; 602 } 603 $info['warning'][] = $warning; 604 605 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier 606 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier 607 // There may be more than one 'UFID' frame in a tag, 608 // but only one with the same 'Owner identifier'. 609 // <Header for 'Unique file identifier', ID: 'UFID'> 610 // Owner identifier <text string> $00 611 // Identifier <up to 64 bytes binary data> 612 $exploded = explode("\x00", $parsedFrame['data'], 2); 613 $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : ''); 614 $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : ''); 615 616 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame 617 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame 618 // There may be more than one 'TXXX' frame in each tag, 619 // but only one with the same description. 620 // <Header for 'User defined text information frame', ID: 'TXXX'> 621 // Text encoding $xx 622 // Description <text string according to encoding> $00 (00) 623 // Value <text string according to encoding> 624 625 $frame_offset = 0; 626 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 627 628 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 629 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 630 } 631 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 632 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 633 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 634 } 635 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 636 if (ord($frame_description) === 0) { 637 $frame_description = ''; 638 } 639 $parsedFrame['encodingid'] = $frame_textencoding; 640 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 641 642 $parsedFrame['description'] = $frame_description; 643 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 644 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 645 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); 646 } 647 //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain 648 649 650 } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame 651 // There may only be one text information frame of its kind in an tag. 652 // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', 653 // excluding 'TXXX' described in 4.2.6.> 654 // Text encoding $xx 655 // Information <text string(s) according to encoding> 656 657 $frame_offset = 0; 658 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 659 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 660 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 661 } 662 663 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 664 665 $parsedFrame['encodingid'] = $frame_textencoding; 666 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 667 668 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 669 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / 670 // This of course breaks when an artist name contains slash character, e.g. "AC/DC" 671 // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense 672 // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user 673 switch ($parsedFrame['encoding']) { 674 case 'UTF-16': 675 case 'UTF-16BE': 676 case 'UTF-16LE': 677 $wordsize = 2; 678 break; 679 case 'ISO-8859-1': 680 case 'UTF-8': 681 default: 682 $wordsize = 1; 683 break; 684 } 685 $Txxx_elements = array(); 686 $Txxx_elements_start_offset = 0; 687 for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) { 688 if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) { 689 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 690 $Txxx_elements_start_offset = $i + $wordsize; 691 } 692 } 693 $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); 694 foreach ($Txxx_elements as $Txxx_element) { 695 $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element); 696 if (!empty($string)) { 697 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; 698 } 699 } 700 unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset); 701 } 702 703 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame 704 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame 705 // There may be more than one 'WXXX' frame in each tag, 706 // but only one with the same description 707 // <Header for 'User defined URL link frame', ID: 'WXXX'> 708 // Text encoding $xx 709 // Description <text string according to encoding> $00 (00) 710 // URL <text string> 711 712 $frame_offset = 0; 713 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 714 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 715 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 716 } 717 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 718 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 719 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 720 } 721 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 722 723 if (ord($frame_description) === 0) { 724 $frame_description = ''; 725 } 726 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 727 728 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 729 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 730 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 731 } 732 if ($frame_terminatorpos) { 733 // there are null bytes after the data - this is not according to spec 734 // only use data up to first null byte 735 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); 736 } else { 737 // no null bytes following data, just use all data 738 $frame_urldata = (string) $parsedFrame['data']; 739 } 740 741 $parsedFrame['encodingid'] = $frame_textencoding; 742 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 743 744 $parsedFrame['url'] = $frame_urldata; 745 $parsedFrame['description'] = $frame_description; 746 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 747 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); 748 } 749 unset($parsedFrame['data']); 750 751 752 } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames 753 // There may only be one URL link frame of its kind in a tag, 754 // except when stated otherwise in the frame description 755 // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX' 756 // described in 4.3.2.> 757 // URL <text string> 758 759 $parsedFrame['url'] = trim($parsedFrame['data']); 760 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 761 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; 762 } 763 unset($parsedFrame['data']); 764 765 766 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) 767 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) 768 // http://id3.org/id3v2.3.0#sec4.4 769 // There may only be one 'IPL' frame in each tag 770 // <Header for 'User defined URL link frame', ID: 'IPL'> 771 // Text encoding $xx 772 // People list strings <textstrings> 773 774 $frame_offset = 0; 775 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 776 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 777 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 778 } 779 $parsedFrame['encodingid'] = $frame_textencoding; 780 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); 781 $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); 782 783 // http://www.getid3.org/phpBB3/viewtopic.php?t=1369 784 // "this tag typically contains null terminated strings, which are associated in pairs" 785 // "there are users that use the tag incorrectly" 786 $IPLS_parts = array(); 787 if (strpos($parsedFrame['data_raw'], "\x00") !== false) { 788 $IPLS_parts_unsorted = array(); 789 if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) { 790 // 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 791 $thisILPS = ''; 792 for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) { 793 $twobytes = substr($parsedFrame['data_raw'], $i, 2); 794 if ($twobytes === "\x00\x00") { 795 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 796 $thisILPS = ''; 797 } else { 798 $thisILPS .= $twobytes; 799 } 800 } 801 if (strlen($thisILPS) > 2) { // 2-byte BOM 802 $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); 803 } 804 } else { 805 // ISO-8859-1 or UTF-8 or other single-byte-null character set 806 $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']); 807 } 808 if (count($IPLS_parts_unsorted) == 1) { 809 // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson" 810 foreach ($IPLS_parts_unsorted as $key => $value) { 811 $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value); 812 $position = ''; 813 foreach ($IPLS_parts_sorted as $person) { 814 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 815 } 816 } 817 } elseif ((count($IPLS_parts_unsorted) % 2) == 0) { 818 $position = ''; 819 $person = ''; 820 foreach ($IPLS_parts_unsorted as $key => $value) { 821 if (($key % 2) == 0) { 822 $position = $value; 823 } else { 824 $person = $value; 825 $IPLS_parts[] = array('position'=>$position, 'person'=>$person); 826 $position = ''; 827 $person = ''; 828 } 829 } 830 } else { 831 foreach ($IPLS_parts_unsorted as $key => $value) { 832 $IPLS_parts[] = array($value); 833 } 834 } 835 836 } else { 837 $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']); 838 } 839 $parsedFrame['data'] = $IPLS_parts; 840 841 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 842 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 843 } 844 845 846 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier 847 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier 848 // There may only be one 'MCDI' frame in each tag 849 // <Header for 'Music CD identifier', ID: 'MCDI'> 850 // CD TOC <binary data> 851 852 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 853 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; 854 } 855 856 857 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes 858 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes 859 // There may only be one 'ETCO' frame in each tag 860 // <Header for 'Event timing codes', ID: 'ETCO'> 861 // Time stamp format $xx 862 // Where time stamp format is: 863 // $01 (32-bit value) MPEG frames from beginning of file 864 // $02 (32-bit value) milliseconds from beginning of file 865 // Followed by a list of key events in the following format: 866 // Type of event $xx 867 // Time stamp $xx (xx ...) 868 // The 'Time stamp' is set to zero if directly at the beginning of the sound 869 // or after the previous event. All events MUST be sorted in chronological order. 870 871 $frame_offset = 0; 872 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 873 874 while ($frame_offset < strlen($parsedFrame['data'])) { 875 $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); 876 $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); 877 $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 878 $frame_offset += 4; 879 } 880 unset($parsedFrame['data']); 881 882 883 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table 884 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table 885 // There may only be one 'MLLT' frame in each tag 886 // <Header for 'Location lookup table', ID: 'MLLT'> 887 // MPEG frames between reference $xx xx 888 // Bytes between reference $xx xx xx 889 // Milliseconds between reference $xx xx xx 890 // Bits for bytes deviation $xx 891 // Bits for milliseconds dev. $xx 892 // Then for every reference the following data is included; 893 // Deviation in bytes %xxx.... 894 // Deviation in milliseconds %xxx.... 895 896 $frame_offset = 0; 897 $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); 898 $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); 899 $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); 900 $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); 901 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); 902 $parsedFrame['data'] = substr($parsedFrame['data'], 10); 903 while ($frame_offset < strlen($parsedFrame['data'])) { 904 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 905 } 906 $reference_counter = 0; 907 while (strlen($deviationbitstream) > 0) { 908 $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); 909 $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); 910 $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); 911 $reference_counter++; 912 } 913 unset($parsedFrame['data']); 914 915 916 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes 917 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes 918 // There may only be one 'SYTC' frame in each tag 919 // <Header for 'Synchronised tempo codes', ID: 'SYTC'> 920 // Time stamp format $xx 921 // Tempo data <binary data> 922 // Where time stamp format is: 923 // $01 (32-bit value) MPEG frames from beginning of file 924 // $02 (32-bit value) milliseconds from beginning of file 925 926 $frame_offset = 0; 927 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 928 $timestamp_counter = 0; 929 while ($frame_offset < strlen($parsedFrame['data'])) { 930 $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 931 if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { 932 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); 933 } 934 $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 935 $frame_offset += 4; 936 $timestamp_counter++; 937 } 938 unset($parsedFrame['data']); 939 940 941 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription 942 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription 943 // There may be more than one 'Unsynchronised lyrics/text transcription' frame 944 // in each tag, but only one with the same language and content descriptor. 945 // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'> 946 // Text encoding $xx 947 // Language $xx xx xx 948 // Content descriptor <text string according to encoding> $00 (00) 949 // Lyrics/text <full text string according to encoding> 950 951 $frame_offset = 0; 952 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 953 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 954 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 955 } 956 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 957 $frame_offset += 3; 958 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 959 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 960 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 961 } 962 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 963 if (ord($frame_description) === 0) { 964 $frame_description = ''; 965 } 966 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 967 968 $parsedFrame['encodingid'] = $frame_textencoding; 969 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 970 971 $parsedFrame['data'] = $parsedFrame['data']; 972 $parsedFrame['language'] = $frame_language; 973 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 974 $parsedFrame['description'] = $frame_description; 975 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 976 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 977 } 978 unset($parsedFrame['data']); 979 980 981 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text 982 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text 983 // There may be more than one 'SYLT' frame in each tag, 984 // but only one with the same language and content descriptor. 985 // <Header for 'Synchronised lyrics/text', ID: 'SYLT'> 986 // Text encoding $xx 987 // Language $xx xx xx 988 // Time stamp format $xx 989 // $01 (32-bit value) MPEG frames from beginning of file 990 // $02 (32-bit value) milliseconds from beginning of file 991 // Content type $xx 992 // Content descriptor <text string according to encoding> $00 (00) 993 // Terminated text to be synced (typically a syllable) 994 // Sync identifier (terminator to above string) $00 (00) 995 // Time stamp $xx (xx ...) 996 997 $frame_offset = 0; 998 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 999 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1000 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1001 } 1002 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1003 $frame_offset += 3; 1004 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1005 $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1006 $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); 1007 $parsedFrame['encodingid'] = $frame_textencoding; 1008 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1009 1010 $parsedFrame['language'] = $frame_language; 1011 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1012 1013 $timestampindex = 0; 1014 $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); 1015 while (strlen($frame_remainingdata)) { 1016 $frame_offset = 0; 1017 $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); 1018 if ($frame_terminatorpos === false) { 1019 $frame_remainingdata = ''; 1020 } else { 1021 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1022 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1023 } 1024 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); 1025 1026 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1027 if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { 1028 // timestamp probably omitted for first data item 1029 } else { 1030 $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); 1031 $frame_remainingdata = substr($frame_remainingdata, 4); 1032 } 1033 $timestampindex++; 1034 } 1035 } 1036 unset($parsedFrame['data']); 1037 1038 1039 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments 1040 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments 1041 // There may be more than one comment frame in each tag, 1042 // but only one with the same language and content descriptor. 1043 // <Header for 'Comment', ID: 'COMM'> 1044 // Text encoding $xx 1045 // Language $xx xx xx 1046 // Short content descrip. <text string according to encoding> $00 (00) 1047 // The actual text <full text string according to encoding> 1048 1049 if (strlen($parsedFrame['data']) < 5) { 1050 1051 $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; 1052 1053 } else { 1054 1055 $frame_offset = 0; 1056 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1057 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1058 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1059 } 1060 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1061 $frame_offset += 3; 1062 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1063 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1064 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1065 } 1066 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1067 if (ord($frame_description) === 0) { 1068 $frame_description = ''; 1069 } 1070 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1071 1072 $parsedFrame['encodingid'] = $frame_textencoding; 1073 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1074 1075 $parsedFrame['language'] = $frame_language; 1076 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1077 $parsedFrame['description'] = $frame_description; 1078 $parsedFrame['data'] = $frame_text; 1079 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1080 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1081 } 1082 1083 } 1084 1085 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) 1086 // There may be more than one 'RVA2' frame in each tag, 1087 // but only one with the same identification string 1088 // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'> 1089 // Identification <text string> $00 1090 // The 'identification' string is used to identify the situation and/or 1091 // device where this adjustment should apply. The following is then 1092 // repeated for every channel: 1093 // Type of channel $xx 1094 // Volume adjustment $xx xx 1095 // Bits representing peak $xx 1096 // Peak volume $xx (xx ...) 1097 1098 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); 1099 $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); 1100 if (ord($frame_idstring) === 0) { 1101 $frame_idstring = ''; 1102 } 1103 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1104 $parsedFrame['description'] = $frame_idstring; 1105 $RVA2channelcounter = 0; 1106 while (strlen($frame_remainingdata) >= 5) { 1107 $frame_offset = 0; 1108 $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1109 $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid; 1110 $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); 1111 $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed 1112 $frame_offset += 2; 1113 $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); 1114 if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) { 1115 $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'; 1116 break; 1117 } 1118 $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); 1119 $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); 1120 $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); 1121 $RVA2channelcounter++; 1122 } 1123 unset($parsedFrame['data']); 1124 1125 1126 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) 1127 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) 1128 // There may only be one 'RVA' frame in each tag 1129 // <Header for 'Relative volume adjustment', ID: 'RVA'> 1130 // ID3v2.2 => Increment/decrement %000000ba 1131 // ID3v2.3 => Increment/decrement %00fedcba 1132 // Bits used for volume descr. $xx 1133 // Relative volume change, right $xx xx (xx ...) // a 1134 // Relative volume change, left $xx xx (xx ...) // b 1135 // Peak volume right $xx xx (xx ...) 1136 // Peak volume left $xx xx (xx ...) 1137 // ID3v2.3 only, optional (not present in ID3v2.2): 1138 // Relative volume change, right back $xx xx (xx ...) // c 1139 // Relative volume change, left back $xx xx (xx ...) // d 1140 // Peak volume right back $xx xx (xx ...) 1141 // Peak volume left back $xx xx (xx ...) 1142 // ID3v2.3 only, optional (not present in ID3v2.2): 1143 // Relative volume change, center $xx xx (xx ...) // e 1144 // Peak volume center $xx xx (xx ...) 1145 // ID3v2.3 only, optional (not present in ID3v2.2): 1146 // Relative volume change, bass $xx xx (xx ...) // f 1147 // Peak volume bass $xx xx (xx ...) 1148 1149 $frame_offset = 0; 1150 $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1151 $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); 1152 $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); 1153 $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1154 $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); 1155 $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1156 if ($parsedFrame['incdec']['right'] === false) { 1157 $parsedFrame['volumechange']['right'] *= -1; 1158 } 1159 $frame_offset += $frame_bytesvolume; 1160 $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1161 if ($parsedFrame['incdec']['left'] === false) { 1162 $parsedFrame['volumechange']['left'] *= -1; 1163 } 1164 $frame_offset += $frame_bytesvolume; 1165 $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1166 $frame_offset += $frame_bytesvolume; 1167 $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1168 $frame_offset += $frame_bytesvolume; 1169 if ($id3v2_majorversion == 3) { 1170 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1171 if (strlen($parsedFrame['data']) > 0) { 1172 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); 1173 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); 1174 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1175 if ($parsedFrame['incdec']['rightrear'] === false) { 1176 $parsedFrame['volumechange']['rightrear'] *= -1; 1177 } 1178 $frame_offset += $frame_bytesvolume; 1179 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1180 if ($parsedFrame['incdec']['leftrear'] === false) { 1181 $parsedFrame['volumechange']['leftrear'] *= -1; 1182 } 1183 $frame_offset += $frame_bytesvolume; 1184 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1185 $frame_offset += $frame_bytesvolume; 1186 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1187 $frame_offset += $frame_bytesvolume; 1188 } 1189 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); 1190 if (strlen($parsedFrame['data']) > 0) { 1191 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); 1192 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1193 if ($parsedFrame['incdec']['center'] === false) { 1194 $parsedFrame['volumechange']['center'] *= -1; 1195 } 1196 $frame_offset += $frame_bytesvolume; 1197 $parsedFrame['peakvolume']['center'] = 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']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); 1203 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1204 if ($parsedFrame['incdec']['bass'] === false) { 1205 $parsedFrame['volumechange']['bass'] *= -1; 1206 } 1207 $frame_offset += $frame_bytesvolume; 1208 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); 1209 $frame_offset += $frame_bytesvolume; 1210 } 1211 } 1212 unset($parsedFrame['data']); 1213 1214 1215 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) 1216 // There may be more than one 'EQU2' frame in each tag, 1217 // but only one with the same identification string 1218 // <Header of 'Equalisation (2)', ID: 'EQU2'> 1219 // Interpolation method $xx 1220 // $00 Band 1221 // $01 Linear 1222 // Identification <text string> $00 1223 // The following is then repeated for every adjustment point 1224 // Frequency $xx xx 1225 // Volume adjustment $xx xx 1226 1227 $frame_offset = 0; 1228 $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1229 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1230 $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1231 if (ord($frame_idstring) === 0) { 1232 $frame_idstring = ''; 1233 } 1234 $parsedFrame['description'] = $frame_idstring; 1235 $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); 1236 while (strlen($frame_remainingdata)) { 1237 $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; 1238 $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); 1239 $frame_remainingdata = substr($frame_remainingdata, 4); 1240 } 1241 $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; 1242 unset($parsedFrame['data']); 1243 1244 1245 } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) 1246 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) 1247 // There may only be one 'EQUA' frame in each tag 1248 // <Header for 'Relative volume adjustment', ID: 'EQU'> 1249 // Adjustment bits $xx 1250 // This is followed by 2 bytes + ('adjustment bits' rounded up to the 1251 // nearest byte) for every equalisation band in the following format, 1252 // giving a frequency range of 0 - 32767Hz: 1253 // Increment/decrement %x (MSB of the Frequency) 1254 // Frequency (lower 15 bits) 1255 // Adjustment $xx (xx ...) 1256 1257 $frame_offset = 0; 1258 $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); 1259 $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); 1260 1261 $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); 1262 while (strlen($frame_remainingdata) > 0) { 1263 $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); 1264 $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); 1265 $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); 1266 $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; 1267 $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); 1268 if ($parsedFrame[$frame_frequency]['incdec'] === false) { 1269 $parsedFrame[$frame_frequency]['adjustment'] *= -1; 1270 } 1271 $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); 1272 } 1273 unset($parsedFrame['data']); 1274 1275 1276 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb 1277 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb 1278 // There may only be one 'RVRB' frame in each tag. 1279 // <Header for 'Reverb', ID: 'RVRB'> 1280 // Reverb left (ms) $xx xx 1281 // Reverb right (ms) $xx xx 1282 // Reverb bounces, left $xx 1283 // Reverb bounces, right $xx 1284 // Reverb feedback, left to left $xx 1285 // Reverb feedback, left to right $xx 1286 // Reverb feedback, right to right $xx 1287 // Reverb feedback, right to left $xx 1288 // Premix left to right $xx 1289 // Premix right to left $xx 1290 1291 $frame_offset = 0; 1292 $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1293 $frame_offset += 2; 1294 $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1295 $frame_offset += 2; 1296 $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1297 $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1298 $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1299 $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1300 $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1301 $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1302 $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1303 $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1304 unset($parsedFrame['data']); 1305 1306 1307 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture 1308 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture 1309 // There may be several pictures attached to one file, 1310 // each in their individual 'APIC' frame, but only one 1311 // with the same content descriptor 1312 // <Header for 'Attached picture', ID: 'APIC'> 1313 // Text encoding $xx 1314 // ID3v2.3+ => MIME type <text string> $00 1315 // ID3v2.2 => Image format $xx xx xx 1316 // Picture type $xx 1317 // Description <text string according to encoding> $00 (00) 1318 // Picture data <binary data> 1319 1320 $frame_offset = 0; 1321 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1322 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1323 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1324 } 1325 1326 if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { 1327 $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); 1328 if (strtolower($frame_imagetype) == 'ima') { 1329 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted 1330 // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) 1331 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1332 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1333 if (ord($frame_mimetype) === 0) { 1334 $frame_mimetype = ''; 1335 } 1336 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); 1337 if ($frame_imagetype == 'JPEG') { 1338 $frame_imagetype = 'JPG'; 1339 } 1340 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1341 } else { 1342 $frame_offset += 3; 1343 } 1344 } 1345 if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { 1346 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1347 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1348 if (ord($frame_mimetype) === 0) { 1349 $frame_mimetype = ''; 1350 } 1351 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1352 } 1353 1354 $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1355 1356 if ($frame_offset >= $parsedFrame['datalength']) { 1357 $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); 1358 } else { 1359 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1360 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1361 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1362 } 1363 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1364 if (ord($frame_description) === 0) { 1365 $frame_description = ''; 1366 } 1367 $parsedFrame['encodingid'] = $frame_textencoding; 1368 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1369 1370 if ($id3v2_majorversion == 2) { 1371 $parsedFrame['imagetype'] = $frame_imagetype; 1372 } else { 1373 $parsedFrame['mime'] = $frame_mimetype; 1374 } 1375 $parsedFrame['picturetypeid'] = $frame_picturetype; 1376 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); 1377 $parsedFrame['description'] = $frame_description; 1378 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); 1379 $parsedFrame['datalength'] = strlen($parsedFrame['data']); 1380 1381 $parsedFrame['image_mime'] = ''; 1382 $imageinfo = array(); 1383 $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); 1384 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { 1385 $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); 1386 if ($imagechunkcheck[0]) { 1387 $parsedFrame['image_width'] = $imagechunkcheck[0]; 1388 } 1389 if ($imagechunkcheck[1]) { 1390 $parsedFrame['image_height'] = $imagechunkcheck[1]; 1391 } 1392 } 1393 1394 do { 1395 if ($this->getid3->option_save_attachments === false) { 1396 // skip entirely 1397 unset($parsedFrame['data']); 1398 break; 1399 } 1400 if ($this->getid3->option_save_attachments === true) { 1401 // great 1402 /* 1403 } elseif (is_int($this->getid3->option_save_attachments)) { 1404 if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) { 1405 // too big, skip 1406 $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; 1407 unset($parsedFrame['data']); 1408 break; 1409 } 1410 */ 1411 } elseif (is_string($this->getid3->option_save_attachments)) { 1412 $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); 1413 if (!is_dir($dir) || !is_writable($dir)) { 1414 // cannot write, skip 1415 $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)'; 1416 unset($parsedFrame['data']); 1417 break; 1418 } 1419 } 1420 // if we get this far, must be OK 1421 if (is_string($this->getid3->option_save_attachments)) { 1422 $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; 1423 if (!file_exists($destination_filename) || is_writable($destination_filename)) { 1424 file_put_contents($destination_filename, $parsedFrame['data']); 1425 } else { 1426 $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'; 1427 } 1428 $parsedFrame['data_filename'] = $destination_filename; 1429 unset($parsedFrame['data']); 1430 } else { 1431 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1432 if (!isset($info['id3v2']['comments']['picture'])) { 1433 $info['id3v2']['comments']['picture'] = array(); 1434 } 1435 $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); 1436 } 1437 } 1438 } while (false); 1439 } 1440 1441 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object 1442 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object 1443 // There may be more than one 'GEOB' frame in each tag, 1444 // but only one with the same content descriptor 1445 // <Header for 'General encapsulated object', ID: 'GEOB'> 1446 // Text encoding $xx 1447 // MIME type <text string> $00 1448 // Filename <text string according to encoding> $00 (00) 1449 // Content description <text string according to encoding> $00 (00) 1450 // Encapsulated object <binary data> 1451 1452 $frame_offset = 0; 1453 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1454 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1455 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1456 } 1457 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1458 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1459 if (ord($frame_mimetype) === 0) { 1460 $frame_mimetype = ''; 1461 } 1462 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1463 1464 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1465 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1466 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1467 } 1468 $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1469 if (ord($frame_filename) === 0) { 1470 $frame_filename = ''; 1471 } 1472 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1473 1474 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1475 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1476 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1477 } 1478 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1479 if (ord($frame_description) === 0) { 1480 $frame_description = ''; 1481 } 1482 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1483 1484 $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); 1485 $parsedFrame['encodingid'] = $frame_textencoding; 1486 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1487 1488 $parsedFrame['mime'] = $frame_mimetype; 1489 $parsedFrame['filename'] = $frame_filename; 1490 $parsedFrame['description'] = $frame_description; 1491 unset($parsedFrame['data']); 1492 1493 1494 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter 1495 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter 1496 // There may only be one 'PCNT' frame in each tag. 1497 // When the counter reaches all one's, one byte is inserted in 1498 // front of the counter thus making the counter eight bits bigger 1499 // <Header for 'Play counter', ID: 'PCNT'> 1500 // Counter $xx xx xx xx (xx ...) 1501 1502 $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); 1503 1504 1505 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter 1506 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter 1507 // There may be more than one 'POPM' frame in each tag, 1508 // but only one with the same email address 1509 // <Header for 'Popularimeter', ID: 'POPM'> 1510 // Email to user <text string> $00 1511 // Rating $xx 1512 // Counter $xx xx xx xx (xx ...) 1513 1514 $frame_offset = 0; 1515 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1516 $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1517 if (ord($frame_emailaddress) === 0) { 1518 $frame_emailaddress = ''; 1519 } 1520 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1521 $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1522 $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1523 $parsedFrame['email'] = $frame_emailaddress; 1524 $parsedFrame['rating'] = $frame_rating; 1525 unset($parsedFrame['data']); 1526 1527 1528 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size 1529 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size 1530 // There may only be one 'RBUF' frame in each tag 1531 // <Header for 'Recommended buffer size', ID: 'RBUF'> 1532 // Buffer size $xx xx xx 1533 // Embedded info flag %0000000x 1534 // Offset to next tag $xx xx xx xx 1535 1536 $frame_offset = 0; 1537 $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); 1538 $frame_offset += 3; 1539 1540 $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); 1541 $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); 1542 $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1543 unset($parsedFrame['data']); 1544 1545 1546 } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) 1547 // There may be more than one 'CRM' frame in a tag, 1548 // but only one with the same 'owner identifier' 1549 // <Header for 'Encrypted meta frame', ID: 'CRM'> 1550 // Owner identifier <textstring> $00 (00) 1551 // Content/explanation <textstring> $00 (00) 1552 // Encrypted datablock <binary data> 1553 1554 $frame_offset = 0; 1555 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1556 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1557 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1558 1559 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1560 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1561 if (ord($frame_description) === 0) { 1562 $frame_description = ''; 1563 } 1564 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1565 1566 $parsedFrame['ownerid'] = $frame_ownerid; 1567 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1568 $parsedFrame['description'] = $frame_description; 1569 unset($parsedFrame['data']); 1570 1571 1572 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption 1573 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption 1574 // There may be more than one 'AENC' frames in a tag, 1575 // but only one with the same 'Owner identifier' 1576 // <Header for 'Audio encryption', ID: 'AENC'> 1577 // Owner identifier <text string> $00 1578 // Preview start $xx xx 1579 // Preview length $xx xx 1580 // Encryption info <binary data> 1581 1582 $frame_offset = 0; 1583 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1584 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1585 if (ord($frame_ownerid) === 0) { 1586 $frame_ownerid == ''; 1587 } 1588 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1589 $parsedFrame['ownerid'] = $frame_ownerid; 1590 $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1591 $frame_offset += 2; 1592 $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1593 $frame_offset += 2; 1594 $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); 1595 unset($parsedFrame['data']); 1596 1597 1598 } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information 1599 (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information 1600 // There may be more than one 'LINK' frame in a tag, 1601 // but only one with the same contents 1602 // <Header for 'Linked information', ID: 'LINK'> 1603 // ID3v2.3+ => Frame identifier $xx xx xx xx 1604 // ID3v2.2 => Frame identifier $xx xx xx 1605 // URL <text string> $00 1606 // ID and additional data <text string(s)> 1607 1608 $frame_offset = 0; 1609 if ($id3v2_majorversion == 2) { 1610 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); 1611 $frame_offset += 3; 1612 } else { 1613 $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); 1614 $frame_offset += 4; 1615 } 1616 1617 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1618 $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1619 if (ord($frame_url) === 0) { 1620 $frame_url = ''; 1621 } 1622 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1623 $parsedFrame['url'] = $frame_url; 1624 1625 $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); 1626 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 1627 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); 1628 } 1629 unset($parsedFrame['data']); 1630 1631 1632 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) 1633 // There may only be one 'POSS' frame in each tag 1634 // <Head for 'Position synchronisation', ID: 'POSS'> 1635 // Time stamp format $xx 1636 // Position $xx (xx ...) 1637 1638 $frame_offset = 0; 1639 $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1640 $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); 1641 unset($parsedFrame['data']); 1642 1643 1644 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) 1645 // There may be more than one 'Terms of use' frame in a tag, 1646 // but only one with the same 'Language' 1647 // <Header for 'Terms of use frame', ID: 'USER'> 1648 // Text encoding $xx 1649 // Language $xx xx xx 1650 // The actual text <text string according to encoding> 1651 1652 $frame_offset = 0; 1653 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1654 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1655 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1656 } 1657 $frame_language = substr($parsedFrame['data'], $frame_offset, 3); 1658 $frame_offset += 3; 1659 $parsedFrame['language'] = $frame_language; 1660 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1661 $parsedFrame['encodingid'] = $frame_textencoding; 1662 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1663 1664 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1665 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1666 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); 1667 } 1668 unset($parsedFrame['data']); 1669 1670 1671 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) 1672 // There may only be one 'OWNE' frame in a tag 1673 // <Header for 'Ownership frame', ID: 'OWNE'> 1674 // Text encoding $xx 1675 // Price paid <text string> $00 1676 // Date of purch. <text string> 1677 // Seller <text string according to encoding> 1678 1679 $frame_offset = 0; 1680 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1681 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1682 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1683 } 1684 $parsedFrame['encodingid'] = $frame_textencoding; 1685 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1686 1687 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1688 $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1689 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1690 1691 $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); 1692 $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); 1693 $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); 1694 1695 $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); 1696 if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { 1697 $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); 1698 } 1699 $frame_offset += 8; 1700 1701 $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); 1702 unset($parsedFrame['data']); 1703 1704 1705 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) 1706 // There may be more than one 'commercial frame' in a tag, 1707 // but no two may be identical 1708 // <Header for 'Commercial frame', ID: 'COMR'> 1709 // Text encoding $xx 1710 // Price string <text string> $00 1711 // Valid until <text string> 1712 // Contact URL <text string> $00 1713 // Received as $xx 1714 // Name of seller <text string according to encoding> $00 (00) 1715 // Description <text string according to encoding> $00 (00) 1716 // Picture MIME type <string> $00 1717 // Seller logo <binary data> 1718 1719 $frame_offset = 0; 1720 $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1721 if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { 1722 $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; 1723 } 1724 1725 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1726 $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1727 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1728 $frame_rawpricearray = explode('/', $frame_pricestring); 1729 foreach ($frame_rawpricearray as $key => $val) { 1730 $frame_currencyid = substr($val, 0, 3); 1731 $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); 1732 $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); 1733 } 1734 1735 $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); 1736 $frame_offset += 8; 1737 1738 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1739 $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1740 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1741 1742 $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1743 1744 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1745 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1746 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1747 } 1748 $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1749 if (ord($frame_sellername) === 0) { 1750 $frame_sellername = ''; 1751 } 1752 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1753 1754 $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); 1755 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { 1756 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1757 } 1758 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1759 if (ord($frame_description) === 0) { 1760 $frame_description = ''; 1761 } 1762 $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); 1763 1764 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1765 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1766 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1767 1768 $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); 1769 1770 $parsedFrame['encodingid'] = $frame_textencoding; 1771 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1772 1773 $parsedFrame['pricevaliduntil'] = $frame_datestring; 1774 $parsedFrame['contacturl'] = $frame_contacturl; 1775 $parsedFrame['receivedasid'] = $frame_receivedasid; 1776 $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); 1777 $parsedFrame['sellername'] = $frame_sellername; 1778 $parsedFrame['description'] = $frame_description; 1779 $parsedFrame['mime'] = $frame_mimetype; 1780 $parsedFrame['logo'] = $frame_sellerlogo; 1781 unset($parsedFrame['data']); 1782 1783 1784 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) 1785 // There may be several 'ENCR' frames in a tag, 1786 // but only one containing the same symbol 1787 // and only one containing the same owner identifier 1788 // <Header for 'Encryption method registration', ID: 'ENCR'> 1789 // Owner identifier <text string> $00 1790 // Method symbol $xx 1791 // Encryption data <binary data> 1792 1793 $frame_offset = 0; 1794 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1795 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1796 if (ord($frame_ownerid) === 0) { 1797 $frame_ownerid = ''; 1798 } 1799 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1800 1801 $parsedFrame['ownerid'] = $frame_ownerid; 1802 $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1803 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1804 1805 1806 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) 1807 1808 // There may be several 'GRID' frames in a tag, 1809 // but only one containing the same symbol 1810 // and only one containing the same owner identifier 1811 // <Header for 'Group ID registration', ID: 'GRID'> 1812 // Owner identifier <text string> $00 1813 // Group symbol $xx 1814 // Group dependent data <binary data> 1815 1816 $frame_offset = 0; 1817 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1818 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1819 if (ord($frame_ownerid) === 0) { 1820 $frame_ownerid = ''; 1821 } 1822 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1823 1824 $parsedFrame['ownerid'] = $frame_ownerid; 1825 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1826 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1827 1828 1829 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) 1830 // The tag may contain more than one 'PRIV' frame 1831 // but only with different contents 1832 // <Header for 'Private frame', ID: 'PRIV'> 1833 // Owner identifier <text string> $00 1834 // The private data <binary data> 1835 1836 $frame_offset = 0; 1837 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1838 $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1839 if (ord($frame_ownerid) === 0) { 1840 $frame_ownerid = ''; 1841 } 1842 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1843 1844 $parsedFrame['ownerid'] = $frame_ownerid; 1845 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1846 1847 1848 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) 1849 // There may be more than one 'signature frame' in a tag, 1850 // but no two may be identical 1851 // <Header for 'Signature frame', ID: 'SIGN'> 1852 // Group symbol $xx 1853 // Signature <binary data> 1854 1855 $frame_offset = 0; 1856 $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1857 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1858 1859 1860 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) 1861 // There may only be one 'seek frame' in a tag 1862 // <Header for 'Seek frame', ID: 'SEEK'> 1863 // Minimum offset to next tag $xx xx xx xx 1864 1865 $frame_offset = 0; 1866 $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1867 1868 1869 } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) 1870 // There may only be one 'audio seek point index' frame in a tag 1871 // <Header for 'Seek Point Index', ID: 'ASPI'> 1872 // Indexed data start (S) $xx xx xx xx 1873 // Indexed data length (L) $xx xx xx xx 1874 // Number of index points (N) $xx xx 1875 // Bits per index point (b) $xx 1876 // Then for every index point the following data is included: 1877 // Fraction at index (Fi) $xx (xx) 1878 1879 $frame_offset = 0; 1880 $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1881 $frame_offset += 4; 1882 $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 1883 $frame_offset += 4; 1884 $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 1885 $frame_offset += 2; 1886 $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); 1887 $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); 1888 for ($i = 0; $i < $frame_indexpoints; $i++) { 1889 $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); 1890 $frame_offset += $frame_bytesperpoint; 1891 } 1892 unset($parsedFrame['data']); 1893 1894 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment 1895 // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 1896 // There may only be one 'RGAD' frame in a tag 1897 // <Header for 'Replay Gain Adjustment', ID: 'RGAD'> 1898 // Peak Amplitude $xx $xx $xx $xx 1899 // Radio Replay Gain Adjustment %aaabbbcd %dddddddd 1900 // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd 1901 // a - name code 1902 // b - originator code 1903 // c - sign bit 1904 // d - replay gain adjustment 1905 1906 $frame_offset = 0; 1907 $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); 1908 $frame_offset += 4; 1909 $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); 1910 $frame_offset += 2; 1911 $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); 1912 $frame_offset += 2; 1913 $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); 1914 $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); 1915 $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); 1916 $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); 1917 $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); 1918 $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); 1919 $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); 1920 $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); 1921 $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); 1922 $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); 1923 $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); 1924 $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); 1925 $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); 1926 $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); 1927 1928 $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; 1929 $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; 1930 $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; 1931 $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; 1932 $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; 1933 1934 unset($parsedFrame['data']); 1935 1936 } 1937 1938 return true; 1939 } 1940 1941 1942 public function DeUnsynchronise($data) { 1943 return str_replace("\xFF\x00", "\xFF", $data); 1944 } 1945 1946 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { 1947 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( 1948 0x00 => 'No more than 128 frames and 1 MB total tag size', 1949 0x01 => 'No more than 64 frames and 128 KB total tag size', 1950 0x02 => 'No more than 32 frames and 40 KB total tag size', 1951 0x03 => 'No more than 32 frames and 4 KB total tag size', 1952 ); 1953 return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); 1954 } 1955 1956 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { 1957 static $LookupExtendedHeaderRestrictionsTextEncodings = array( 1958 0x00 => 'No restrictions', 1959 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', 1960 ); 1961 return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); 1962 } 1963 1964 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { 1965 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( 1966 0x00 => 'No restrictions', 1967 0x01 => 'No string is longer than 1024 characters', 1968 0x02 => 'No string is longer than 128 characters', 1969 0x03 => 'No string is longer than 30 characters', 1970 ); 1971 return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); 1972 } 1973 1974 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { 1975 static $LookupExtendedHeaderRestrictionsImageEncoding = array( 1976 0x00 => 'No restrictions', 1977 0x01 => 'Images are encoded only with PNG or JPEG', 1978 ); 1979 return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); 1980 } 1981 1982 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { 1983 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( 1984 0x00 => 'No restrictions', 1985 0x01 => 'All images are 256x256 pixels or smaller', 1986 0x02 => 'All images are 64x64 pixels or smaller', 1987 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', 1988 ); 1989 return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); 1990 } 1991 1992 public function LookupCurrencyUnits($currencyid) { 1993 1994 $begin = __LINE__; 1995 1996 /** This is not a comment! 1997 1998 1999 AED Dirhams 2000 AFA Afghanis 2001 ALL Leke 2002 AMD Drams 2003 ANG Guilders 2004 AOA Kwanza 2005 ARS Pesos 2006 ATS Schillings 2007 AUD Dollars 2008 AWG Guilders 2009 AZM Manats 2010 BAM Convertible Marka 2011 BBD Dollars 2012 BDT Taka 2013 BEF Francs 2014 BGL Leva 2015 BHD Dinars 2016 BIF Francs 2017 BMD Dollars 2018 BND Dollars 2019 BOB Bolivianos 2020 BRL Brazil Real 2021 BSD Dollars 2022 BTN Ngultrum 2023 BWP Pulas 2024 BYR Rubles 2025 BZD Dollars 2026 CAD Dollars 2027 CDF Congolese Francs 2028 CHF Francs 2029 CLP Pesos 2030 CNY Yuan Renminbi 2031 COP Pesos 2032 CRC Colones 2033 CUP Pesos 2034 CVE Escudos 2035 CYP Pounds 2036 CZK Koruny 2037 DEM Deutsche Marks 2038 DJF Francs 2039 DKK Kroner 2040 DOP Pesos 2041 DZD Algeria Dinars 2042 EEK Krooni 2043 EGP Pounds 2044 ERN Nakfa 2045 ESP Pesetas 2046 ETB Birr 2047 EUR Euro 2048 FIM Markkaa 2049 FJD Dollars 2050 FKP Pounds 2051 FRF Francs 2052 GBP Pounds 2053 GEL Lari 2054 GGP Pounds 2055 GHC Cedis 2056 GIP Pounds 2057 GMD Dalasi 2058 GNF Francs 2059 GRD Drachmae 2060 GTQ Quetzales 2061 GYD Dollars 2062 HKD Dollars 2063 HNL Lempiras 2064 HRK Kuna 2065 HTG Gourdes 2066 HUF Forints 2067 IDR Rupiahs 2068 IEP Pounds 2069 ILS New Shekels 2070 IMP Pounds 2071 INR Rupees 2072 IQD Dinars 2073 IRR Rials 2074 ISK Kronur 2075 ITL Lire 2076 JEP Pounds 2077 JMD Dollars 2078 JOD Dinars 2079 JPY Yen 2080 KES Shillings 2081 KGS Soms 2082 KHR Riels 2083 KMF Francs 2084 KPW Won 2085 KWD Dinars 2086 KYD Dollars 2087 KZT Tenge 2088 LAK Kips 2089 LBP Pounds 2090 LKR Rupees 2091 LRD Dollars 2092 LSL Maloti 2093 LTL Litai 2094 LUF Francs 2095 LVL Lati 2096 LYD Dinars 2097 MAD Dirhams 2098 MDL Lei 2099 MGF Malagasy Francs 2100 MKD Denars 2101 MMK Kyats 2102 MNT Tugriks 2103 MOP Patacas 2104 MRO Ouguiyas 2105 MTL Liri 2106 MUR Rupees 2107 MVR Rufiyaa 2108 MWK Kwachas 2109 MXN Pesos 2110 MYR Ringgits 2111 MZM Meticais 2112 NAD Dollars 2113 NGN Nairas 2114 NIO Gold Cordobas 2115 NLG Guilders 2116 NOK Krone 2117 NPR Nepal Rupees 2118 NZD Dollars 2119 OMR Rials 2120 PAB Balboa 2121 PEN Nuevos Soles 2122 PGK Kina 2123 PHP Pesos 2124 PKR Rupees 2125 PLN Zlotych 2126 PTE Escudos 2127 PYG Guarani 2128 QAR Rials 2129 ROL Lei 2130 RUR Rubles 2131 RWF Rwanda Francs 2132 SAR Riyals 2133 SBD Dollars 2134 SCR Rupees 2135 SDD Dinars 2136 SEK Kronor 2137 SGD Dollars 2138 SHP Pounds 2139 SIT Tolars 2140 SKK Koruny 2141 SLL Leones 2142 SOS Shillings 2143 SPL Luigini 2144 SRG Guilders 2145 STD Dobras 2146 SVC Colones 2147 SYP Pounds 2148 SZL Emalangeni 2149 THB Baht 2150 TJR Rubles 2151 TMM Manats 2152 TND Dinars 2153 TOP Pa'anga 2154 TRL Liras 2155 TTD Dollars 2156 TVD Tuvalu Dollars 2157 TWD New Dollars 2158 TZS Shillings 2159 UAH Hryvnia 2160 UGX Shillings 2161 USD Dollars 2162 UYU Pesos 2163 UZS Sums 2164 VAL Lire 2165 VEB Bolivares 2166 VND Dong 2167 VUV Vatu 2168 WST Tala 2169 XAF Francs 2170 XAG Ounces 2171 XAU Ounces 2172 XCD Dollars 2173 XDR Special Drawing Rights 2174 XPD Ounces 2175 XPF Francs 2176 XPT Ounces 2177 YER Rials 2178 YUM New Dinars 2179 ZAR Rand 2180 ZMK Kwacha 2181 ZWD Zimbabwe Dollars 2182 2183 */ 2184 2185 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); 2186 } 2187 2188 2189 public function LookupCurrencyCountry($currencyid) { 2190 2191 $begin = __LINE__; 2192 2193 /** This is not a comment! 2194 2195 AED United Arab Emirates 2196 AFA Afghanistan 2197 ALL Albania 2198 AMD Armenia 2199 ANG Netherlands Antilles 2200 AOA Angola 2201 ARS Argentina 2202 ATS Austria 2203 AUD Australia 2204 AWG Aruba 2205 AZM Azerbaijan 2206 BAM Bosnia and Herzegovina 2207 BBD Barbados 2208 BDT Bangladesh 2209 BEF Belgium 2210 BGL Bulgaria 2211 BHD Bahrain 2212 BIF Burundi 2213 BMD Bermuda 2214 BND Brunei Darussalam 2215 BOB Bolivia 2216 BRL Brazil 2217 BSD Bahamas 2218 BTN Bhutan 2219 BWP Botswana 2220 BYR Belarus 2221 BZD Belize 2222 CAD Canada 2223 CDF Congo/Kinshasa 2224 CHF Switzerland 2225 CLP Chile 2226 CNY China 2227 COP Colombia 2228 CRC Costa Rica 2229 CUP Cuba 2230 CVE Cape Verde 2231 CYP Cyprus 2232 CZK Czech Republic 2233 DEM Germany 2234 DJF Djibouti 2235 DKK Denmark 2236 DOP Dominican Republic 2237 DZD Algeria 2238 EEK Estonia 2239 EGP Egypt 2240 ERN Eritrea 2241 ESP Spain 2242 ETB Ethiopia 2243 EUR Euro Member Countries 2244 FIM Finland 2245 FJD Fiji 2246 FKP Falkland Islands (Malvinas) 2247 FRF France 2248 GBP United Kingdom 2249 GEL Georgia 2250 GGP Guernsey 2251 GHC Ghana 2252 GIP Gibraltar 2253 GMD Gambia 2254 GNF Guinea 2255 GRD Greece 2256 GTQ Guatemala 2257 GYD Guyana 2258 HKD Hong Kong 2259 HNL Honduras 2260 HRK Croatia 2261 HTG Haiti 2262 HUF Hungary 2263 IDR Indonesia 2264 IEP Ireland (Eire) 2265 ILS Israel 2266 IMP Isle of Man 2267 INR India 2268 IQD Iraq 2269 IRR Iran 2270 ISK Iceland 2271 ITL Italy 2272 JEP Jersey 2273 JMD Jamaica 2274 JOD Jordan 2275 JPY Japan 2276 KES Kenya 2277 KGS Kyrgyzstan 2278 KHR Cambodia 2279 KMF Comoros 2280 KPW Korea 2281 KWD Kuwait 2282 KYD Cayman Islands 2283 KZT Kazakstan 2284 LAK Laos 2285 LBP Lebanon 2286 LKR Sri Lanka 2287 LRD Liberia 2288 LSL Lesotho 2289 LTL Lithuania 2290 LUF Luxembourg 2291 LVL Latvia 2292 LYD Libya 2293 MAD Morocco 2294 MDL Moldova 2295 MGF Madagascar 2296 MKD Macedonia 2297 MMK Myanmar (Burma) 2298 MNT Mongolia 2299 MOP Macau 2300 MRO Mauritania 2301 MTL Malta 2302 MUR Mauritius 2303 MVR Maldives (Maldive Islands) 2304 MWK Malawi 2305 MXN Mexico 2306 MYR Malaysia 2307 MZM Mozambique 2308 NAD Namibia 2309 NGN Nigeria 2310 NIO Nicaragua 2311 NLG Netherlands (Holland) 2312 NOK Norway 2313 NPR Nepal 2314 NZD New Zealand 2315 OMR Oman 2316 PAB Panama 2317 PEN Peru 2318 PGK Papua New Guinea 2319 PHP Philippines 2320 PKR Pakistan 2321 PLN Poland 2322 PTE Portugal 2323 PYG Paraguay 2324 QAR Qatar 2325 ROL Romania 2326 RUR Russia 2327 RWF Rwanda 2328 SAR Saudi Arabia 2329 SBD Solomon Islands 2330 SCR Seychelles 2331 SDD Sudan 2332 SEK Sweden 2333 SGD Singapore 2334 SHP Saint Helena 2335 SIT Slovenia 2336 SKK Slovakia 2337 SLL Sierra Leone 2338 SOS Somalia 2339 SPL Seborga 2340 SRG Suriname 2341 STD São Tome and Principe 2342 SVC El Salvador 2343 SYP Syria 2344 SZL Swaziland 2345 THB Thailand 2346 TJR Tajikistan 2347 TMM Turkmenistan 2348 TND Tunisia 2349 TOP Tonga 2350 TRL Turkey 2351 TTD Trinidad and Tobago 2352 TVD Tuvalu 2353 TWD Taiwan 2354 TZS Tanzania 2355 UAH Ukraine 2356 UGX Uganda 2357 USD United States of America 2358 UYU Uruguay 2359 UZS Uzbekistan 2360 VAL Vatican City 2361 VEB Venezuela 2362 VND Viet Nam 2363 VUV Vanuatu 2364 WST Samoa 2365 XAF Communauté Financière Africaine 2366 XAG Silver 2367 XAU Gold 2368 XCD East Caribbean 2369 XDR International Monetary Fund 2370 XPD Palladium 2371 XPF Comptoirs Français du Pacifique 2372 XPT Platinum 2373 YER Yemen 2374 YUM Yugoslavia 2375 ZAR South Africa 2376 ZMK Zambia 2377 ZWD Zimbabwe 2378 2379 */ 2380 2381 return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); 2382 } 2383 2384 2385 2386 public static function LanguageLookup($languagecode, $casesensitive=false) { 2387 2388 if (!$casesensitive) { 2389 $languagecode = strtolower($languagecode); 2390 } 2391 2392 // http://www.id3.org/id3v2.4.0-structure.txt 2393 // [4. ID3v2 frame overview] 2394 // The three byte language field, present in several frames, is used to 2395 // describe the language of the frame's content, according to ISO-639-2 2396 // [ISO-639-2]. The language should be represented in lower case. If the 2397 // language is not known the string "XXX" should be used. 2398 2399 2400 // ISO 639-2 - http://www.id3.org/iso639-2.html 2401 2402 $begin = __LINE__; 2403 2404 /** This is not a comment! 2405 2406 XXX unknown 2407 xxx unknown 2408 aar Afar 2409 abk Abkhazian 2410 ace Achinese 2411 ach Acoli 2412 ada Adangme 2413 afa Afro-Asiatic (Other) 2414 afh Afrihili 2415 afr Afrikaans 2416 aka Akan 2417 akk Akkadian 2418 alb Albanian 2419 ale Aleut 2420 alg Algonquian Languages 2421 amh Amharic 2422 ang English, Old (ca. 450-1100) 2423 apa Apache Languages 2424 ara Arabic 2425 arc Aramaic 2426 arm Armenian 2427 arn Araucanian 2428 arp Arapaho 2429 art Artificial (Other) 2430 arw Arawak 2431 asm Assamese 2432 ath Athapascan Languages 2433 ava Avaric 2434 ave Avestan 2435 awa Awadhi 2436 aym Aymara 2437 aze Azerbaijani 2438 bad Banda 2439 bai Bamileke Languages 2440 bak Bashkir 2441 bal Baluchi 2442 bam Bambara 2443 ban Balinese 2444 baq Basque 2445 bas Basa 2446 bat Baltic (Other) 2447 bej Beja 2448 bel Byelorussian 2449 bem Bemba 2450 ben Bengali 2451 ber Berber (Other) 2452 bho Bhojpuri 2453 bih Bihari 2454 bik Bikol 2455 bin Bini 2456 bis Bislama 2457 bla Siksika 2458 bnt Bantu (Other) 2459 bod Tibetan 2460 bra Braj 2461 bre Breton 2462 bua Buriat 2463 bug Buginese 2464 bul Bulgarian 2465 bur Burmese 2466 cad Caddo 2467 cai Central American Indian (Other) 2468 car Carib 2469 cat Catalan 2470 cau Caucasian (Other) 2471 ceb Cebuano 2472 cel Celtic (Other) 2473 ces Czech 2474 cha Chamorro 2475 chb Chibcha 2476 che Chechen 2477 chg Chagatai 2478 chi Chinese 2479 chm Mari 2480 chn Chinook jargon 2481 cho Choctaw 2482 chr Cherokee 2483 chu Church Slavic 2484 chv Chuvash 2485 chy Cheyenne 2486 cop Coptic 2487 cor Cornish 2488 cos Corsican 2489 cpe Creoles and Pidgins, English-based (Other) 2490 cpf Creoles and Pidgins, French-based (Other) 2491 cpp Creoles and Pidgins, Portuguese-based (Other) 2492 cre Cree 2493 crp Creoles and Pidgins (Other) 2494 cus Cushitic (Other) 2495 cym Welsh 2496 cze Czech 2497 dak Dakota 2498 dan Danish 2499 del Delaware 2500 deu German 2501 din Dinka 2502 div Divehi 2503 doi Dogri 2504 dra Dravidian (Other) 2505 dua Duala 2506 dum Dutch, Middle (ca. 1050-1350) 2507 dut Dutch 2508 dyu Dyula 2509 dzo Dzongkha 2510 efi Efik 2511 egy Egyptian (Ancient) 2512 eka Ekajuk 2513 ell Greek, Modern (1453-) 2514 elx Elamite 2515 eng English 2516 enm English, Middle (ca. 1100-1500) 2517 epo Esperanto 2518 esk Eskimo (Other) 2519 esl Spanish 2520 est Estonian 2521 eus Basque 2522 ewe Ewe 2523 ewo Ewondo 2524 fan Fang 2525 fao Faroese 2526 fas Persian 2527 fat Fanti 2528 fij Fijian 2529 fin Finnish 2530 fiu Finno-Ugrian (Other) 2531 fon Fon 2532 fra French 2533 fre French 2534 frm French, Middle (ca. 1400-1600) 2535 fro French, Old (842- ca. 1400) 2536 fry Frisian 2537 ful Fulah 2538 gaa Ga 2539 gae Gaelic (Scots) 2540 gai Irish 2541 gay Gayo 2542 gdh Gaelic (Scots) 2543 gem Germanic (Other) 2544 geo Georgian 2545 ger German 2546 gez Geez 2547 gil Gilbertese 2548 glg Gallegan 2549 gmh German, Middle High (ca. 1050-1500) 2550 goh German, Old High (ca. 750-1050) 2551 gon Gondi 2552 got Gothic 2553 grb Grebo 2554 grc Greek, Ancient (to 1453) 2555 gre Greek, Modern (1453-) 2556 grn Guarani 2557 guj Gujarati 2558 hai Haida 2559 hau Hausa 2560 haw Hawaiian 2561 heb Hebrew 2562 her Herero 2563 hil Hiligaynon 2564 him Himachali 2565 hin Hindi 2566 hmo Hiri Motu 2567 hun Hungarian 2568 hup Hupa 2569 hye Armenian 2570 iba Iban 2571 ibo Igbo 2572 ice Icelandic 2573 ijo Ijo 2574 iku Inuktitut 2575 ilo Iloko 2576 ina Interlingua (International Auxiliary language Association) 2577 inc Indic (Other) 2578 ind Indonesian 2579 ine Indo-European (Other) 2580 ine Interlingue 2581 ipk Inupiak 2582 ira Iranian (Other) 2583 iri Irish 2584 iro Iroquoian uages 2585 isl Icelandic 2586 ita Italian 2587 jav Javanese 2588 jaw Javanese 2589 jpn Japanese 2590 jpr Judeo-Persian 2591 jrb Judeo-Arabic 2592 kaa Kara-Kalpak 2593 kab Kabyle 2594 kac Kachin 2595 kal Greenlandic 2596 kam Kamba 2597 kan Kannada 2598 kar Karen 2599 kas Kashmiri 2600 kat Georgian 2601 kau Kanuri 2602 kaw Kawi 2603 kaz Kazakh 2604 kha Khasi 2605 khi Khoisan (Other) 2606 khm Khmer 2607 kho Khotanese 2608 kik Kikuyu 2609 kin Kinyarwanda 2610 kir Kirghiz 2611 kok Konkani 2612 kom Komi 2613 kon Kongo 2614 kor Korean 2615 kpe Kpelle 2616 kro Kru 2617 kru Kurukh 2618 kua Kuanyama 2619 kum Kumyk 2620 kur Kurdish 2621 kus Kusaie 2622 kut Kutenai 2623 lad Ladino 2624 lah Lahnda 2625 lam Lamba 2626 lao Lao 2627 lat Latin 2628 lav Latvian 2629 lez Lezghian 2630 lin Lingala 2631 lit Lithuanian 2632 lol Mongo 2633 loz Lozi 2634 ltz Letzeburgesch 2635 lub Luba-Katanga 2636 lug Ganda 2637 lui Luiseno 2638 lun Lunda 2639 luo Luo (Kenya and Tanzania) 2640 mac Macedonian 2641 mad Madurese 2642 mag Magahi 2643 mah Marshall 2644 mai Maithili 2645 mak Macedonian 2646 mak Makasar 2647 mal Malayalam 2648 man Mandingo 2649 mao Maori 2650 map Austronesian (Other) 2651 mar Marathi 2652 mas Masai 2653 max Manx 2654 may Malay 2655 men Mende 2656 mga Irish, Middle (900 - 1200) 2657 mic Micmac 2658 min Minangkabau 2659 mis Miscellaneous (Other) 2660 mkh Mon-Kmer (Other) 2661 mlg Malagasy 2662 mlt Maltese 2663 mni Manipuri 2664 mno Manobo Languages 2665 moh Mohawk 2666 mol Moldavian 2667 mon Mongolian 2668 mos Mossi 2669 mri Maori 2670 msa Malay 2671 mul Multiple Languages 2672 mun Munda Languages 2673 mus Creek 2674 mwr Marwari 2675 mya Burmese 2676 myn Mayan Languages 2677 nah Aztec 2678 nai North American Indian (Other) 2679 nau Nauru 2680 nav Navajo 2681 nbl Ndebele, South 2682 nde Ndebele, North 2683 ndo Ndongo 2684 nep Nepali 2685 new Newari 2686 nic Niger-Kordofanian (Other) 2687 niu Niuean 2688 nla Dutch 2689 nno Norwegian (Nynorsk) 2690 non Norse, Old 2691 nor Norwegian 2692 nso Sotho, Northern 2693 nub Nubian Languages 2694 nya Nyanja 2695 nym Nyamwezi 2696 nyn Nyankole 2697 nyo Nyoro 2698 nzi Nzima 2699 oci Langue d'Oc (post 1500) 2700 oji Ojibwa 2701 ori Oriya 2702 orm Oromo 2703 osa Osage 2704 oss Ossetic 2705 ota Turkish, Ottoman (1500 - 1928) 2706 oto Otomian Languages 2707 paa Papuan-Australian (Other) 2708 pag Pangasinan 2709 pal Pahlavi 2710 pam Pampanga 2711 pan Panjabi 2712 pap Papiamento 2713 pau Palauan 2714 peo Persian, Old (ca 600 - 400 B.C.) 2715 per Persian 2716 phn Phoenician 2717 pli Pali 2718 pol Polish 2719 pon Ponape 2720 por Portuguese 2721 pra Prakrit uages 2722 pro Provencal, Old (to 1500) 2723 pus Pushto 2724 que Quechua 2725 raj Rajasthani 2726 rar Rarotongan 2727 roa Romance (Other) 2728 roh Rhaeto-Romance 2729 rom Romany 2730 ron Romanian 2731 rum Romanian 2732 run Rundi 2733 rus Russian 2734 sad Sandawe 2735 sag Sango 2736 sah Yakut 2737 sai South American Indian (Other) 2738 sal Salishan Languages 2739 sam Samaritan Aramaic 2740 san Sanskrit 2741 sco Scots 2742 scr Serbo-Croatian 2743 sel Selkup 2744 sem Semitic (Other) 2745 sga Irish, Old (to 900) 2746 shn Shan 2747 sid Sidamo 2748 sin Singhalese 2749 sio Siouan Languages 2750 sit Sino-Tibetan (Other) 2751 sla Slavic (Other) 2752 slk Slovak 2753 slo Slovak 2754 slv Slovenian 2755 smi Sami Languages 2756 smo Samoan 2757 sna Shona 2758 snd Sindhi 2759 sog Sogdian 2760 som Somali 2761 son Songhai 2762 sot Sotho, Southern 2763 spa Spanish 2764 sqi Albanian 2765 srd Sardinian 2766 srr Serer 2767 ssa Nilo-Saharan (Other) 2768 ssw Siswant 2769 ssw Swazi 2770 suk Sukuma 2771 sun Sudanese 2772 sus Susu 2773 sux Sumerian 2774 sve Swedish 2775 swa Swahili 2776 swe Swedish 2777 syr Syriac 2778 tah Tahitian 2779 tam Tamil 2780 tat Tatar 2781 tel Telugu 2782 tem Timne 2783 ter Tereno 2784 tgk Tajik 2785 tgl Tagalog 2786 tha Thai 2787 tib Tibetan 2788 tig Tigre 2789 tir Tigrinya 2790 tiv Tivi 2791 tli Tlingit 2792 tmh Tamashek 2793 tog Tonga (Nyasa) 2794 ton Tonga (Tonga Islands) 2795 tru Truk 2796 tsi Tsimshian 2797 tsn Tswana 2798 tso Tsonga 2799 tuk Turkmen 2800 tum Tumbuka 2801 tur Turkish 2802 tut Altaic (Other) 2803 twi Twi 2804 tyv Tuvinian 2805 uga Ugaritic 2806 uig Uighur 2807 ukr Ukrainian 2808 umb Umbundu 2809 und Undetermined 2810 urd Urdu 2811 uzb Uzbek 2812 vai Vai 2813 ven Venda 2814 vie Vietnamese 2815 vol Volapük 2816 vot Votic 2817 wak Wakashan Languages 2818 wal Walamo 2819 war Waray 2820 was Washo 2821 wel Welsh 2822 wen Sorbian Languages 2823 wol Wolof 2824 xho Xhosa 2825 yao Yao 2826 yap Yap 2827 yid Yiddish 2828 yor Yoruba 2829 zap Zapotec 2830 zen Zenaga 2831 zha Zhuang 2832 zho Chinese 2833 zul Zulu 2834 zun Zuni 2835 2836 */ 2837 2838 return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); 2839 } 2840 2841 2842 public static function ETCOEventLookup($index) { 2843 if (($index >= 0x17) && ($index <= 0xDF)) { 2844 return 'reserved for future use'; 2845 } 2846 if (($index >= 0xE0) && ($index <= 0xEF)) { 2847 return 'not predefined synch 0-F'; 2848 } 2849 if (($index >= 0xF0) && ($index <= 0xFC)) { 2850 return 'reserved for future use'; 2851 } 2852 2853 static $EventLookup = array( 2854 0x00 => 'padding (has no meaning)', 2855 0x01 => 'end of initial silence', 2856 0x02 => 'intro start', 2857 0x03 => 'main part start', 2858 0x04 => 'outro start', 2859 0x05 => 'outro end', 2860 0x06 => 'verse start', 2861 0x07 => 'refrain start', 2862 0x08 => 'interlude start', 2863 0x09 => 'theme start', 2864 0x0A => 'variation start', 2865 0x0B => 'key change', 2866 0x0C => 'time change', 2867 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', 2868 0x0E => 'sustained noise', 2869 0x0F => 'sustained noise end', 2870 0x10 => 'intro end', 2871 0x11 => 'main part end', 2872 0x12 => 'verse end', 2873 0x13 => 'refrain end', 2874 0x14 => 'theme end', 2875 0x15 => 'profanity', 2876 0x16 => 'profanity end', 2877 0xFD => 'audio end (start of silence)', 2878 0xFE => 'audio file ends', 2879 0xFF => 'one more byte of events follows' 2880 ); 2881 2882 return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); 2883 } 2884 2885 public static function SYTLContentTypeLookup($index) { 2886 static $SYTLContentTypeLookup = array( 2887 0x00 => 'other', 2888 0x01 => 'lyrics', 2889 0x02 => 'text transcription', 2890 0x03 => 'movement/part name', // (e.g. 'Adagio') 2891 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') 2892 0x05 => 'chord', // (e.g. 'Bb F Fsus') 2893 0x06 => 'trivia/\'pop up\' information', 2894 0x07 => 'URLs to webpages', 2895 0x08 => 'URLs to images' 2896 ); 2897 2898 return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); 2899 } 2900 2901 public static function APICPictureTypeLookup($index, $returnarray=false) { 2902 static $APICPictureTypeLookup = array( 2903 0x00 => 'Other', 2904 0x01 => '32x32 pixels \'file icon\' (PNG only)', 2905 0x02 => 'Other file icon', 2906 0x03 => 'Cover (front)', 2907 0x04 => 'Cover (back)', 2908 0x05 => 'Leaflet page', 2909 0x06 => 'Media (e.g. label side of CD)', 2910 0x07 => 'Lead artist/lead performer/soloist', 2911 0x08 => 'Artist/performer', 2912 0x09 => 'Conductor', 2913 0x0A => 'Band/Orchestra', 2914 0x0B => 'Composer', 2915 0x0C => 'Lyricist/text writer', 2916 0x0D => 'Recording Location', 2917 0x0E => 'During recording', 2918 0x0F => 'During performance', 2919 0x10 => 'Movie/video screen capture', 2920 0x11 => 'A bright coloured fish', 2921 0x12 => 'Illustration', 2922 0x13 => 'Band/artist logotype', 2923 0x14 => 'Publisher/Studio logotype' 2924 ); 2925 if ($returnarray) { 2926 return $APICPictureTypeLookup; 2927 } 2928 return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); 2929 } 2930 2931 public static function COMRReceivedAsLookup($index) { 2932 static $COMRReceivedAsLookup = array( 2933 0x00 => 'Other', 2934 0x01 => 'Standard CD album with other songs', 2935 0x02 => 'Compressed audio on CD', 2936 0x03 => 'File over the Internet', 2937 0x04 => 'Stream over the Internet', 2938 0x05 => 'As note sheets', 2939 0x06 => 'As note sheets in a book with other sheets', 2940 0x07 => 'Music on other media', 2941 0x08 => 'Non-musical merchandise' 2942 ); 2943 2944 return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); 2945 } 2946 2947 public static function RVA2ChannelTypeLookup($index) { 2948 static $RVA2ChannelTypeLookup = array( 2949 0x00 => 'Other', 2950 0x01 => 'Master volume', 2951 0x02 => 'Front right', 2952 0x03 => 'Front left', 2953 0x04 => 'Back right', 2954 0x05 => 'Back left', 2955 0x06 => 'Front centre', 2956 0x07 => 'Back centre', 2957 0x08 => 'Subwoofer' 2958 ); 2959 2960 return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); 2961 } 2962 2963 public static function FrameNameLongLookup($framename) { 2964 2965 $begin = __LINE__; 2966 2967 /** This is not a comment! 2968 2969 AENC Audio encryption 2970 APIC Attached picture 2971 ASPI Audio seek point index 2972 BUF Recommended buffer size 2973 CNT Play counter 2974 COM Comments 2975 COMM Comments 2976 COMR Commercial frame 2977 CRA Audio encryption 2978 CRM Encrypted meta frame 2979 ENCR Encryption method registration 2980 EQU Equalisation 2981 EQU2 Equalisation (2) 2982 EQUA Equalisation 2983 ETC Event timing codes 2984 ETCO Event timing codes 2985 GEO General encapsulated object 2986 GEOB General encapsulated object 2987 GRID Group identification registration 2988 IPL Involved people list 2989 IPLS Involved people list 2990 LINK Linked information 2991 LNK Linked information 2992 MCDI Music CD identifier 2993 MCI Music CD Identifier 2994 MLL MPEG location lookup table 2995 MLLT MPEG location lookup table 2996 OWNE Ownership frame 2997 PCNT Play counter 2998 PIC Attached picture 2999 POP Popularimeter 3000 POPM Popularimeter 3001 POSS Position synchronisation frame 3002 PRIV Private frame 3003 RBUF Recommended buffer size 3004 REV Reverb 3005 RVA Relative volume adjustment 3006 RVA2 Relative volume adjustment (2) 3007 RVAD Relative volume adjustment 3008 RVRB Reverb 3009 SEEK Seek frame 3010 SIGN Signature frame 3011 SLT Synchronised lyric/text 3012 STC Synced tempo codes 3013 SYLT Synchronised lyric/text 3014 SYTC Synchronised tempo codes 3015 TAL Album/Movie/Show title 3016 TALB Album/Movie/Show title 3017 TBP BPM (Beats Per Minute) 3018 TBPM BPM (beats per minute) 3019 TCM Composer 3020 TCMP Part of a compilation 3021 TCO Content type 3022 TCOM Composer 3023 TCON Content type 3024 TCOP Copyright message 3025 TCP Part of a compilation 3026 TCR Copyright message 3027 TDA Date 3028 TDAT Date 3029 TDEN Encoding time 3030 TDLY Playlist delay 3031 TDOR Original release time 3032 TDRC Recording time 3033 TDRL Release time 3034 TDTG Tagging time 3035 TDY Playlist delay 3036 TEN Encoded by 3037 TENC Encoded by 3038 TEXT Lyricist/Text writer 3039 TFLT File type 3040 TFT File type 3041 TIM Time 3042 TIME Time 3043 TIPL Involved people list 3044 TIT1 Content group description 3045 TIT2 Title/songname/content description 3046 TIT3 Subtitle/Description refinement 3047 TKE Initial key 3048 TKEY Initial key 3049 TLA Language(s) 3050 TLAN Language(s) 3051 TLE Length 3052 TLEN Length 3053 TMCL Musician credits list 3054 TMED Media type 3055 TMOO Mood 3056 TMT Media type 3057 TOA Original artist(s)/performer(s) 3058 TOAL Original album/movie/show title 3059 TOF Original filename 3060 TOFN Original filename 3061 TOL Original Lyricist(s)/text writer(s) 3062 TOLY Original lyricist(s)/text writer(s) 3063 TOPE Original artist(s)/performer(s) 3064 TOR Original release year 3065 TORY Original release year 3066 TOT Original album/Movie/Show title 3067 TOWN File owner/licensee 3068 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group 3069 TP2 Band/Orchestra/Accompaniment 3070 TP3 Conductor/Performer refinement 3071 TP4 Interpreted, remixed, or otherwise modified by 3072 TPA Part of a set 3073 TPB Publisher 3074 TPE1 Lead performer(s)/Soloist(s) 3075 TPE2 Band/orchestra/accompaniment 3076 TPE3 Conductor/performer refinement 3077 TPE4 Interpreted, remixed, or otherwise modified by 3078 TPOS Part of a set 3079 TPRO Produced notice 3080 TPUB Publisher 3081 TRC ISRC (International Standard Recording Code) 3082 TRCK Track number/Position in set 3083 TRD Recording dates 3084 TRDA Recording dates 3085 TRK Track number/Position in set 3086 TRSN Internet radio station name 3087 TRSO Internet radio station owner 3088 TS2 Album-Artist sort order 3089 TSA Album sort order 3090 TSC Composer sort order 3091 TSI Size 3092 TSIZ Size 3093 TSO2 Album-Artist sort order 3094 TSOA Album sort order 3095 TSOC Composer sort order 3096 TSOP Performer sort order 3097 TSOT Title sort order 3098 TSP Performer sort order 3099 TSRC ISRC (international standard recording code) 3100 TSS Software/hardware and settings used for encoding 3101 TSSE Software/Hardware and settings used for encoding 3102 TSST Set subtitle 3103 TST Title sort order 3104 TT1 Content group description 3105 TT2 Title/Songname/Content description 3106 TT3 Subtitle/Description refinement 3107 TXT Lyricist/text writer 3108 TXX User defined text information frame 3109 TXXX User defined text information frame 3110 TYE Year 3111 TYER Year 3112 UFI Unique file identifier 3113 UFID Unique file identifier 3114 ULT Unsychronised lyric/text transcription 3115 USER Terms of use 3116 USLT Unsynchronised lyric/text transcription 3117 WAF Official audio file webpage 3118 WAR Official artist/performer webpage 3119 WAS Official audio source webpage 3120 WCM Commercial information 3121 WCOM Commercial information 3122 WCOP Copyright/Legal information 3123 WCP Copyright/Legal information 3124 WOAF Official audio file webpage 3125 WOAR Official artist/performer webpage 3126 WOAS Official audio source webpage 3127 WORS Official Internet radio station homepage 3128 WPAY Payment 3129 WPB Publishers official webpage 3130 WPUB Publishers official webpage 3131 WXX User defined URL link frame 3132 WXXX User defined URL link frame 3133 TFEA Featured Artist 3134 TSTU Recording Studio 3135 rgad Replay Gain Adjustment 3136 3137 */ 3138 3139 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); 3140 3141 // Last three: 3142 // from Helium2 [www.helium2.com] 3143 // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html 3144 } 3145 3146 3147 public static function FrameNameShortLookup($framename) { 3148 3149 $begin = __LINE__; 3150 3151 /** This is not a comment! 3152 3153 AENC audio_encryption 3154 APIC attached_picture 3155 ASPI audio_seek_point_index 3156 BUF recommended_buffer_size 3157 CNT play_counter 3158 COM comment 3159 COMM comment 3160 COMR commercial_frame 3161 CRA audio_encryption 3162 CRM encrypted_meta_frame 3163 ENCR encryption_method_registration 3164 EQU equalisation 3165 EQU2 equalisation 3166 EQUA equalisation 3167 ETC event_timing_codes 3168 ETCO event_timing_codes 3169 GEO general_encapsulated_object 3170 GEOB general_encapsulated_object 3171 GRID group_identification_registration 3172 IPL involved_people_list 3173 IPLS involved_people_list 3174 LINK linked_information 3175 LNK linked_information 3176 MCDI music_cd_identifier 3177 MCI music_cd_identifier 3178 MLL mpeg_location_lookup_table 3179 MLLT mpeg_location_lookup_table 3180 OWNE ownership_frame 3181 PCNT play_counter 3182 PIC attached_picture 3183 POP popularimeter 3184 POPM popularimeter 3185 POSS position_synchronisation_frame 3186 PRIV private_frame 3187 RBUF recommended_buffer_size 3188 REV reverb 3189 RVA relative_volume_adjustment 3190 RVA2 relative_volume_adjustment 3191 RVAD relative_volume_adjustment 3192 RVRB reverb 3193 SEEK seek_frame 3194 SIGN signature_frame 3195 SLT synchronised_lyric 3196 STC synced_tempo_codes 3197 SYLT synchronised_lyric 3198 SYTC synchronised_tempo_codes 3199 TAL album 3200 TALB album 3201 TBP bpm 3202 TBPM bpm 3203 TCM composer 3204 TCMP part_of_a_compilation 3205 TCO genre 3206 TCOM composer 3207 TCON genre 3208 TCOP copyright_message 3209 TCP part_of_a_compilation 3210 TCR copyright_message 3211 TDA date 3212 TDAT date 3213 TDEN encoding_time 3214 TDLY playlist_delay 3215 TDOR original_release_time 3216 TDRC recording_time 3217 TDRL release_time 3218 TDTG tagging_time 3219 TDY playlist_delay 3220 TEN encoded_by 3221 TENC encoded_by 3222 TEXT lyricist 3223 TFLT file_type 3224 TFT file_type 3225 TIM time 3226 TIME time 3227 TIPL involved_people_list 3228 TIT1 content_group_description 3229 TIT2 title 3230 TIT3 subtitle 3231 TKE initial_key 3232 TKEY initial_key 3233 TLA language 3234 TLAN language 3235 TLE length 3236 TLEN length 3237 TMCL musician_credits_list 3238 TMED media_type 3239 TMOO mood 3240 TMT media_type 3241 TOA original_artist 3242 TOAL original_album 3243 TOF original_filename 3244 TOFN original_filename 3245 TOL original_lyricist 3246 TOLY original_lyricist 3247 TOPE original_artist 3248 TOR original_year 3249 TORY original_year 3250 TOT original_album 3251 TOWN file_owner 3252 TP1 artist 3253 TP2 band 3254 TP3 conductor 3255 TP4 remixer 3256 TPA part_of_a_set 3257 TPB publisher 3258 TPE1 artist 3259 TPE2 band 3260 TPE3 conductor 3261 TPE4 remixer 3262 TPOS part_of_a_set 3263 TPRO produced_notice 3264 TPUB publisher 3265 TRC isrc 3266 TRCK track_number 3267 TRD recording_dates 3268 TRDA recording_dates 3269 TRK track_number 3270 TRSN internet_radio_station_name 3271 TRSO internet_radio_station_owner 3272 TS2 album_artist_sort_order 3273 TSA album_sort_order 3274 TSC composer_sort_order 3275 TSI size 3276 TSIZ size 3277 TSO2 album_artist_sort_order 3278 TSOA album_sort_order 3279 TSOC composer_sort_order 3280 TSOP performer_sort_order 3281 TSOT title_sort_order 3282 TSP performer_sort_order 3283 TSRC isrc 3284 TSS encoder_settings 3285 TSSE encoder_settings 3286 TSST set_subtitle 3287 TST title_sort_order 3288 TT1 content_group_description 3289 TT2 title 3290 TT3 subtitle 3291 TXT lyricist 3292 TXX text 3293 TXXX text 3294 TYE year 3295 TYER year 3296 UFI unique_file_identifier 3297 UFID unique_file_identifier 3298 ULT unsychronised_lyric 3299 USER terms_of_use 3300 USLT unsynchronised_lyric 3301 WAF url_file 3302 WAR url_artist 3303 WAS url_source 3304 WCM commercial_information 3305 WCOM commercial_information 3306 WCOP copyright 3307 WCP copyright 3308 WOAF url_file 3309 WOAR url_artist 3310 WOAS url_source 3311 WORS url_station 3312 WPAY url_payment 3313 WPB url_publisher 3314 WPUB url_publisher 3315 WXX url_user 3316 WXXX url_user 3317 TFEA featured_artist 3318 TSTU recording_studio 3319 rgad replay_gain_adjustment 3320 3321 */ 3322 3323 return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); 3324 } 3325 3326 public static function TextEncodingTerminatorLookup($encoding) { 3327 // http://www.id3.org/id3v2.4.0-structure.txt 3328 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3329 static $TextEncodingTerminatorLookup = array( 3330 0 => "\x00", // $00 ISO-8859-1. Terminated with $00. 3331 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. 3332 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3333 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. 3334 255 => "\x00\x00" 3335 ); 3336 return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : ''); 3337 } 3338 3339 public static function TextEncodingNameLookup($encoding) { 3340 // http://www.id3.org/id3v2.4.0-structure.txt 3341 // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: 3342 static $TextEncodingNameLookup = array( 3343 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00. 3344 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. 3345 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. 3346 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00. 3347 255 => 'UTF-16BE' 3348 ); 3349 return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); 3350 } 3351 3352 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { 3353 switch ($id3v2majorversion) { 3354 case 2: 3355 return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); 3356 break; 3357 3358 case 3: 3359 case 4: 3360 return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); 3361 break; 3362 } 3363 return false; 3364 } 3365 3366 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { 3367 for ($i = 0; $i < strlen($numberstring); $i++) { 3368 if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { 3369 if (($numberstring{$i} == '.') && $allowdecimal) { 3370 // allowed 3371 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { 3372 // allowed 3373 } else { 3374 return false; 3375 } 3376 } 3377 } 3378 return true; 3379 } 3380 3381 public static function IsValidDateStampString($datestamp) { 3382 if (strlen($datestamp) != 8) { 3383 return false; 3384 } 3385 if (!self::IsANumber($datestamp, false)) { 3386 return false; 3387 } 3388 $year = substr($datestamp, 0, 4); 3389 $month = substr($datestamp, 4, 2); 3390 $day = substr($datestamp, 6, 2); 3391 if (($year == 0) || ($month == 0) || ($day == 0)) { 3392 return false; 3393 } 3394 if ($month > 12) { 3395 return false; 3396 } 3397 if ($day > 31) { 3398 return false; 3399 } 3400 if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { 3401 return false; 3402 } 3403 if (($day > 29) && ($month == 2)) { 3404 return false; 3405 } 3406 return true; 3407 } 3408 3409 public static function ID3v2HeaderLength($majorversion) { 3410 return (($majorversion == 2) ? 6 : 10); 3411 } 3412 3413 } 3414
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |