Sound can make or break a video game’s success. If you don’t have a memorable melody in the soundtrack, you lose a hook to convince people to buy a sequel. If you don’t have good sound design, you’ll find it more difficult to immerse yourself in the world that’s been created.
If you don’t have sound at all… you’re likely dealing with the initial release of an emulator.
After v0.1, I fixed some small bugs here and there, but I soon decided to attempt (what seemed like) a more complex task: finally adding sound to the emulator. And it works, somewhat. If you want to test it yourself, you can clone the v0.2 branch on GitHub and compile it using qmake.
But how does the DS sound processing unit (SPU) work?
The SPU has sixteen channels and two sound capture units. The channels can all play sound in PCM8, PCM16, and IMA-ADPCM; the first two are raw sound data, and the last one is compressed PCM16, similar to what you’d find in a .wav file. Channels 8-13 can also play PSG (square waves, like on the Game Boy), and channels 14-15 can play white noise. The SPU runs on its own clock at a frequency of ~16 MHz, which is just half the ARM7’s clock rate. To load music into a channel, you input an address for memory and a frequency… and you’re done. The channels are more configurable than that, of course, but it really is that simple. The capture units just take samples from channels 0-3 and put them in RAM, where the CPU can perform fancy sound effects before re-outputting them.
Implementing all of the above wasn’t that bad. I had some difficulties getting ADPCM to work, but all of the issues were resolved in a matter of hours. The hardest part was making things compatible for the Qt frontend. On Windows and Linux, Qt sound automatically shuts itself off when it detects no sound data. Because the SPU would not output sound upon starting a game, well… there wouldn’t be any sound. I resolved this by creating an intermediate buffer that stores old sound data, which is outputted if the SPU doesn’t have anything. It doesn’t sound great, but it gets the job done for now.
If you’re familiar with the GBA, you’ll see the DS SPU is a vast improvement over it. If not, let’s do a comparison. The GBA has six channels, four of which are just copies of the Game Boy’s sound registers. The other two take PCM8 data, but not automatically. If you want to use both channels, two of the four DMA (Direct Memory Access) units must be reserved for them. Furthermore, you must also reserve two of the four timer units, as the channels do not have their own clock. The GBA CPU is paused during the DMA transfers, so that means you have to make a tradeoff between CPU time and sound quality. The DS suffers from none of these flaws; all you need to do is supply the data, and the SPU works on its own.
The SPU implementation in CorgiDS is incomplete, for the moment. Neither the capture units nor the sound FIFOs are implemented. Sound is synchronous, meaning that if CorgiDS isn’t running full speed, it sounds terrible. Even when running full speed, things sound a bit off. Even so, the sound was good enough that I got a bit sidetracked and played Mario and Luigi: Partners in Time for a couple of hours. 🙂