MikeyP.com MikeyP.com

Patching PACPL for Album Art and WMA Lossless Support

PACPL, the Perl Audio Converter, is a great Perl script that will batch transcode audio files from one format to another.  For a variety of reasons, my music library is in the WMA Lossless format, and I wanted to use PACPL to transcode the WMA files to MP3.  By default, PACPL does not support transcoding of WMA Lossless files, so I patched the script to add the support.  While I was under the hood, I added support for the “Composer” and “Album Artist” meta tags for WMA decoding and MP3 encoding.  The Album Artist tag helps media players like iTunes group songs by album, even if there are different performers. I also added support for embedding album art into MP3 files.

PACPL normally uses ffmpeg to decode WMA files, but ffmpeg does not support WMA Lossless.  With the patch, PACPL has the option to use mplayer to decode WMA files.  This works fine, with one exception.  Some versions of mplayer (like the one I use: 1.0rc1-4.1.2) have a bug that prevents it from properly creating files with commas in the filename or path.  I added options to remove commas from decoded filenames and to use the /tmp directory for decoding.  Edit the script and set both options to 1 if you have problems with mplayer.

# decoding options for temp files
# use /tmp/ directory for temporary decoded .wav files
my $decode_usetmp = 0;
# replace commas in decode filename to work around mplayer bug
my $decode_replacecommas = 0;

For embedded album art, I added a command line option --albumart= to specify the source album art JPG file name.  It expects a JPG file that is in the same directory as the source audio files.  If it finds the JPG, it will embed the image into the MP3 header.  I did not add album art support to any other output formats, though it should be easy enough to do if their Perl modules support it.

I ended up learning more about the MP3 ID3 metadata format than I intended.  What a mess. Fortunately, the MP3::Tag module is pretty robust.  That said, I did find a bug. The test file I used was a Stevie Ray Vaughan song, which has the genre “Blues.”  This is the first genre in the ID3 genre list, and has the ID of 0.  For the life of me, I couldn’t figure out why the script was losing the genre tag until I realized that a genre ID of 0 was getting skipped by MP3::Tag.  I added a workaround to change the Blues genre name to be “Blues & Roots”, which forces the genre to be encoded as a string instead of ID number.

I also found that different media players treat album art in different ways.  That is, some players are very forgiving of bad metadata headers, while others are not. I tested album art in iTunes, Foobar 2000, Windows Media Player, VLC, Winamp, and Amarok.  For reference, here’s the magic header for JPG album art that works with all of the tested media players:

# important to set unsync to false, otherwise ID3v2 data can appear "corrupt"

$tag_m = MP3::Tag->new("$out_file");

# ID3v2 Tags
unless(exists($tag_m->{ID3v2})) { $tag_m->new_tag("ID3v2"); }

$tag_m->{ID3v2}->add_frame("APIC", "\x00", "image/jpeg", "\x03", "Cover (front)", $imagedata);

With my patches, here’s the shell command I use for transcoding a directory tree of WMA Lossless files to high-bitrate mp3 files:

pacpl -v -t mp3 -r -p -o wma --albumart="Folder.jpg" --defopts 0 --eopts="-q 0 -v -V 0 -b 320 -F" --decoder mplayer /music/WindowsMedia/ --outdir /music/mp3/

works-on-my-machine-starburst_3Unfortunately, I stink at Perl, so a lot of my Perl code looks like C.  I tested the patches on my personal WMA Lossless collection on two different installs of ZenWalk Linux.  Therefore, I hereby grant this code the honorary Scott Hanselman badge of “Works on My Machine.”

You can download the patched Perl script here: pacpl-wmal-patched.tar.gz

I’ll send the patches up to the maintainer of PACPL, but I’m not sure if he’s interested (or if he’ll recoil in horror at my code).  I’ll add a comment if I hear back from him.


UPDATE: Turns out that I accidentally switched the Artist and Album Artist metadata tags, which is critical for iTunes. I fixed it and re-uploaded the tarball.

Read more stories like this one...