source: gs3-extensions/web-audio/trunk/js-mad/jsmad-master/experiments/src/id3v23stream.js@ 28388

Last change on this file since 28388 was 28388, checked in by davidb, 11 years ago

Set of JS, CSS, PNG etc web resources to support a mixture of audio player/document display capabilities

File size: 15.1 KB
Line 
1var trimString = function (string) {
2 return string.replace(/\0$/, "");
3};
4
5var stringToEncoding = function(string, encoding) {
6 switch (encoding) {
7 case 0:
8 return trimString(string);
9 case 1:
10 var ix = 2, offset1 = 1, offset2 = 0;
11
12 if (string.slice(0, 2) === "\xFE\xFF") {
13 offset1 = 0, offset2 = 1;
14 } else {
15 offset1 = 1, offset2 = 0;
16 }
17
18 var result = "";
19
20 for (var ix = 2; ix < string.length; ix += 2) {
21 var byte1 = string[ix + offset1].charCodeAt(0);
22 var byte2 = string[ix + offset2].charCodeAt(0);
23
24 var word1 = (byte1 << 8) | byte2;
25
26 if (byte1 < 0xD8 || byte1 >= 0xE0) {
27 result += String.fromCharCode(word1);
28 } else {
29 ix += 2;
30
31 var byte3 = string[ix + offset1].charCodeAt(0);
32 var byte4 = string[ix + offset2].charCodeAt(0);
33
34 var word2 = (byte3 << 8) | byte4;
35
36 result += String.fromCharCode(word1, word2);
37 }
38 }
39
40 return trimString(result);
41 default:
42 return string;
43 }
44}
45
46var decodeTerminatedString = function(header, stream) {
47
48 // skip text encoding (nobody cares, always ISO-8559-15)
49 stream.readAsString(1);
50
51 var c;
52
53 var mimetype = "";
54 while(true) {
55 c = stream.readAsString(1);
56 if(c === "\0") {
57 break;
58 }
59 mimetype += c;
60 }
61 console.log("mimetype = " + mimetype);
62
63 var filename = "";
64 while(true) {
65 c = stream.readAsString(1);
66 if(c === "\0") {
67 break;
68 }
69 filename += c;
70 }
71 console.log("filename = " + filename);
72
73 var contentDescription = "";
74 while(true) {
75 c = stream.readAsString(1);
76 if(c === "\0") {
77 break;
78 }
79 contentDescription += c;
80 }
81 console.log("contentDescription = " + contentDescription);
82
83 var objectName = stream.readAsString(4);
84
85 // skip 4 bytes, then read binary length
86 stream.readAsString(4);
87 var length = stream.readU32(true) - 4;
88
89 if(length > header.length) return null;
90 var value = stream.readAsString(length);
91
92 return {
93 'header': header,
94 'mimetype': mimetype,
95 'filename': filename,
96 'contentDescription': contentDescription,
97 'objectName': objectName,
98 'value': value
99 };
100}
101
102var decodeTextFrame = function(header, stream) {
103 var encoding = stream.readU8();
104 var data = stream.readAsString(header['length'] - 1);
105
106 return {
107 'header': header,
108 'value': stringToEncoding(data, encoding)
109 };
110}
111
112var decodeAttachedPictureFrame = function(header, stream) {
113 var encoding = stream.readU8();
114
115 var data = stream.readAsString(header['length'] - 1);
116
117 var array = data.split("\0");
118
119 return {
120 'header': header,
121 'mime': stringToEncoding(array[0], 0),
122 'type': array[1].charCodeAt(0),
123 'description': array[1].slice(1, array[1].length - 1),
124 'value': array.slice(2, array.length - 2).join("\0")
125 };
126}
127
128var decodeIdentifierFrame = function(header, stream) {
129 var data = stream.readAsString(header['length']);
130
131 var array = data.split("\0", 2);
132
133 return {
134 'header': header,
135 'owner': array[0],
136 'identifier': array[1]
137 };
138}
139
140
141var decodeCommentFrame = function(header, stream) {
142 var encoding = stream.readU8();
143 var language = stream.readAsString(3);
144
145 var data = stream.readAsString(header['length'] - 4);
146
147 var array = data.split("\0", 2);
148
149 return {
150 'header': header,
151 'language': stringToEncoding(language, 0),
152 'description': stringToEncoding(array[0], 0),
153 'value': stringToEncoding(array[1], encoding)
154 };
155}
156
157var decodeBinaryFrame = function(header, stream) {
158 var data = stream.readAsString(header['length']);
159
160 return {
161 'header': header,
162 'value': data
163 };
164}
165
166var decodeUserDefinedLinkFrame = function(header, stream) {
167 var encoding = stream.readU8();
168 var data = stream.readAsString(header['length'] - 1);
169
170 var array = data.split("\0", 2);
171
172 return {
173 'header': header,
174 'description': stringToEncoding(array[0], 0),
175 'value': stringToEncoding(array[1], encoding)
176 };
177}
178
179var extendWithValueFrame = function(tags, frame) {
180 tags[frame['name']] = frame['value'];
181
182 return tags;
183}
184
185var extendWithPictureFrame = function(tags, frame) {
186 if (!tags[frame['name']]) {
187 tags[frame['name']] = []
188 }
189
190 tags[frame['name']].push({
191 'mime': frame['mime'],
192 'type': frame['type'],
193 'description': frame['description'],
194 'value': frame['value']
195 });
196
197 return tags;
198}
199
200var extendWithIdentifierFrame = function(tags, frame) {
201 tags[frame['name']] = {
202 'owner': frame['value'],
203 'identifier': frame['identifier']
204 };
205
206 return tags;
207}
208
209var extendWithCommentFrame = function(tags, frame) {
210 if (!tags[frame['name']]) {
211 tags[frame['name']] = []
212 }
213
214 tags[frame['name']][frame['description']] = {
215 'language': frame['language'],
216 'value': frame['value']
217 };
218
219 return tags;
220}
221
222var extendWithUserDefinedLinkFrame = function(tags, frame) {
223 if (!tags[frame['name']]) {
224 tags[frame['name']] = {}
225 }
226
227 tags[frame['name']][frame['description']] = frame['value'];
228
229 return tags;
230}
231
232var extendWithPrivateFrame = function(tags, frame) {
233 if (!tags[frame['name']]) {
234 tags[frame['name']] = []
235 }
236
237 tags[frame['name']].push(frame['value']);
238
239 return tags;
240}
241
242Mad.ID3v23Stream = function(header, stream) {
243 this.offset = 0;
244
245 this.header = header;
246 this.stream = stream;
247
248 this.decoders = {
249 /* Identification Frames */
250 'TIT1': decodeTextFrame,
251 'TIT2': decodeTextFrame,
252 'TIT3': decodeTextFrame,
253 'TALB': decodeTextFrame,
254 'TOAL': decodeTextFrame,
255 'TRCK': decodeTextFrame,
256 'TPOS': decodeTextFrame,
257 'TSST': decodeTextFrame,
258 'TSRC': decodeTextFrame,
259
260 /* Involved Persons Frames */
261 'TPE1': decodeTextFrame,
262 'TPE2': decodeTextFrame,
263 'TPE3': decodeTextFrame,
264 'TPE4': decodeTextFrame,
265 'TOPE': decodeTextFrame,
266 'TEXT': decodeTextFrame,
267 'TOLY': decodeTextFrame,
268 'TCOM': decodeTextFrame,
269 'TMCL': decodeTextFrame,
270 'TIPL': decodeTextFrame,
271 'TENC': decodeTextFrame,
272
273 /* Derived and Subjective Properties Frames */
274 'TBPM': decodeTextFrame,
275 'TLEN': decodeTextFrame,
276 'TKEY': decodeTextFrame,
277 'TLAN': decodeTextFrame,
278 'TCON': decodeTextFrame,
279 'TFLT': decodeTextFrame,
280 'TMED': decodeTextFrame,
281 'TMOO': decodeTextFrame,
282
283 /* Rights and Licence Frames */
284 'TCOP': decodeTextFrame,
285 'TPRO': decodeTextFrame,
286 'TPUB': decodeTextFrame,
287 'TOWN': decodeTextFrame,
288 'TRSN': decodeTextFrame,
289 'TRSO': decodeTextFrame,
290
291 /* Other Text Frames */
292 'TOFN': decodeTextFrame,
293 'TDLY': decodeTextFrame,
294 'TDEN': decodeTextFrame,
295 'TDOR': decodeTextFrame,
296 'TDRC': decodeTextFrame,
297 'TDRL': decodeTextFrame,
298 'TDTG': decodeTextFrame,
299 'TSSE': decodeTextFrame,
300 'TSOA': decodeTextFrame,
301 'TSOP': decodeTextFrame,
302 'TSOT': decodeTextFrame,
303
304 /* Attached Picture Frame */
305 'APIC': decodeAttachedPictureFrame,
306
307 /* Unique Identifier Frame */
308 'UFID': decodeIdentifierFrame,
309
310 /* Music CD Identifier Frame */
311 'MCDI': decodeBinaryFrame,
312
313 /* Comment Frame */
314 'COMM': decodeCommentFrame,
315
316 /* User Defined URL Link Frame */
317 'WXXX': decodeUserDefinedLinkFrame,
318
319 /* Private Frame */
320 'PRIV': decodeBinaryFrame,
321
322 /* Deprecated ID3v2 Frames */
323 'TDAT': decodeTextFrame,
324 'TIME': decodeTextFrame,
325 'TORY': decodeTextFrame,
326 'TRDA': decodeTextFrame,
327 'TSIZ': decodeTextFrame,
328 'TYER': decodeTextFrame,
329
330 /* General encapsulated object */
331 'GEOB': decodeTerminatedString
332 };
333
334 this.names = {
335 /* Identification Frames */
336 'TIT1': 'Content group description',
337 'TIT2': 'Title/Songname/Content description',
338 'TIT3': 'Subtitle/Description refinement',
339 'TALB': 'Album/Movie/Show title',
340 'TOAL': 'Original album/movie/show title',
341 'TRCK': 'Track number/Position in set',
342 'TPOS': 'Part of a set',
343 'TSST': 'Set subtitle',
344 'TSRC': 'ISRC',
345
346 /* Involved Persons Frames */
347 'TPE1': 'Lead artist/Lead performer/Soloist/Performing group',
348 'TPE2': 'Band/Orchestra/Accompaniment',
349 'TPE3': 'Conductor',
350 'TPE4': 'Interpreted, remixed, or otherwise modified by',
351 'TOPE': 'Original artist/performer',
352 'TEXT': 'Lyricist/Text writer',
353 'TOLY': 'Original lyricist/text writer',
354 'TCOM': 'Composer',
355 'TMCL': 'Musician credits list',
356 'TIPL': 'Involved people list',
357 'TENC': 'Encoded by',
358
359 /* Derived and Subjective Properties Frames */
360 'TBPM': 'BPM',
361 'TLEN': 'Length',
362 'TKEY': 'Initial key',
363 'TLAN': 'Language',
364 'TCON': 'Content type',
365 'TFLT': 'File type',
366 'TMED': 'Media type',
367 'TMOO': 'Mood',
368
369 /* Rights and Licence Frames */
370 'TCOP': 'Copyright message',
371 'TPRO': 'Produced notice',
372 'TPUB': 'Publisher',
373 'TOWN': 'File owner/licensee',
374 'TRSN': 'Internet radio station name',
375 'TRSO': 'Internet radio station owner',
376
377 /* Other Text Frames */
378 'TOFN': 'Original filename',
379 'TDLY': 'Playlist delay',
380 'TDEN': 'Encoding time',
381 'TDOR': 'Original release time',
382 'TDRC': 'Recording time',
383 'TDRL': 'Release time',
384 'TDTG': 'Tagging time',
385 'TSSE': 'Software/Hardware and settings used for encoding',
386 'TSOA': 'Album sort order',
387 'TSOP': 'Performer sort order',
388 'TSOT': 'Title sort order',
389
390 /* Attached Picture Frame */
391 'APIC': 'Attached picture',
392
393 /* Unique Identifier Frame */
394 'UFID': 'Unique identifier',
395
396 /* Music CD Identifier Frame */
397 'MCDI': 'Music CD identifier',
398
399 /* Comment Frame */
400 'COMM': 'Comment',
401
402 /* User Defined URL Link Frame */
403 'WXXX': 'User defined URL link',
404
405 /* Private Frame */
406 'PRIV': 'Private',
407
408 /* Deprecated ID3v2 frames */
409 'TDAT': 'Date',
410 'TIME': 'Time',
411 'TORY': 'Original release year',
412 'TRDA': 'Recording dates',
413 'TSIZ': 'Size',
414 'TYER': 'Year'
415 };
416
417 this.extenders = {
418 /* Identification Frames */
419 'TIT1': extendWithValueFrame,
420 'TIT2': extendWithValueFrame,
421 'TIT3': extendWithValueFrame,
422 'TALB': extendWithValueFrame,
423 'TOAL': extendWithValueFrame,
424 'TRCK': extendWithValueFrame,
425 'TPOS': extendWithValueFrame,
426 'TSST': extendWithValueFrame,
427 'TSRC': extendWithValueFrame,
428
429 /* Involved Persons Frames */
430 'TPE1': extendWithValueFrame,
431 'TPE2': extendWithValueFrame,
432 'TPE3': extendWithValueFrame,
433 'TPE4': extendWithValueFrame,
434 'TOPE': extendWithValueFrame,
435 'TEXT': extendWithValueFrame,
436 'TOLY': extendWithValueFrame,
437 'TCOM': extendWithValueFrame,
438 'TMCL': extendWithValueFrame,
439 'TIPL': extendWithValueFrame,
440 'TENC': extendWithValueFrame,
441
442 /* Derived and Subjective Properties Frames */
443 'TBPM': extendWithValueFrame,
444 'TLEN': extendWithValueFrame,
445 'TKEY': extendWithValueFrame,
446 'TLAN': extendWithValueFrame,
447 'TCON': extendWithValueFrame,
448 'TFLT': extendWithValueFrame,
449 'TMED': extendWithValueFrame,
450 'TMOO': extendWithValueFrame,
451
452 /* Rights and Licence Frames */
453 'TCOP': extendWithValueFrame,
454 'TPRO': extendWithValueFrame,
455 'TPUB': extendWithValueFrame,
456 'TOWN': extendWithValueFrame,
457 'TRSN': extendWithValueFrame,
458 'TRSO': extendWithValueFrame,
459
460 /* Other Text Frames */
461 'TOFN': extendWithValueFrame,
462 'TDLY': extendWithValueFrame,
463 'TDEN': extendWithValueFrame,
464 'TDOR': extendWithValueFrame,
465 'TDRC': extendWithValueFrame,
466 'TDRL': extendWithValueFrame,
467 'TDTG': extendWithValueFrame,
468 'TSSE': extendWithValueFrame,
469 'TSOA': extendWithValueFrame,
470 'TSOP': extendWithValueFrame,
471 'TSOT': extendWithValueFrame,
472
473 /* Attached Picture Frame */
474 'APIC': extendWithPictureFrame,
475
476 /* Unique Identifier Frame */
477 'UFID': extendWithIdentifierFrame,
478
479 /* Music CD Identifier */
480 'MCDI': extendWithValueFrame,
481
482 /* Comment Frame */
483 'COMM': extendWithCommentFrame,
484
485 /* User Defined URL Link Frame */
486 'WXXX': extendWithUserDefinedLinkFrame,
487
488 /* Private Frame */
489 'PRIV': extendWithPrivateFrame,
490
491 /* Deprecated ID3v2 Frames */
492 'TDAT': extendWithValueFrame,
493 'TIME': extendWithValueFrame,
494 'TORY': extendWithValueFrame,
495 'TRDA': extendWithValueFrame,
496 'TSIZ': extendWithValueFrame,
497 'TYER': extendWithValueFrame
498 };
499}
500
501Mad.ID3v23Stream.prototype.readFrame = function() {
502 if (this.offset >= this.header.length) {
503 return null;
504 }
505
506 var identifier = this.stream.readAsString(4);
507
508 if (identifier.charCodeAt(0) === 0) {
509 this.offset = this.header.length + 1;
510 return null;
511 }
512
513 var length = this.stream.readU32(true);
514 var flags = this.stream.readU16(true);
515
516 var header = {
517 'identifier': identifier,
518 'length': length,
519 'flags': flags
520 };
521
522 var result = null;
523
524 if (this.decoders[identifier]) {
525 result = this.decoders[identifier](header, this.stream);
526 } else {
527 result = {
528 'identifier': identifier,
529 'header': header
530 };
531
532 this.stream.readAsString(Math.min(length, this.header.length - this.offset));
533 }
534
535 if(result) {
536 result['name'] = this.names[identifier] ? this.names[identifier] : 'UNKNOWN';
537 }
538
539 this.offset += 10 + length;
540
541 return result;
542}
543
544Mad.ID3v23Stream.prototype.read = function() {
545 if (!this.array) {
546 this.array = [];
547
548 var frame = null;
549
550 //try {
551 while (frame = this.readFrame()) {
552 this.array.push(frame);
553 }
554 //} catch (e) {
555 //throw(e);
556 //console.log("ID3 Error: " + e);
557 //}
558 }
559
560 return this.array;
561}
562
563Mad.ID3v23Stream.prototype.toHash = function() {
564 var frames = this.read();
565
566 var hash = {};
567
568 for (var i = 0; i < frames.length; i++) {
569 var frame = frames[i];
570
571 var extender = this.extenders[frame['header']['identifier']];
572
573 if (extender) {
574 hash = extender(hash, frames[i]);
575 }
576 }
577
578 return hash;
579}
Note: See TracBrowser for help on using the repository browser.