PageRenderTime 81ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 1ms

/components/com_jce/editor/tiny_mce/plugins/mediamanager/classes/getid3/module.tag.id3v2.php

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