I am building an MP3 jukebox for my home...
I know that I am supposed to use ID3v2, but my MP3 collection (CD ripped, Amazon and Emusic) still sports ID3v1 tags, so I thought it would be a safe bet to just parse it.
I quickly wrote an ID3v1 tag parser in Perl (yes, I know CPAN has several solutions for this but I wanted to write my own just for the fun). Here is what it looks like:
Basically it takes a stream of MP3 filenames over stdin, opens them and dumps out a pipe delimited summary of what it found. Here is how it is run:
I am considering using Haskell for my jukebox, so I was curious what this would look like in Haskell. Here is my newbie Haskell implementation:
import Text.Printf
import Data.Array
import Char
import System.Environment
import System.IO
-- Create a array of genres
genres = listArray (0, l-1) genres_l
where
genres_l = [
"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","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 &","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",
"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" ]
l = length genres_l
main = do
hSetEncoding stdin latin1
hSetEncoding stdout latin1
fname <- getContents -- lazily read list of files from stdin
mapM print_id3v1 (lines fname)
print_id3v1 fname = do
print fname
inh <- openBinaryFile fname ReadMode
hSeek inh SeekFromEnd (-128)
dat <- hGetContents inh
printf "%s|%s|%s|%s|%s|%d|%s\n"
fname
(extract 3 30 dat) -- Title
(extract 33 30 dat) -- Artist
(extract 63 30 dat) -- Album
(extract 93 4 dat) -- Year
(Char.ord (head (extract 126 1 dat))) -- Track
(genres !(Char.ord (head (extract 127 1 dat)))) -- Genre
hClose inh
-- extract and trim a range of elements from list
extract idx ln s =
trim0 (take ln (drop idx s))
-- Trim nulls from list
trim0 s = filter (/= '\0') s
You run it similarly. Frustratingly, it isn't very happy with filenames with non-ASCII characters :-(
I am not a Haskell expert, but I didn't expect it to choke there...
EDIT: Fixed the filename problem by adding:
hSetEncoding stdin latin1
hSetEncoding stdout latin1