Files

856 lines
30 KiB
JavaScript

(function ( w, d, PKAE ) {
'use strict';
var StringUtils = {
readUTF16String: function(bytes, bigEndian, maxBytes) {
var ix = 0;
var offset1 = 1, offset2 = 0;
maxBytes = Math.min(maxBytes||bytes.length, bytes.length);
if( bytes[0] == 0xFE && bytes[1] == 0xFF ) {
bigEndian = true;
ix = 2;
} else if( bytes[0] == 0xFF && bytes[1] == 0xFE ) {
bigEndian = false;
ix = 2;
}
if( bigEndian ) {
offset1 = 0;
offset2 = 1;
}
var arr = [];
for( var j = 0; ix < maxBytes; j++ ) {
var byte1 = bytes[ix+offset1];
var byte2 = bytes[ix+offset2];
var word1 = (byte1<<8)+byte2;
ix += 2;
if( word1 == 0x0000 ) {
break;
} else if( byte1 < 0xD8 || byte1 >= 0xE0 ) {
arr[j] = String.fromCharCode(word1);
} else {
var byte3 = bytes[ix+offset1];
var byte4 = bytes[ix+offset2];
var word2 = (byte3<<8)+byte4;
ix += 2;
arr[j] = String.fromCharCode(word1, word2);
}
}
var string = new String(arr.join(""));
string.bytesReadCount = ix;
return string;
},
readUTF8String: function(bytes, maxBytes) {
var ix = 0;
maxBytes = Math.min(maxBytes||bytes.length, bytes.length);
if( bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF ) {
ix = 3;
}
var arr = [];
for( var j = 0; ix < maxBytes; j++ ) {
var byte1 = bytes[ix++];
if( byte1 == 0x00 ) {
break;
} else if( byte1 < 0x80 ) {
arr[j] = String.fromCharCode(byte1);
} else if( byte1 >= 0xC2 && byte1 < 0xE0 ) {
var byte2 = bytes[ix++];
arr[j] = String.fromCharCode(((byte1&0x1F)<<6) + (byte2&0x3F));
} else if( byte1 >= 0xE0 && byte1 < 0xF0 ) {
var byte2 = bytes[ix++];
var byte3 = bytes[ix++];
arr[j] = String.fromCharCode(((byte1&0xFF)<<12) + ((byte2&0x3F)<<6) + (byte3&0x3F));
} else if( byte1 >= 0xF0 && byte1 < 0xF5) {
var byte2 = bytes[ix++];
var byte3 = bytes[ix++];
var byte4 = bytes[ix++];
var codepoint = ((byte1&0x07)<<18) + ((byte2&0x3F)<<12)+ ((byte3&0x3F)<<6) + (byte4&0x3F) - 0x10000;
arr[j] = String.fromCharCode(
(codepoint>>10) + 0xD800,
(codepoint&0x3FF) + 0xDC00
);
}
}
var string = new String(arr.join(""));
string.bytesReadCount = ix;
return string;
},
readNullTerminatedString: function(bytes, maxBytes) {
var arr = [];
maxBytes = maxBytes || bytes.length;
for ( var i = 0; i < maxBytes; ) {
var byte1 = bytes[i++];
if( byte1 == 0x00 ) break;
arr[i-1] = String.fromCharCode(byte1);
}
var string = new String(arr.join(""));
string.bytesReadCount = i;
return string;
}
};
var getBytesAt = function(data, iOffset, iLength) {
var bytes = new Array(iLength);
for( var i = 0; i < iLength; i++ ) {
bytes[i] = data.getUint8(iOffset+i);
}
return bytes;
};
var getStringWithCharsetAt = function(data, iOffset, iLength, iCharset) {
var bytes = getBytesAt(data, iOffset, iLength);
var sString;
switch( iCharset.toLowerCase() ) {
case 'utf-16':
case 'utf-16le':
case 'utf-16be':
sString = StringUtils.readUTF16String(bytes, iCharset);
break;
case 'utf-8':
sString = StringUtils.readUTF8String(bytes);
break;
default:
sString = StringUtils.readNullTerminatedString(bytes);
break;
}
return sString;
};
var ID3v2 = {
readFrameData: {}
};
ID3v2.frames = {
// v2.2
"BUF" : "Recommended buffer size",
"CNT" : "Play counter",
"COM" : "Comments",
"CRA" : "Audio encryption",
"CRM" : "Encrypted meta frame",
"ETC" : "Event timing codes",
"EQU" : "Equalization",
"GEO" : "General encapsulated object",
"IPL" : "Involved people list",
"LNK" : "Linked information",
"MCI" : "Music CD Identifier",
"MLL" : "MPEG location lookup table",
"PIC" : "Attached picture",
"POP" : "Popularimeter",
"REV" : "Reverb",
"RVA" : "Relative volume adjustment",
"SLT" : "Synchronized lyric/text",
"STC" : "Synced tempo codes",
"TAL" : "Album/Movie/Show title",
"TBP" : "BPM (Beats Per Minute)",
"TCM" : "Composer",
"TCO" : "Content type",
"TCR" : "Copyright message",
"TDA" : "Date",
"TDY" : "Playlist delay",
"TEN" : "Encoded by",
"TFT" : "File type",
"TIM" : "Time",
"TKE" : "Initial key",
"TLA" : "Language(s)",
"TLE" : "Length",
"TMT" : "Media type",
"TOA" : "Original artist(s)/performer(s)",
"TOF" : "Original filename",
"TOL" : "Original Lyricist(s)/text writer(s)",
"TOR" : "Original release year",
"TOT" : "Original album/Movie/Show title",
"TP1" : "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
"TP2" : "Band/Orchestra/Accompaniment",
"TP3" : "Conductor/Performer refinement",
"TP4" : "Interpreted, remixed, or otherwise modified by",
"TPA" : "Part of a set",
"TPB" : "Publisher",
"TRC" : "ISRC (International Standard Recording Code)",
"TRD" : "Recording dates",
"TRK" : "Track number/Position in set",
"TSI" : "Size",
"TSS" : "Software/hardware and settings used for encoding",
"TT1" : "Content group description",
"TT2" : "Title/Songname/Content description",
"TT3" : "Subtitle/Description refinement",
"TXT" : "Lyricist/text writer",
"TXX" : "User defined text information frame",
"TYE" : "Year",
"UFI" : "Unique file identifier",
"ULT" : "Unsychronized lyric/text transcription",
"WAF" : "Official audio file webpage",
"WAR" : "Official artist/performer webpage",
"WAS" : "Official audio source webpage",
"WCM" : "Commercial information",
"WCP" : "Copyright/Legal information",
"WPB" : "Publishers official webpage",
"WXX" : "User defined URL link frame",
// v2.3
"AENC" : "Audio encryption",
"APIC" : "Attached picture",
"COMM" : "Comments",
"COMR" : "Commercial frame",
"ENCR" : "Encryption method registration",
"EQUA" : "Equalization",
"ETCO" : "Event timing codes",
"GEOB" : "General encapsulated object",
"GRID" : "Group identification registration",
"IPLS" : "Involved people list",
"LINK" : "Linked information",
"MCDI" : "Music CD identifier",
"MLLT" : "MPEG location lookup table",
"OWNE" : "Ownership frame",
"PRIV" : "Private frame",
"PCNT" : "Play counter",
"POPM" : "Popularimeter",
"POSS" : "Position synchronisation frame",
"RBUF" : "Recommended buffer size",
"RVAD" : "Relative volume adjustment",
"RVRB" : "Reverb",
"SYLT" : "Synchronized lyric/text",
"SYTC" : "Synchronized tempo codes",
"TALB" : "Album/Movie/Show title",
"TBPM" : "BPM (beats per minute)",
"TCOM" : "Composer",
"TCON" : "Content type",
"TCOP" : "Copyright message",
"TDAT" : "Date",
"TDLY" : "Playlist delay",
"TENC" : "Encoded by",
"TEXT" : "Lyricist/Text writer",
"TFLT" : "File type",
"TIME" : "Time",
"TIT1" : "Content group description",
"TIT2" : "Title/songname/content description",
"TIT3" : "Subtitle/Description refinement",
"TKEY" : "Initial key",
"TLAN" : "Language(s)",
"TLEN" : "Length",
"TMED" : "Media type",
"TOAL" : "Original album/movie/show title",
"TOFN" : "Original filename",
"TOLY" : "Original lyricist(s)/text writer(s)",
"TOPE" : "Original artist(s)/performer(s)",
"TORY" : "Original release year",
"TOWN" : "File owner/licensee",
"TPE1" : "Lead performer(s)/Soloist(s)",
"TPE2" : "Band/orchestra/accompaniment",
"TPE3" : "Conductor/performer refinement",
"TPE4" : "Interpreted, remixed, or otherwise modified by",
"TPOS" : "Part of a set",
"TPUB" : "Publisher",
"TRCK" : "Track number/Position in set",
"TRDA" : "Recording dates",
"TRSN" : "Internet radio station name",
"TRSO" : "Internet radio station owner",
"TSIZ" : "Size",
"TSRC" : "ISRC (international standard recording code)",
"TSSE" : "Software/Hardware and settings used for encoding",
"TYER" : "Year",
"TXXX" : "User defined text information frame",
"UFID" : "Unique file identifier",
"USER" : "Terms of use",
"USLT" : "Unsychronized lyric/text transcription",
"WCOM" : "Commercial information",
"WCOP" : "Copyright/Legal information",
"WOAF" : "Official audio file webpage",
"WOAR" : "Official artist/performer webpage",
"WOAS" : "Official audio source webpage",
"WORS" : "Official internet radio station homepage",
"WPAY" : "Payment",
"WPUB" : "Publishers official webpage",
"WXXX" : "User defined URL link frame"
};
var pictureType = [
"32x32 pixels 'file icon' (PNG only)",
"Other file icon",
"Cover (front)",
"Cover (back)",
"Leaflet page",
"Media (e.g. lable side of CD)",
"Lead artist/lead performer/soloist",
"Artist/performer",
"Conductor",
"Band/Orchestra",
"Composer",
"Lyricist/text writer",
"Recording Location",
"During recording",
"During performance",
"Movie/video screen capture",
"A bright coloured fish",
"Illustration",
"Band/artist logotype",
"Publisher/Studio logotype"
];
var getStringAt = function(data, iOffset, iLength) {
var aStr = [];
for (var i=iOffset,j=0;i<iOffset+iLength;i++,j++) {
aStr[j] = String.fromCharCode(data.getUint8(i));
}
return aStr.join("");
};
var getLongAt = function(data, iOffset, bBigEndian) {
var iByte1 = data.getUint8(iOffset),
iByte2 = data.getUint8(iOffset + 1),
iByte3 = data.getUint8(iOffset + 2),
iByte4 = data.getUint8(iOffset + 3);
var iLong = bBigEndian ?
(((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4
: (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
if (iLong < 0) iLong += 4294967296;
return iLong;
};
var getSLongAt = function(data, iOffset, bBigEndian) {
var iULong = getLongAt(data, iOffset, bBigEndian);
if (iULong > 2147483647)
return iULong - 4294967296;
else
return iULong;
};
var getShortAt = function(data, iOffset, bBigEndian) {
var iShort = bBigEndian ?
(data.getUint8(iOffset) << 8) + data.getUint8(iOffset + 1)
: (data.getUint8(iOffset + 1) << 8) + data.getUint8(iOffset);
if (iShort < 0) iShort += 65536;
return iShort;
};
var getInteger24At = function(data, iOffset, bBigEndian) {
var iByte1 = data.getUint8(iOffset),
iByte2 = data.getUint8(iOffset + 1),
iByte3 = data.getUint8(iOffset + 2);
var iInteger = bBigEndian ?
((((iByte1 << 8) + iByte2) << 8) + iByte3)
: ((((iByte3 << 8) + iByte2) << 8) + iByte1);
if (iInteger < 0) iInteger += 16777216;
return iInteger;
};
var isBitSetAt = function ( dataview, offset, bit ) {
var ibyte = dataview.getUint8(offset);
return (ibyte & (1 << bit)) != 0;
};
var readSynchsafeInteger32At = function (offset, data) {
var size1 = data.getUint8(offset);
var size2 = data.getUint8(offset+1);
var size3 = data.getUint8(offset+2);
var size4 = data.getUint8(offset+3);
// 0x7f = 0b01111111
var size = size4 & 0x7f
| ((size3 & 0x7f) << 7)
| ((size2 & 0x7f) << 14)
| ((size1 & 0x7f) << 21);
return size;
};
var readFrameFlags = function(data, offset) {
var flags =
{
message:
{
tag_alter_preservation : isBitSetAt(data, offset, 6),
file_alter_preservation : isBitSetAt(data, offset, 5),
read_only : isBitSetAt(data, offset, 4)
},
format:
{
grouping_identity : isBitSetAt(data, offset+1, 7),
compression : isBitSetAt(data, offset+1, 3),
encription : isBitSetAt(data, offset+1, 2),
unsynchronisation : isBitSetAt(data, offset+1, 1),
data_length_indicator : isBitSetAt(data, offset+1, 0)
}
};
return flags;
};
var _shortcuts = {
"title" : ["TIT2", "TT2"],
"artist" : ["TPE1", "TP1"],
"album" : ["TALB", "TAL"],
"year" : ["TYER", "TYE"],
"comment" : ["COMM", "COM"],
"track" : ["TRCK", "TRK"],
"genre" : ["TCON", "TCO"],
"picture" : ["APIC", "PIC"],
"lyrics" : ["USLT", "ULT"]
};
var _defaultShortcuts = ["title", "artist", "album", "track"];
var getTagsFromShortcuts = function(shortcuts) {
var tags = [];
for( var i = 0, shortcut; shortcut = shortcuts[i]; i++ ) {
tags = tags.concat(_shortcuts[shortcut]||[shortcut]);
}
return tags;
};
var getFrameData = function( frames, ids ) {
if( typeof ids == 'string' ) { ids = [ids]; }
for( var i = 0, id; id = ids[i]; i++ ) {
if( id in frames ) { return frames[id].data; }
}
};
var readFrames = function (offset, end, data, id3header, tags) {
var frames = {};
var frameDataSize;
var major = id3header["major"];
tags = getTagsFromShortcuts(tags || _defaultShortcuts);
while( offset < end ) {
var readFrameFunc = null;
var frameData = data;
var frameDataOffset = offset;
var flags = null;
switch( major ) {
case 2:
var frameID = getStringAt(frameData, frameDataOffset, 3);
var frameSize = getInteger24At(frameData, frameDataOffset+3, true);
var frameHeaderSize = 6;
break;
case 3:
var frameID = getStringAt(frameData, frameDataOffset, 4);
var frameSize = getLongAt(frameData, frameDataOffset+4, true);
var frameHeaderSize = 10;
break;
case 4:
var frameID = getStringAt(frameData, frameDataOffset, 4);
var frameSize = readSynchsafeInteger32At(frameDataOffset+4, frameData);
var frameHeaderSize = 10;
break;
}
// if last frame GTFO
if( frameID == "" ) { break; }
// advance data offset to the next frame data
offset += frameHeaderSize + frameSize;
// skip unwanted tags
if( tags.indexOf( frameID ) < 0 ) { continue; }
// read frame message and format flags
if( major > 2 )
{
flags = readFrameFlags(frameData, frameDataOffset+8);
}
frameDataOffset += frameHeaderSize;
// the first 4 bytes are the real data size
// (after unsynchronisation && encryption)
if( flags && flags.format.data_length_indicator )
{
frameDataSize = readSynchsafeInteger32At(frameDataOffset, frameData);
frameDataOffset += 4;
frameSize -= 4;
}
// TODO: support unsynchronisation
if( flags && flags.format.unsynchronisation )
{
//frameData = removeUnsynchronisation(frameData, frameSize);
continue;
}
// find frame parsing function
if( frameID in ID3v2.readFrameData ) {
readFrameFunc = ID3v2.readFrameData[frameID];
} else if( frameID[0] == "T" ) {
readFrameFunc = ID3v2.readFrameData["T*"];
}
var parsedData = readFrameFunc ? readFrameFunc(frameDataOffset, frameSize, frameData, flags) : undefined;
var desc = frameID in ID3v2.frames ? ID3v2.frames[frameID] : 'Unknown';
var frame = {
id : frameID,
size : frameSize,
description : desc,
data : parsedData
};
if( frameID in frames ) {
if( frames[frameID].id ) {
frames[frameID] = [frames[frameID]];
}
frames[frameID].push(frame);
} else {
frames[frameID] = frame;
}
}
return frames;
};
function getTextEncoding( bite ) {
var charset;
switch( bite )
{
case 0x00:
charset = 'iso-8859-1';
break;
case 0x01:
charset = 'utf-16';
break;
case 0x02:
charset = 'utf-16be';
break;
case 0x03:
charset = 'utf-8';
break;
}
return charset;
}
function getTime( duration )
{
var duration = duration/1000,
seconds = Math.floor( duration ) % 60,
minutes = Math.floor( duration/60 ) % 60,
hours = Math.floor( duration/3600 );
return {
seconds : seconds,
minutes : minutes,
hours : hours
};
}
function formatTime( time )
{
var seconds = time.seconds < 10 ? '0'+time.seconds : time.seconds;
var minutes = (time.hours > 0 && time.minutes < 10) ? '0'+time.minutes : time.minutes;
return (time.hours>0?time.hours+':':'') + minutes + ':' + seconds;
}
ID3v2.readFrameData['APIC'] = function readPictureFrame(offset, length, data, flags, v) {
v = v || '3';
var start = offset;
var charset = getTextEncoding( data.getUint8(offset) );
switch( v ) {
case '2':
var format = getStringAt(data, offset+1, 3);
offset += 4;
break;
case '3':
case '4':
var format = getStringWithCharsetAt(data, offset+1, length - (offset-start), '');
offset += 1 + format.bytesReadCount;
break;
}
var bite = data.getUint8(offset, 1);
var type = pictureType[bite];
var desc = getStringWithCharsetAt(data, offset+1, length - (offset-start), charset);
offset += 1 + desc.bytesReadCount;
return {
"format" : format.toString(),
"type" : type,
"description" : desc.toString(),
"data" : getBytesAt(data, offset, (start+length) - offset)
};
};
ID3v2.readFrameData['COMM'] = function readCommentsFrame(offset, length, data) {
var start = offset;
var charset = getTextEncoding( data.getUint8(offset) );
var language = getStringAt(data, offset+1, 3 );
var shortdesc = getStringWithCharsetAt(data, offset+4, length-4, charset);
offset += 4 + shortdesc.bytesReadCount;
var text = getStringWithCharsetAt(data, offset, (start+length) - offset, charset );
return {
language : language,
short_description : shortdesc.toString(),
text : text.toString()
};
};
ID3v2.readFrameData['COM'] = ID3v2.readFrameData['COMM'];
ID3v2.readFrameData['PIC'] = function(offset, length, data, flags) {
return ID3v2.readFrameData['APIC'](offset, length, data, flags, '2');
};
ID3v2.readFrameData['PCNT'] = function readCounterFrame(offset, length, data) {
// FIXME: implement the rest of the spec
return data.getInteger32At(offset);
};
ID3v2.readFrameData['CNT'] = ID3v2.readFrameData['PCNT'];
ID3v2.readFrameData['T*'] = function readTextFrame(offset, length, data) {
var charset = getTextEncoding( data.getUint8(offset) );
return getStringWithCharsetAt(data, offset+1, length-1, charset).toString();
};
ID3v2.readFrameData['TCON'] = function readGenreFrame(offset, length, data) {
var text = ID3v2.readFrameData['T*'].apply( this, arguments );
return text.replace(/^\(\d+\)/, '');
};
ID3v2.readFrameData['TCO'] = ID3v2.readFrameData['TCON'];
//ID3v2.readFrameData['TLEN'] = function readLengthFrame(offset, length, data) {
// var text = ID3v2.readFrameData['T*'].apply( this, arguments );
//
// return {
// text : text,
// parsed : formatTime( getTime(parseInt(text)) )
// };
//};
ID3v2.readFrameData['USLT'] = function readLyricsFrame(offset, length, data) {
var start = offset;
var charset = getTextEncoding( data.getUint8(offset) );
var language = getStringAt(data, offset+1, 3 );
var descriptor = getStringWithCharsetAt(data, offset+4, length-4, charset );
offset += 4 + descriptor.bytesReadCount;
var lyrics = getStringWithCharsetAt(data, offset, (start+length) - offset, charset );
return {
language : language,
descriptor : descriptor.toString(),
lyrics : lyrics.toString()
};
};
ID3v2.readFrameData['ULT'] = ID3v2.readFrameData['USLT'];
ID3v2.ReadTags = function ( arraybuffer ) {
var data = new DataView ( arraybuffer );
var offset = 0;
var major = data.getUint8(offset+3);
if( major > 4 ) { return {version: '>2.4'}; }
var revision = data.getUint8(offset+4);
var unsynch = isBitSetAt(data, offset+5, 7);
var xheader = isBitSetAt(data, offset+5, 6);
var xindicator = isBitSetAt(data, offset+5, 5);
var size = readSynchsafeInteger32At(offset+6, data);
offset += 10;
if( xheader ) {
var xheadersize = data.getInt32( offset, true ); //data.getLongAt(offset, true);
// The 'Extended header size', currently 6 or 10 bytes, excludes itself.
offset += xheadersize + 4;
}
var id3 = {
"version" : '2.' + major + '.' + revision,
"major" : major,
"revision" : revision,
"flags" : {
"unsynchronisation" : unsynch,
"extended_header" : xheader,
"experimental_indicator" : xindicator
},
"size" : size
};
var frames = unsynch ? {} : readFrames(offset, size-10, data, id3);
// create shortcuts for most common data
for( var name in _shortcuts ) if(_shortcuts.hasOwnProperty(name)) {
var data = getFrameData( frames, _shortcuts[name] );
if( data ) id3[name] = data;
}
for( var frame in frames ) {
if( frames.hasOwnProperty(frame) ) {
id3[frame] = frames[frame];
}
}
return id3;
};
w.ID3v2 = ID3v2;
/// -------
var ID4 = {};
ID4.types = {
'0' : 'uint8',
'1' : 'text',
'13' : 'jpeg',
'14' : 'png',
'21' : 'uint8'
};
ID4.atom = {
'©alb': ['album'],
'©art': ['artist'],
'©ART': ['artist'],
'aART': ['artist'],
'©day': ['year'],
'©nam': ['title'],
'©gen': ['genre'],
'trkn': ['track'],
'©wrt': ['composer'],
'©too': ['encoder'],
'cprt': ['copyright'],
'covr': ['picture'],
'©grp': ['grouping'],
'keyw': ['keyword'],
'©lyr': ['lyrics'],
'©cmt': ['comment'],
'tmpo': ['tempo'],
'cpil': ['compilation'],
'disk': ['disc']
};
ID4.loadData = function(arraybuffer, callback) {
var data = new DataView ( arraybuffer );
// load the header of the first block
loadAtom(data, 0, data.byteLength, callback);
};
/**
* Make sure that the [offset, offset+7] bytes (the block header) are
* already loaded before calling this function.
*/
function loadAtom(data, offset, length, callback) {
// 8 is the size of the atomSize and atomName fields.
// When reading the current block we always read 8 more bytes in order
// to also read the header of the next block.
var atomSize = getLongAt(data, offset, true);
if (atomSize == 0) return callback();
var atomName = getStringAt(data, offset + 4, 4);
// Container atoms
if (['moov', 'udta', 'meta', 'ilst'].indexOf(atomName) > -1)
{
if (atomName == 'meta') offset += 4; // next_item_id (uint32)
// data.loadRange([offset+8, offset+8 + 8], function() {
loadAtom(data, offset + 8, atomSize - 8, callback);
// });
} else {
// Value atoms
var readAtom = atomName in ID4.atom;
// data.loadRange([offset+(readAtom?0:atomSize), offset+atomSize + 8], function() {
loadAtom(data, offset+atomSize, length, callback);
// });
}
};
ID4.ReadTags = function(arraybuffer) {
var data = new DataView ( arraybuffer );
var tag = {};
readAtom(tag, data, 0, data.byteLength);
return tag;
};
function readAtom(tag, data, offset, length, indent)
{
// debugger;
indent = indent === undefined ? "" : indent + " ";
var seek = offset;
while (seek < offset + length)
{
var atomSize = data.getInt32(seek); // getLongAt(data, seek, true);
if (atomSize == 0) return;
var atomName = getStringAt(data, seek + 4, 4);
// Container atoms
if (atomName === 'meta')
{
seek += 4; // next_item_id (uint32)
readAtom(tag, data, seek + 8, atomSize - 8, indent);
return;
}
if (atomName === 'moov' || atomName === 'udta' || atomName === 'ilst' ) // ['moov', 'udta', 'meta', 'ilst'].indexOf(atomName) > -1)
{
readAtom(tag, data, seek + 8, atomSize - 8, indent);
return;
}
/*
if (['moov', 'udta', 'meta', 'ilst'].indexOf(atomName) > -1)
{
if (atomName === 'meta') seek += 4; // next_item_id (uint32)
readAtom(tag, data, seek + 8, atomSize - 8, indent);
return;
}
*/
// Value atoms
if (ID4.atom[atomName])
{
var klass = getInteger24At(data, seek + 16 + 1, true);
var atom = ID4.atom[atomName];
var type = ID4.types[klass];
if (atomName === 'trkn')
{
tag[atom[0]] = data.getUint8(seek + 16 + 11);
tag['count'] = data.getUint8(seek + 16 + 13);
}
else
{
// 16: name + size + "data" + size (4 bytes each)
// 4: atom version (1 byte) + atom flags (3 bytes)
// 4: NULL (usually locale indicator)
var dataStart = seek + 16 + 4 + 4;
var dataEnd = atomSize - 16 - 4 - 4;
var atomData;
switch( type ) {
case 'text':
atomData = getStringWithCharsetAt(data, dataStart, dataEnd, "UTF-8");
break;
case 'uint8':
atomData = getShortAt(data, dataStart);
break;
case 'jpeg':
case 'png':
atomData = {
format : "image/" + type,
data : getBytesAt(data, dataStart, dataEnd)
};
break;
}
if (atom[0] === "comment") {
tag[atom[0]] = {
"text": atomData
};
} else {
tag[atom[0]] = atomData;
}
}
}
seek += atomSize;
}
}
w.ID4 = ID4;
})( window, document, PKAudioEditor );