PageRenderTime 88ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Vendor/getid3/module.tag.id3v2.php

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