Wednesday, May 18, 2011

Transcoding MP3 files for Nintendo DSi

My son's Nintendo DSi is a very cool little toy, but could they have made it harder to put music on it? After copying his MP3 files over to an SD card and putting it in, I thought I was done. But clearly Nintendo has other ideas.

Some basic googling (for instance this Nintendo Tech Support page) uncovered that the files must be in AAC format, and furthermore they must have a .m4a file extension. So some transcoding was in order, but none of the methods I was finding online were helping. I tried using Transmageddon, which is an all-purpose transcoding program, but it didn't seem to run properly (even though I installed it from the standard Ubuntu packages). I tried using ffmpeg based on a few pages recommendations, but this didn't give me any love, either.

I decided to do what I should have done in the first place and just chain together the simple Linux tools I had to do the job I needed. This article on Taco Bell Programing describes it well, but one of the great things about using Linux is that you have a bunch of simple tools that are designed to be chained together to process data.

In this case, I have two primary tools that are useful:
  • lame - an MP3 encoder and decoder, which will take the MP3 file and decode it to a "raw" wave file.
  • faac - an AAC encoder, which will take the wave file and convert it to AAC format
To use them together, I pipe the output from one into the input of the other like this:
lame --decode "my file.mp3" - | faac - -o "my file.m4a"
and voilá—I have a DSi-friendly sound file!

So that was one file, but I need to transcode every MP3 file in a directory. And I want to take advantage of my fancy quad-core laptop. So I need to add a few more tools:
  • find - list files. Searches for files. All of our files are in a single directory, so I don't need all of the finding power, but find gives me a few more options that work well with the next tool, xargs
  • xargs - takes a list of items (such as the results of a find operation) and uses them in to supply arguments to multiple copies of the same command. It can also split the work over multiple processors, which can speed things up on a multiprocessor machine.
The easiest way to combine all of this is with a few scripts. First I make a script to transcode a single file called

lame --decode "${1}" - | faac - -o "${1%mp3}m4a"

Just a quick rundown on what this script does:
  • lame - invoke the lame encoder/decoder
  • --decode - tells lame to convert from MP3 to the raw wave format
  • "${1}" - the ${1} will be replaced with the first argument given to the script—presumably the name of our file. The quotes ensure that it can handle filenames with spaces in them, which is common with MP3 files.
  • The dash - this tells lame that the output should be sent to standard out instead of to a file.
  • The pipe character (|) - this tells Linux to take the output from lame and send it to the next command.
  • faac - the AAC encoder
  • The dash - this tells faac that the input should come from standard in instead of from a file.
  • -o indicates that the output filename will follow
  • "${1%mp3}m4a" - like the earlier ${1}, the script argument is replaced here, but the %mp3 tells it to ignore that part of the filename (so file.mp3 will just show up as file). The m4a is tacked on the end to make it file.m4a.
Next I make a script to find the files and pass them to the first script called

find *.mp3 -print0 | xargs -0 -P4 -n1 -I{} ./ "{}"

This one has a little more going on:
  • find - like the ls command, this returns lists of files
  • *.mp3 - this is the pattern to search for. Since I don't specify any other criteria like a directory, just the local directory will be searched.
  • -print0 - this is a little arcane, but it causes each filename found to be followed by a null character. It is designed to be used with the xargs command's -0 argument so that filenames with spaces in them can be used.
  • The pipe character (|) - see above
  • xargs - the command
  • -0 - tells xargs to use the null character for a terminator between items. Works with the find command's -print0 argument and allows filenames with spaces in them to be used.
  • -P4 - divide the work among 4 processes to take advantage of the 4 processors I have.
  • -n1 - use a single filename for each command. Some commands that are used with xargs (such as the rm command for deleting files) can take multiple arguments at once, but in this case I want each argument (a filename) to be used for a single command.
  • -I{} - tells xargs that wherever it sees the curly braces to replace it with the argument (the filename)
  • ./ - call our other script
  • "{}" - pass the filename as an argument to our other script. Put it in quotes to ensure that filenames with spaces are seen as a single argument.
I launched the script and watched as the quad-core laptop heated up to the temperature of the sun and the files were transcoded. The newly created .m4a files were copied to an SD card and were read properly by the DSi.

No comments:

Post a Comment