#! /usr/local/bin/tclsh8.3 package require msgcat namespace import msgcat::* if {[info exists ::env(HOME)]} { set initial_dir $::env(HOME) } else { set initial_dir [pwd] } # # Read tag (if there is one) # proc rd_tag {} { global title artist album year comment track genre hastag fnam set fd [open $fnam r] fconfigure $fd -encoding binary -translation binary if {[catch {seek $fd -128 end}]} { too_short .m.file entryconfigure [mc Schreiben] -state disabled return } set tag [read $fd] close $fd binary scan $tag A3 id if {[string equal $id TAG]} { set hastag 1 set genre 12 binary scan $tag A3A30A30A30A4A28ccc id title artist album year comment zero track genre } else { set hastag 0 set title "" set artist "" set album "" set year "" set comment "" set track 0 set genre 12 } .f3.lbgen selection clear 0 end .f3.lbgen selection set $genre .f3.lbgen see $genre } # # Choose a file to read # proc rd_file {} { global fnam initial_dir set ftypes {{MP3 .mp3} {All *}} set fnam [tk_getOpenFile -filetypes $ftypes -defaultextension .mp3 \ -initialdir $initial_dir] if {[string length $fnam]} { set initial_dir [file dirname $fnam] .m.file entryconfigure [mc Schreiben] -state normal rd_tag } else { .m.file entryconfigure [mc Schreiben] -state disabled } } # # Write (back) tag to file # proc wr_file {} { global title artist album year comment track hastag fnam set genre [.f3.lbgen curselection] set tag [binary format a3a30a30a30a4a28ccc TAG $title $artist $album $year $comment 0 [string trimleft $track 0] $genre] set fd [open $fnam a] fconfigure $fd -encoding binary -translation binary if {$hastag} { seek $fd -128 end } puts -nonewline $fd $tag close $fd } # # seek fails, file too short # proc too_short {} { global fnam tk_messageBox -type ok -icon warning \ -message "[mc \"Die Datei\"] $fnam [mc \"ist keine MP3-Datei.\"]" } # # Message catalogs: # # Deutsch mcset de Lesen mcset de Schreiben mcset de Datei mcset de Ende mcset de Titel mcset de Interpret mcset de Album mcset de Kommentar mcset de Jahr mcset de Track mcset de Genre mcset de "Die Datei" mcset de "ist keine MP3-Datei." # English mcset en Lesen Read mcset en Schreiben Write mcset en Datei File mcset en Ende Exit mcset en Titel Title mcset en Interpret Artist mcset en Album mcset en Kommentar Comment mcset en Jahr Year mcset en Track mcset en Genre mcset en "Die Datei" "The file" mcset en "ist keine MP3-Datei." "is not an MP3 file." # Add your preferred language here # # Build GUI # menu .m -type menubar . configure -menu .m .m add cascade -label [mc Datei] -menu .m.file menu .m.file -tearoff 0 .m.file add command -label [mc Lesen] -command rd_file .m.file add command -label [mc Schreiben] -command wr_file .m.file add separator .m.file add command -label [mc Ende] -command {destroy .} frame .f1 frame .f2 frame .f3 label .f1.lfil -text [mc Datei]: entry .f1.efil -textvariable fnam -width 30 label .f1.ltit -text [mc Titel]: entry .f1.etit -textvariable title -width 30 label .f1.lart -text [mc Interpret]: entry .f1.eart -textvariable artist -width 30 label .f1.lalb -text [mc Album]: entry .f1.ealb -textvariable album -width 30 label .f1.lcom -text [mc Kommentar]: entry .f1.ecom -textvariable comment -width 30 label .f2.lyea -text [mc Jahr]: entry .f2.eyea -textvariable year -width 4 label .f2.ltrk -text [mc Track]: entry .f2.etrk -textvariable track -width 2 label .f3.lgen -text [mc Genre]: listbox .f3.lbgen -listvar lgenre -width 40 -height 5 \ -yscrollcommand {.f3.sbgen set} \ -exportselection 0 scrollbar .f3.sbgen -orient vert -command {.f3.lbgen yview} pack .f1 .f2 .f3 -padx 1m -pady 1m grid .f1.lfil -row 0 -column 0 -sticky e grid .f1.efil -row 0 -column 1 grid .f1.ltit -row 1 -column 0 -sticky e grid .f1.etit -row 1 -column 1 grid .f1.lart -row 2 -column 0 -sticky e grid .f1.eart -row 2 -column 1 grid .f1.lalb -row 3 -column 0 -sticky e grid .f1.ealb -row 3 -column 1 grid .f1.lcom -row 4 -column 0 -sticky e grid .f1.ecom -row 4 -column 1 pack .f2.lyea .f2.eyea .f2.ltrk .f2.etrk -side left pack .f3.lgen pack .f3.lbgen .f3.sbgen -side left -fill y .m.file entryconfigure [mc Schreiben] -state disabled # # Some variables # set hastag 0 set fnam "" set lgenre { Blues {Classic Rock} Country Dance Disco Funk Grunge Hip-Hop Jazz Metal {New Age} Oldies Other Pop R&B Rap Reggae Rock Techno Industrial Alternative Ska {Death Metal} Pranks Soundtrack Euro-Techno Ambient Trip-Hop Vocal Jazz+Funk Fusion Trance Classical Instrumental Acid House Game {Sound Clip} Gospel Noise AlternRock Bass Soul Punk Space Meditative {Instrumental Pop} {Instrumental Rock} Ethnic Gothic Darkwave Techno-Industrial Electronic Pop-Folk Eurodance Dream {Southern Rock} Comedy Cult Gangsta {Top 40} {Christian Rap} Pop/Funk Jungle {Native American} Cabaret {New Wave} Psychadelic Rave Showtunes Trailer Lo-Fi Tribal {Acid Punk} {Acid Jazz} Polka Retro Musical {Rock & Roll} {Hard Rock} Folk Folk-Rock {National Folk} Swing {Fast Fusion} Bebob Latin Revival Celtic Bluegrass Avantgarde {Gothic Rock} {Progressive Rock} {Psychedelic Rock} {Symphonic Rock} {Slow Rock} {Big Band} Chorus {Easy Listening} Acoustic Humour Speech Chanson Opera {Chamber Music} Sonata Symphony {Booty Brass} Primus {Porn Groove} Satire {Slow Jam} Club Tango Samba Folklore Ballad {Power Ballad} {Rhytmic Soul} Freestyle Duet {Punk Rock} {Drum Solo} {A Capela} Euro-House {Dance Hall} } # # Set defaults # .f3.lbgen selection clear 0 end .f3.lbgen selection set 12 .f3.lbgen see 12
cf Cool application. One modification to the write procedure:
# Write (back) tag to file # proc wr_file {} { global title artist album year comment track hastag fnam set genre [.f3.lbgen curselection] scan $track %d track set tag [binary format a3a30a30a30a4a28ccc TAG $title $artist $album $year $comment 0 $track $genre] set fd [open $fnam a] fconfigure $fd -encoding binary -translation binary if {$hastag} { seek $fd -128 end } puts -nonewline $fd $tag close $fd }
The trimleft trick returns an empty string when the track number is 0, while the scan function works regardless.
If you need IDV2 read/edit download snackAmp souces from sourceforce and take id3.tcl file which implements both IDV1 and IDV2 read/write. It is not complete and you will need to comment some lines that are snackAmp related like "Trace" and "Update" but it is not so difficult. also some tk_message should be removed. remove the upvar connection to snackAmpSettings and add setting to ID3ReadOnly,preserveTime,preserveV2data and you are done.I also added proper mp3 jpeg image add/change support. To add it modify/add this lines:modify line258 - variable v2_3_IDs [list "COMM" "TALB" "TCOM" "TCON" "TENC" "TIT2" "TMED" "TPE1" "TRCK" "TSST" "TYER" "APIC"]add line 672 - "APIC" {set data(Image) [string range $String 12 end]]}add line 828 - "Image" {append Tag "APIC" [tagLen [expr {$slen +8}]] $flags $uenc "image/jpg" \0\3\0 $String} ;# lang is ENG (for now)and you can get/set images. this method can also be done (with some changes according to encoding) to other mp3 properties you want to support.I hope there is a way to make this part of tcllib or other public easily accessible library.