Nintendo 64 console with EverDrive cartridge

Nintendo 64 Part 14B: Streaming Music

, Nintendo 64, Programming

Streaming ADPCM

Short update: I’ve encoded a short audio track in the Nintendo 64’s ADPCM format and streamed it from disk.

Here’s the resulting ROM image. For some reason, it seems to play more smoothly on real hardware. The music sounds off when played through an emulator, like it’s being played through a record player with a dying motor.

Audio Demo rev 150 624 kB

I dug around on my computer and found some music that I had created, converted it to FLAC, and put it in the repo.

Step 1: Convert to 16-bit, monophonic PCM in an AIFF-C file. SoX is my preferred tool for manipulating audio for tasks like these.

rate := 32000
track01.aifc: track01.flac
    flac --silent --decode $< --output-name track01.tmp.wav
    sox track01.temp.wav --bits 16 --rate $(rate) --channels 1 $@

Step 2: Create a codebook and encode as ADPCM.

track01.adpcm.aifc: track01.aifc
    tabledesign $< >track01.codebook.txt
    vadpcm_enc -c track01.codebook.txt $< $@

Step 3: Extract data from AIFF-C file and embed it in ROM image.

I chose to add the audio track as two files: one file contains the codebook and the other file contains the ADPCM data itself. The codebook itself is found inside an APPL (application-specific data) chunk in the output of vadpcm_enc. The chunk format is simple:

OffsetSizeDescription
04Chunk ID, APPL
44Chunk length, not including ID and length
84Application signature, stoc
1212Data type name, 1-byte length (11) followed by VADPCMCODES
242Version, 1
262Prediction order (by default, 2)
282Number of entries (by default, 4)
30variable16-bit table, containing order × num entries × 8 elements

Since order is 2 by default and the number of entries is 4, there are by default 64 elements in the table, each a 16-bit integer.

The above codebook has to be converted to an ALADPCMBook structure to be played by the LibUltra audio libraries. This structure is only slightly different from the codebook structure in the AIFF-C file.

typedef struct {
  s32 order;
  s32 npredictors;
  s16 book[1];
} ALADPCMBook;

The sample data is available in the AIFF-C file in the SSND chunk, which also contains 8 bytes of extra fields at the beginning which must be stripped out.

Data Rates

I would love to have a decent amount of music in my game, so I made a comparison of the different formats I could use and how much music each one would allow me to fit on the cartridge:

FormatBit rate (kbit/s)Length (s/MiB)
16-bit PCM @ 32 kHz51016
16-bit PCM @ 22.05 kHz35024
4-bit ADPCM @ 32 kHz13066
4-bit ADPCM @ 22.05 kHz8895
MP3 or Vorbis64130

You can see that 4-bit ADPCM at 22.05 kHz approaches the bit rates that you would see for codecs like MP3 or Vorbis. Given that MP3 or Vorbis at 64 kbit/s would only let me add 40% more music, I think I’ll just stick with ADPCM. From this experiment, I’m satisfied with the quality and the compression ratios of ADPCM, and I don’t see the need to try anything more advanced.