getid3_id3v2

The WordPress Core getid3 id3v2 class.

Defined (1)

The class is defined in the following location(s).

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