PageRenderTime 105ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/getid3/module.tag.id3v2.php

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