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