Nintendo 64 Part 14B: Streaming Music
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.
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:
Offset | Size | Description |
---|---|---|
0 | 4 | Chunk ID, APPL |
4 | 4 | Chunk length, not including ID and length |
8 | 4 | Application signature, stoc |
12 | 12 | Data type name, 1-byte length (11) followed by
VADPCMCODES |
24 | 2 | Version, 1 |
26 | 2 | Prediction order (by default, 2) |
28 | 2 | Number of entries (by default, 4) |
30 | variable | 16-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:
Format | Bit rate (kbit/s) | Length (s/MiB) |
---|---|---|
16-bit PCM @ 32 kHz | 510 | 16 |
16-bit PCM @ 22.05 kHz | 350 | 24 |
4-bit ADPCM @ 32 kHz | 130 | 66 |
4-bit ADPCM @ 22.05 kHz | 88 | 95 |
MP3 or Vorbis | 64 | 130 |
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.