Instruction how to read ID3 tag version 2.
According to the ID3v2.3 specification header contians following information:
So first of all script should check first 10 bytes:
set fr [open $fileN r] set data [read $fr 10] binary scan $data a3c2b8I1 id3header ver flags headSize
Let's try to extract some info if we are sure that there is ID3 tag.
According to the specification each frame has header contains following informations:
All we need to do is find Frame ID extract its size and print out its content:
set data [read $fr 10] binary scan $data a4I1B16 frameType frameSize frameFlags
Everything is quite clear and easy but ... sometimes saved frame size is wrong, then is not so easy to find next frame. To avoid situation that script is trying to read wrond data I've crated $allFrames list of almost all possible frames. Script checks all received data and corrects position in the file if wrong frame ID is detected.
Additionaly $frameToDisplay list is created to limit printed out data. All printed out data has a human readable names defined via frameMap array:
set frameMap(COMM) Comment set frameMap(TCOM) Composer set frameMap(TIT1) "Content group description" set frameMap(TIT2) "Title" set frameMap(TPE1) "Lead performer(s)/Soloist(s)" set frameMap(TPE2) "Band" set frameMap(TPE3) "Conductor" set frameMap(TPE4) "Interpreted, remixed, or otherwise modified by" set frameMap(TYER) "Year"
Script:
set debugMode 0 #set debugMode 1 #procedure to print out debugs if enabled proc putss {str} { global debugMode if {$debugMode} {puts $str} } proc parseID3v2 {fileN} { set fr [open $fileN r] set frameToDisplay {COMM TCOM TIT1 TIT2 TPE1 TPE2 TPE3 TPE4 TYER} set allFrames {AENC APIC COMM COMR ENCR EQUA ETCO GEOB GRID IPLS LINK MCDI MLLT OWNE PRIV PCNT POPM POSS RBUF RVAD\ RVRB SYLT SYTC TALB TBPM TCOM TCON TCOP TDAT TDLY TENC TEXT TFLT TIME TIT1 TIT2 TIT3 TKEY TLAN TLEN TMED TOAL TOFN\ TOLY TOPE TORY TOWN TPE1 TPE2 TPE3 TPE4 TPOS TPUB TRCK TRDA TRSN TRSO TSIZ TSRC TSSE TYER TXXX UFID USER USLT WCOM\ WCOP WOAF WOAR WOAS WORS WPAY WPUB WXXX} set frameMap(COMM) Comment set frameMap(TCOM) Composer set frameMap(TIT1) "Content group description" set frameMap(TIT2) "Title" set frameMap(TPE1) "Lead performer(s)/Soloist(s)" set frameMap(TPE2) "Band" set frameMap(TPE3) "Conductor" set frameMap(TPE4) "Interpreted, remixed, or otherwise modified by" set frameMap(TYER) "Year" #read header and check basic info set data [read $fr 10] binary scan $data a3c2b8I1 id3header ver flags headSize putss "### Basic file info ###" putss $id3header putss $ver putss $flags putss $headSize putss "########################" #read and check frame basic info set frameSize 0 while {[tell $fr] < $headSize} { set data [read $fr 10] set prevFrameSize $frameSize binary scan $data a4I1B16 frameType frameSize frameFlags if {[expr 0x[format %x [scan 0100000000000000 %i]] & 0x[format %x [scan $frameFlags %i]]] eq 2147483647} { putss "alter file detected" } putss "[tell $fr] frameType: $frameType, frameSize: $frameSize, frameFlags: $frameFlags" if {[lsearch $allFrames $frameType] < 0} { putss "Unknown frame $frameType detected!" putss [tell $fr] set currentPos [tell $fr] putss "Move back $prevFrameSize and find first defined frame" seek $fr [expr $currentPos - $prevFrameSize] putss [tell $fr] if {[set newPos [___findNextFrame $fr $allFrames $headSize]] > 0} { seek $fr $newPos continue } } if {$frameSize < 0} {puts "Wrong id3 tag structure!"; return -1} set data [read $fr [expr $frameSize]] if {[lsearch $frameToDisplay $frameType] >= 0} { puts "$frameMap($frameType) : $data" } } close $fr } proc ___findNextFrame {fileHandler frameList headerSize} { set curretnPos [tell $fileHandler] set index 0 while {[tell $fileHandler] < $headerSize} { seek $fileHandler [expr $curretnPos + $index] set data [read $fileHandler 4] if {[lsearch $frameList $data] >= 0} { set newPos [expr $curretnPos + $index] putss "New frame $data detected under position $newPos" return $newPos } incr index } return -1 }
To test the script call:
parseID3v2 test.mp3