citizen428.blog()

Try to learn something about everything

Erlang Bit Syntax and ID3

A couple of days ago I finally started properly looking at Erlang for the first time. One aspect I find especially interesting is the bit syntax, so I wrote a small program for parsing ID3v1 tags for practice. There’s definitely room for improvement (I ignored ID3v1.1), but it was a fun little exercise. Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-module(mp3).
-export([get_id3/1, get_tags/2]).

get_id3(File) ->
    case file:open(File, [read, binary]) of
        {ok, MP3} ->
            Result = case file:pread(MP3, {eof, -128}, 128) of
                {eof} -> eof;
                {error, Reason} -> Reason;
                {ok, <<"TAG", Tags/binary>>} -> parse_id3(Tags);
                {ok, _} -> no_id3
            end,
            file:close(MP3),
            Result;
        {error, Reason} -> Reason
    end.

get_tags(Tags, L) ->
    lists:map(fun (Tag) -> proplists:get_value(Tag, L) end, Tags).

parse_id3(<<T:30/binary,Ar:30/binary,Al:30/binary,Y:4/binary,C:30/binary,G:1/binary>>) ->
    Clean = lists:map(fun cleanup/1, [T, Ar, Al, Y, C, G]),
    {id3v1, lists:zip([title, artist, album, year, comment, genre], Clean)}.

cleanup(T) ->
    lists:filter(fun(X) -> X =/= 0 end, binary_to_list(T)).

Lets see this in action in the Erlang shell (the MP3 comes from a similar exercise in RubyLearning’s core Ruby course):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
147> % file doesn’t exist
147> mp3:get_id3(./test.txt).
enoent
148> % file is not an MP3
148> mp3:get_id3(./test.clj).
no_id3
150> % get the tags
150> {id3v1, Tags} = mp3:get_id3(song.mp3).
{id3v1,[{title,Dancing Shoes},
 {artist,Cliff Richard and The Shadows},
 {album,(SUMMER HOLIDAY 1963)},
 {year,2000},
 {comment,Rubylearningr},
 {genre,[24]}]}

I’m too new to Erlang to judge if this is a proper use of a property list, but it allowed me to write get_tags/2 as a wrapper for proplists:get_value/2 which is rather nice:

1
2
3
4
151> mp3:get_tags([artist], Tags).
[Cliff Richard and The Shadows]
152> mp3:get_tags([artist, year], Tags).
[Cliff Richard and The Shadows,2000]

Some initial help came from this related blog post, but I think our versions came out quite differently in the end.

All in all Erlang feels quite nice, except for minor syntactic quirks like different statement modifiers depending on context or the need to “extract” a local function with fun for the call in lists:map/2. Any feedback would be much appreciated, I’m sure there’s plenty of things I could have done better.

Comments