[QUIZ][SOLUTION] ID3 Tags (#136)

$genres = ["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 Bass","Primus","Porn Groove","Satire","Slow Jam",
  "Club","Tango","Samba","Folklore","Ballad","Power Ballad",
  "Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo",
  "A capella","Euro-House","Dance Hall"]

file = File.new(ARGV[0].chomp)

tag, song, artist, album, year, comment, genre =
  file.read[-128..-1].unpack("A3A30A30A30A4A30C")

file.close

track = nil

genre = $genres[genre.to_i]

if comment[28] == 0
  track = comment[29]
  comment = comment[0..27]
end

if tag == "TAG"
  [:song, :artist, :album, :track, :year, :comment, :genre].each do |sym|
    puts sym.to_s.capitalize + ": " + eval(sym.id2name).to_s
  end
else
  puts "Did not find ID3v1 tags."
end

···

----- Original Message ----
From: Ruby Quiz <james@grayproductions.net>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
Sent: Friday, August 24, 2007 7:34:47 AM
Subject: [QUIZ] ID3 Tags (#136)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The MP3 file format, didn't provide any means for including metadata about the
song. ID3 tags were invented to solve this problem.

You can tell if an MP3 file includes ID3 tags by examining the last 128 bytes of
the file. If they begin with the characters TAG, you have found an ID3 tag.
The format of the tag is as follows:

    TAG song album artist comment year genre

The spaces above are just for us humans. The actual tags are fixed-width fields
with no spacing between them. Song, album, artist, and comment are 30 bytes
each. The year is four bytes and the genre just gets one, which is an index
into a list of predefined genres I'll include at the end of this quiz.

A minor change was later made to ID3 tags to allow them to include track
numbers, creating ID3v1.1. In that format, if the 29th byte of a comment is
null and the 30th is not, the 30th byte is an integer representing the track
number.

Later changes evolved ID3v2 which is a scary beast we won't worry about.

This week's Ruby Quiz is to write an ID3 tag parser. Using a library is
cheating. Roll up your sleeves and parse it yourself. It's not hard at all.

If you don't have MP3 files to test your solution on, you can find some free
files at:

    http://www.mfiles.co.uk/mp3-files.htm

Here's the official genre list with some extensions added by Winamp:

    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 Bass
    Primus
    Porn Groove
    Satire
    Slow Jam
    Club
    Tango
    Samba
    Folklore
    Ballad
    Power Ballad
    Rhythmic Soul
    Freestyle
    Duet
    Punk Rock
    Drum Solo
    A capella
    Euro-House
    Dance Hall

____________________________________________________________________________________
Sick sense of humor? Visit Yahoo! TV's
Comedy with an Edge to see what's on, when.
http://tv.yahoo.com/collections/222

def fileTail (file, offset)
  f=File.new(file)
  f.seek(-offset,IO::SEEK_END)
  f.read
end

class ID3Tag
  GENRES=["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 Bass","Primus","Porn Groove","Satire","Slow
Jam","Club","Tango",
          "Samba","Folklore","Ballad","Power Ballad","Rhythmic
Soul","Freestyle",
          "Duet","Punk Rock","Drum Solo","A capella","Euro-House","Dance
Hall"]
  attr_reader :title, :artist, :album, :year, :comment, :genre, :track
  def initialize fname
    tag,@title,@artist,@album,@year,@comment,@genre=fileTail(fname,128).unpack
"A3A30A30A30A4A30C"
    raise "No ID3 Info" if tag!='TAG'
    s_com,flag,track=@comment.unpack "A28CC"
    if flag==0 and track!=0
      @comment=s_com
      @track=track
    end
    @genre=GENRES[@genre]
    @genre="Unknown" if !@genre
  end
end

p ID3Tag.new(ARGV[0])