Music to my Ears
Welcome back to your favourite monthly progress report! NooDS has seen a lot of fun improvements over the past month, so buckle up and I’ll take you through them.
First up, of course, we have audio output. Compared to my previous experience emulating the NES’s audio unit, the DS’s was surprisingly straightforward. The DS has sixteen audio channels, which is eleven more than the NES has, but most of these channels only support playing PCM8, PCM16, or ADPCM data. Some of the channels also support pulse waves or noise, similar to the NES, but with none of the fancy features such as sweeps or envelopes. The DS also has a sound capture functionality that allows copying audio data from the channels into memory. This can be used for software manipulation of the output data, similar to the display capture functionality. The audio unit runs at half the speed of the ARM7 processor, so roughly 16.8MHz. It outputs an audio sample every 512 cycles, so dividing the previous frequency by 512 gives us a sample rate of 32768Hz.
NooDS doesn’t emulate the entirety of what the DS has to offer just yet; it only outputs PCM data, and sound capture isn’t implemented. However, what it has is enough for a large portion of the audio found in DS games to work. During my testing, I found that a lot of games rely almost entirely on ADPCM data. While the PCM8 and PCM16 formats are uncompressed and can more or less be written directly to the audio buffer (after volume and other adjustments are applied), the ADPCM format is compressed and must be, well, decompressed. Of course, GBATEK supplies a handy algorithm to convert ADPCM data to PCM16 data, so while it was certainly more complicated than PCM8 and PCM16, ADPCM support didn’t give me too much trouble.
Aside from the missing functionality, another issue with the current audio implementation is the timing. The way I have it implemented right now is what I suppose you would call asynchronous; the sound unit is running entirely separate from the rest of the emulator, generating samples for the audio buffer as they’re needed. This works well enough, and it even sounds good when running at slower speeds becuase the samples will continue to be generated at the correct speed. However, when running at full speed, you might start to notice some issues. DS games tend to use the built-in CPU timers to decide when a sound should start playing; if the audio isn’t synchronized to the CPU, the current sample could be ahead or behind of what it should be, causing sounds to potentially start playing at slightly incorrect times. I plan on eventually switching to a synchronous solution for accuracy’s sake, but as it is now you should still be able to jam out to your favourite DS soundtracks (with only minor inaccuracies!).
After audio, I got a lot of graphics-related work done as well. The 2D engine got support for blending, allowing for transparency effects with the 2D objects and background layers. The 3D engine got alpha blending, allowing for transparency, as well as culling, allowing for polygons facing away from (or towards, depending on the setting) the screen to be skipped during rendering. The latter can be used for some interesting effects, such as the character outlines in Okamiden.
In non-blending-related news, a few features and fixes were added to allow for proper dual-screen 3D support. The DS can only output 3D to one screen at a time, so some tricks are employed to create the illusion of dual-screen 3D. Each frame, the 3D output alternates between the top and bottom screens. Display capture is used to save a bitmap of the previous frame, which is then displayed on the non-3D screen to make it look like the 3D output is still there. To display the bitmap, one screen uses an extended affine background layer, and the other uses bitmap objects. I haven’t looked into the specifics, but the reason why both screens use different methods is probably because of VRAM constraints. I had to implement display capture and bitmap objects for this, as well as fix a timing issue that was causing the screens to be swapped. With all of this, NooDS is coming very close to being feature-complete in the graphics department!
As I briefly mentioned in the previous post, I’ve been thinking about doing some sort of release. To that end, I also decided to work on some much-needed UI features. I added a settings system, including toggles for direct boot and the FPS limiter, customizable BIOS and firmware file paths, and control remapping. I also added a dialog that pops up when booting a game that has no save file; it asks the user to specify the save type, allowing for a save file to be created and saving to be possible. Previously, games without save files would just not save, because there was no way to detect which save type the game uses. Of course, if the user selects the wrong save type, saving will still be broken. I mentioned this back when I first added save support, but since the save type isn’t stored anywhere in a cartridge dump, the options for determining save type automatically are to either use some sort of database, or write some hacky detection code. Since neither method is likely to be completely accurate, this dialog will probably stick around as a manual override in the future.
So, what about that release? Well, the more I think about it, the more unsure I become. It’s not uncommon for emulators these days to have very outdated releases, while the development builds are months, or even years, ahead. NooDS already has automatic development builds set up, so it feels like a release would merely be a formality. I can’t see myself ever recommending that someone use a release build over a development build, so I might just adopt the rolling release ideology for this project. Or maybe I’ll do a v1.0 release once NooDS is finally ready to compete with the likes of melonDS and DeSmuME. Either way, doing a release right now just doesn’t seem like a worthwhile idea.
That being said, NooDS is coming along very nicely, and I’m really excited to see where it goes from here! I think I’ll start focusing on bug fixes next; Mario Kart DS doesn’t boot, and neither do the Legend of Zelda games. I’d love to test the 3D renderer out with these titles, so getting them working is high on the to-do list. It would also be interesting to start working on being accurate enough to avoid triggering the anti-piracy measures that I’ve encountered in a number of games. Well, see you next time, and thanks as always for your continued support!