PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/zina/extras/getid3/module.tag.id3v2.php

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