Weird Looking: creating mp3 cds from itunes playlists

creating mp3 cds from itunes playlists

January 1, 2006 10:19pm (6 years, 1 month and 5 days ago)
As you may know, I’ve been thinking about MP3 CDs entirely too much lately.  I quickly realized there was a problem.  My music is currently formatted on the computer as such:
  My Music\artist\album\song.mp3

This has worked fine so far because I can create playlists that group music independent of where it lives in the filesystem.  iTunes can create a flat MP3 CD of a playlist, but that’s not ideal.  What I’d like is to have a directory for each playlist on the CD, with the files from that playlist in them.

Luckily, iTunes keeps your playlists in an XML file.  I hacked up something in C# to parse the XML and enumerate my playlists.  Then I mucked around all afternoon trying to figure out the best way to tell the CD burner what to do with that list.

After toying with mkisofs and XP’s IMAPI CD burning interface, I figured out I can just copy the files to XP’s CD staging area.  So I rewrote my program to create directories for each playlist in the staging area, then copy the files from those playlists into the directories.  Once that’s done, XP’s wizard takes care of the burning.  Tada!

This is the first thing I’ve written in C# beyond “Hello World!”, so if you look at it, please be gentle :P

CDSetup.cs

Click to enlargen

Comments

Jan 2, 2006 9:11am
In answer to your in-code question, the managed equivalent to SHGetFolderPath is System.Environment.GetFolderPath.  However, that method takes a System.SpecialFolder enumeration argument, and while there is a SpecialFolder.MyMusic value, I do not see a value for the CD Burning folder.

You could always try using a cast Environment.GetFolderPath((SpecialFolder)CSIDL_CDBURN_AREA), but if the method checks the argument for a valid value before calling the system it will likely throw an ArgumentException.
Jan 2, 2006 11:30am
I’ve changed the code that looks for the My Music directory.  I tried casting 59 to an Environment.SpecialFolder, and it did throw an ArgumentException, “Illegal enum value 59”.
Jan 2, 2006 3:30pm
After looking at the decompiled source to that method in Reflector, it seems that the only differences between calling the Windows function directly and using the managed method are security and portability.  Using P/Invoke to call SHGetFolderPath requires that you run your code at full-trust, which basically means you cannot run it off a network drive or on most shared ASP.NET servers.

The managed method just checks the enum argument, calls the Windows function while suppressing unmanaged code security demands and then demands a path-discovery permission for the specific path you are looking up.  So technically, if you add a SuppressUnmanagedCodeSecurity attribute to your external function prototype then the direct call will probably be a tiny iota faster.

However, it’s certain not to work on Mono running on non-Windows machines, which I am sure is not a big deal since the rest of the program is pretty Windows-specific.  Still, I cringe every time I have to go outside the framework because I know it will make the program that much harder to port when I eventually get around to it.
Jan 2, 2006 7:51pm
Yeah, no huge portability concerns since it’ll probably see the outside of my computer.  I imagine the same thing could be done with XMMS in Linux and iTunes on the Mac, though.

Actually I think this could be done much more easily with AppleScript on the Mac, since iTunes exports some sort of interface to its playlists.

Obviously the most portable thing would be for it to generate .iso files.  So someone write that for me and I’ll stick it in :D
Jan 6, 2006 12:33am
I was just joking about outputting .iso images.  But after reading the specs, I think there may be a valid reason to do so.  Say I have a file in two directories:
  \COUNTRY_MUSIC\RING_OF_FIRE.MP3
  \JOHNNY_CASH\RING_OF_FIRE.MP3
All of the tools I’ve looked at will make two copies of that file.  But with the filesystem on a CD, it’s trivial to point more than one file name at a block of data.  So I’ve started on some more bad C# to output an ISO 9660 file system.

Why am I spending so much time on this?  Partly because I’m bored, partly because I need practice with C#, and partly because it may turn out useful (at least for myself).
Jan 6, 2006 10:33am
Sweet merciful Jebus!  I never thought about it before, but symlinks would not be a big deal on read-only media, even if the filesystem does not have direct support for them.  I suppose that the old Hybrid discs (with PC and Mac filesystems/data) would do this to share common data.  I’m willing to bet that the “You Don’t Know Jack” discs do this.

Anyway, this is turning out to be a cool program.  Is there any way it could automatically come up with 11-character filenames, since a lot of DVD players and car MP3 players only display 11 characters.  I have my music with paths like "They Might Be Giants\[1990-01-16] Flood\[04] Istanbul (Not Constantinople).mp3“, and I’d like it to be something like "TMBG\Flood\Istanbul.mp3“.

Even if it doesn’t always come up with an ideal name, anything would be better than "They Might \[1990-01-16]\[04] Istanb“, which is what shows up on my DVD player.
Jan 6, 2006 1:26pm
11?  I guess that’s just an arbitrary length, since 8.3 filenames wouldn’t even fit.  Technically, you can’t even have characters like space [ ] or - in an iso9660 filename.  I guess everyone ignores that now.  I’ll probably just make a setting for whether or not filenames are normalized to iso9660 specs.

I’m not sure what sort of heuristic you could apply to get decent shorter file names automatically, except maybe splitting the filename into word sections and then picking the longest one.  Maybe use an acronym if none of the words are interestingly long.

As for non-automatic file names, I don’t see how letting the user map files to a certain file name on the CD would be any different than what I’m doing now.  Except that it’d become beneficial to have some sort of editor.
Jan 6, 2006 10:20pm
Forget the editor.

I’m thinking of storing a short name in a custom field in the metadata.  So if you wanted to read the ID3v2 tag (not an easy task, but certainly not impossible), you could get the filename out of there.  I believe there’s an “Original Filename” field that I’d be willing to use for this purpose.  Otherwise, the custom fields would be fine.

Otherwise, automatic filename generation should probably come out of the metadata anyway, as parsing meaningful information out of filenames is not always feasible.

I’m also wondering how difficult it would be to support DVDs as well as CDs?  I’m not sure if you can just use ISO9660 on DVDs.  It might work with some players, but I’m sure it’s not a good idea.  I believe that you are supposed to use UDF, and I have no idea how complicated that is.
Jan 7, 2006 1:09am
Talk about feature creep…

But you’re right.  Renaldo.  Storing information in the MP3’s meta data would be better.  ID3v2 kind of scares me, but I’ll take it on after the iso9660 stuff is done.

I don’t know much about DVDs, mainly because my DVD burner broke relatively quickly and I haven’t found a compelling enough reason to go buy a new one.
Jan 7, 2006 10:30am
id3.org has a link to this closed-source ID3 library for .NET.  I’ve never used it, but it seems to be more complete than many of the others I’ve come across.  It’s a shame it’s not open source.

As for writing your own ID3v2 code, I’ve actually looked into it a few times before, and it’s not that bad.  Reading the tags is much easier than writing them, except the new spec allows for tags at the beginning or the end of the file (or possibly both, I think), which complicates things a bit.  The only other major problem is reading sync-safe integers, and that’s not a big deal in a language that supports all the bitwise operators.
Jan 7, 2006 1:04pm
It’s been a while since I read the specs, but I think what scared me was:
A) finding the tags.  Requires not only parsing id3, but also mp3 to some degree.  The probable quick, dirty, 99% of the time solution is to search for “TAG”, then see if what comes after it is a sane ID3 header.
and B) There can be junk space or application-specific data in some places (e.g. between structures), which means following the specs to the letter if you want to parse every possible tag.
Jan 7, 2006 7:28pm
I received some compilation errors while using Mono 1.1.

CDSetup.cs(5,7): error CS0234: The type or namespace name `Drawing' does not exist in the namespace `System’. Are you missing an assembly reference?
CDSetup.cs(5,1): error CS0246: The type or namespace name `System.Drawing' could not be found. Are you missing a using directive or an assembly reference?
Try using -r:System.Drawing
CDSetup.cs(6,7): error CS0234: The type or namespace name `Windows' does not exist in the namespace `System’. Are you missing an assembly reference?
CDSetup.cs(6,1): error CS0246: The type or namespace name `System.Windows.Forms' could not be found. Are you missing a using directive or an assembly reference?
Try using -r:System.Windows.Forms
Compilation failed: 4 error(s), 0 warnings

Any ideas?
Jan 7, 2006 7:36pm
Cole, I don’t believe that System.Windows.Forms is a part of the core mono runtime.  It may be once it’s more feature-complete, but that’s still a little way off.  As for System.Drawing, I’m not sure.  I thought that it was built on top of Cairo, but it may not be bundled with Mono.

But don’t expect GUI applications to run on both .NET and Mono unless they’re written with GTK#, and then you have to install some libraries for the program to run on .NET.

It will get better, though.
Jan 7, 2006 7:41pm
The ID3v2 specification is not different than any variable-length record-based storage.  Each chunk has it’s associated length stored in a header, so if you don’t understand that chunk, you ignore it and locate the next one using the given length.

As for finding the tag, I’m pretty sure you search from the beginning until you find a certain pattern, and then (if you are using the new version) you search from the end.  Tags were initially put at the beginning so that streaming MP3s could display the metadata, but that makes it hard to add data later (hence the padding used).
Jan 7, 2006 9:35pm
It won’t run in Mono, but it should compile.  You have to tell the compiler which assemblies to include though.

mcs ./CDSetup.cs -target:exe -out:CDSetup.exe -main:CDSetup -r:System.Windows.Forms -r:System.Drawing -r:System.Data -r:System.Web
Jan 7, 2006 11:21pm
Thanks guys. I’ll try it again with your suggestions.

Leave a comment


Accepts BBCode with a few enhancements.