Delta 1.3 - Nintendo DS Emulation For Everyone- 10 mins
After more than a year in development, I’m thrilled to announce Delta 1.3 is now available and brings support for playing Nintendo DS games to everyone 🥳
Delta, meet DS
When I first launched Delta almost 2 years ago, I announced that support for playing DS games would be coming soon, and released a beta version of Delta exclusively for my Patrons that included an early preview of the feature. Unsurprisingly, this initial release was extremely buggy — but thanks to feedback from my Patrons, I’ve continued to refine Delta’s DS support over time, and now it’s finally stable enough for public use!
Powered by melonDS
Delta originally used DeSmuME to emulate DS games, but after some discussions with my Patrons I decided to instead switch to melonDS. This switch was motivated by the fact that DeSmuME development has mostly stalled while melonDS is still being actively developed, as well as melonDS supporting features I would eventually like to add to Delta that DeSmuME doesn’t (such as multiplayer support).
However, there were still some features I wanted to add that weren’t already supported by melonDS, so I ended up forking melonDS to implement them. Specifically, my changes were:
- Added callback function for when melonDS requests microphone data, allowing Delta to delay activating the microphone until it is actually needed.
- Supports JIT on iOS by mapping separate virtual memory addresses with different permissions (one with EXEC permissions, the other with READ and WRITE) to the same underlying memory.
- Skips rendering frames that will not be displayed due to fast forwarding for improved performance.
Unlike DeSmuME, melonDS requires several “BIOS” files in order to work, which can be found & downloaded relatively easily by Googling (no, I can’t link you to them sorry). Once you’ve imported these files in Delta’s Nintendo DS core settings, you’ll then be able to emulate DS games as normal, and even access the DS home screen to change settings such as your nickname, theme color, etc.
⚠️ For now, DeSmuME will remain an optional core in beta versions of Delta. This allows Patrons to keep using any DeSmuME save states they’ve already made, and eventually migrate to melonDS. However, all future DS improvements will be limited to the melonDS core, and eventually DeSmuME will be permanently removed.
Beautiful Controller Skin
Every Delta system comes with its own custom-designed controller skin optimized for touch, and DS is no different! Like Delta’s other controller skins, this skin was designed by Caroline Moore and pays homage to the original DS hardware — in this case, the iconic pink Nintendo DS Lite.
The standard DS controller skin adapts perfectly to both portrait and landscape orientations.
melonDS can run at 100% speed on most newer iOS devices, but Delta can only fast forward up to 1.5x for a few minutes before being throttled by the system. By enabling just-in-time (JIT) compilation, however, Delta can dramatically improve melonDS performance — doubling the maximum fast forward speed from 1.5x to 3x. Even with this speed increase, the overall boost in performance is enough to also prevent iOS from throttling your device’s CPU after fast forwarding for extended periods, allowing you to play games at 3x speed without any slowing down.
Unfortunately, there is one major caveat: Delta’s current JIT implementation only works on iOS 14.2 — 14.3. This is because only these iOS versions allow JIT without any restrictions (for whatever reason), which unfortunately was changed in iOS 14.4. In other words, melonDS JIT is not supported on devices running iOS 14.4 or later.
That being said, there is hope! I’ve discovered a new method to enable JIT on devices running iOS 14 or later — including iOS 14.4 and the upcoming iOS 14.5 — which I plan to release in beta soon. This new method is slightly more complex than what iOS 14.2 and 14.3 support, but I plan to automate as much complexity as possible so it can “just work” — so stay tuned 😉
A small but nice feature: Delta can use your iOS device’s microphone to emulate the DS’ microphone. Probably not a feature you’ll need often, but when you do come across a game that requires the microphone, Delta has you covered 💪
Mario Party DS makes extensive use of the DS microphone.
Simulate Closing & Opening DS Lid
Even more niche than microphone support, Delta can simulate closing & opening the DS lid just by sleeping & waking your device. For the vast majority of games this doesn’t matter — but at least now you can hear Mario saying “Bye-Bye!” when playing New Super Mario Bros. 🤷♂️
Misc. DS Features
Of course, DS games support all the same features you’re used to with Delta’s other consoles, including:
- Save States
- Cheats (Action Replay)
- Fast Forwarding (up to 3x with JIT, 1.5x without)
- Game Controller support
Delta strives to be the best emulator for each system it supports, so keep in mind these are just the initial DS features! I plan to add several more features — such as local and online multiplayer — in the future, and would love to hear what features y’all want most 😄
Other Delta 1.3 Changes
Besides DS support, Delta 1.3 also comes with a bunch of additional features and changes to improve the overall experience using the app.
Dark Mode Support
Thanks to Eric Lewis, Delta now supports Dark Mode throughout the app! Delta already used a dark theme for its main UI, but several secondary screens such as Settings used a lighter theme — which could be especially jarring to switch between in dark environments. Now these secondary screens will also take on a dark appearance when your device is in Dark Mode, saving you from burning your eyes at night!
All screens now support dark mode, including Delta’s settings and the “Add Cheat” screen.
Audio Distortion Fix
When playing games for an extended period of time, Delta would randomly start distorting the game audio for several minutes before (hopefully) fixing itself. I spent several days trying to track down the cause, and eventually hypothesized the issue had something to do with Delta’s audio “ring buffer” becoming corrupted. Unfortunately, as a visual problem solver it was hard for me to “visualize” how exactly the ring buffer was corrupted whenever I reproduced it (did it fill up too quickly? did it not fill up quickly enough?), but then it hit me: I could graph the memory addresses of the audio buffer’s head and tail pointers, and then I could visually see whether anything strange was going on!
today I fixed an audio bug by plotting memory addresses in Numbers AMA pic.twitter.com/TfA9qjpcD1— Riles 🤷♂️ (@rileytestut) December 1, 2020
Sure enough, I noticed that occasionally the ring buffer’s head pointer would go “beyond” the tail pointer, effectively corrupting the audio data. But why? Simple: good ol’ human error 🙃 I originally used TPCircularBuffer as-is for Delta’s audio buffer, but in order to launch Delta Lite I needed to rewrite everything in Swift so that it could be used from Swift Playgrounds. So, I re-wrote TPCircularBuffer line-for-line in Swift, and then continued working on Delta Lite without giving it another thought…but as it turns out, this rewrite was what caused the bug in the first place.
I checked my code to see why the head pointer would go beyond the tail, but didn’t see anything obvious (I already had bounds-checking logic that should’ve prevented something like this), so I wondered if there was some strange concurrency issue at play. I decided to check out TPCircularBuffer’s source again to see if there was anything I missed, and to my delight the README had an explicit “Thread safety” section which mentioned:
As long as you restrict multithreaded access to just one producer, and just one consumer, this utility should be thread safe.
Only one shared variable is used (the buffer fill count), and OSAtomic primitives are used to write to this value to ensure atomicity.
BINGO! In my re-implementation, I did not use OSAtomic primitives when updating the buffer fill count, because I (naively) assumed it was unnecessary at the time 😅 Sure enough, once I updated Delta’s RingBuffer to atomically update the buffer fill count via OSAtomic primitives, the issue disappeared once and for all!
Better Real Time Clock Support
Another long requested bug fix, Delta 1.3 now supports the Real Time Clock (RTC) for all Game Boy Advance games — including Pokémon hacks. Previously Delta would only enable RTC if it detected the game needed it, but this detection was imperfect and led to several false negatives. Now, RTC is enabled for all games by default, so any game that didn’t work with Delta’s RTC before should now work as expected.
Besides these bigger changes, Delta 1.3 also contains several quality-of-life improvements, including:
- Option to disable previews when using context menu
- Support for transparency in game artwork
- “Import Controller Skin” button when changing controller skins
Now that Delta 1.3 is finished, it’s time to start working on Delta 2.0! Delta 2.0 will be the biggest update yet to Delta, adding support for iPad and macOS as well as a bunch of other awesome features. What awesome features exactly though? Well…that’s up to you!
To make it easier to stay up-to-date with the latest Delta developments, there is now an official Delta Emulator Roadmap Trello board. In addition to listing all the features I either plan to add or that are already in development, this board also lets you vote on which features you’re most interested in, helping me prioritize what features to work on next. I’ve been testing it internally with my Patrons for the past few months and it’s been a huge help, so if you’d like to have a say in what features make it into Delta 2.0, please check it out — with your feedback, we can continue to make Delta the best possible app it can be 🙏