The New Era of HLE Audio


In early 2013, Dolphin had began its first steps in a new focus on accurate emulation. The 3.5 release represented a shift in the emulator's focus, and as such, saw great improvements in terms of compatibility and accuracy over the previous release. But one area that stuck out like a sore thumb during this era was the quality of High Level Emulation (HLE) audio. Hundreds of games suffered from crashes associated to audio, and thousands had significant problems, with missing effects, incorrect volume, and random bursts of noise.

The problems of HLE were systemic, deeply rooted problems within its design, and would require a complete rewrite in order to solve. Rewriting HLE audio was always a priority, but the daunting task to reverse engineer, implement, and test kept most developers away. So instead they pursued Low Level Emulation (LLE) to great success. LLE audio worked so well, the developers were able to avoid the mess of HLE and more or less just tell users to dump a GameCube/Wii DSP-ROM and use that instead. The problem with that option is performance: LLE audio is incredibly demanding, especially when the DSP is being strained by many sound effects.

This situation finally changed right after Dolphin 3.5 when delroth merged New-AX-HLE-GC, a rewrite of the most common microcode (µcode) for GameCube games, AX-GC. Thousands of bugs disappeared over night and stability increased greatly. While previously there was argument among developers that HLE audio bugs could be ignored because of the option for LLE, as tens of thousands of users finally experienced accurate audio for the first time it became apparent just how important HLE audio truly was. Later in the year, the AX-HLE rewrite was expanded to Wii games in a second cleanup. The ability for users to use HLE audio for most games instead of LLE audio resulted in one of the greatest performance increases in Dolphin's history!


The Non-AX µcode Games

While over 99% of GameCube and Wii titles use the AX µcode, there are a small number of games that use a different µcode. The "Zelda µcode”, named after its exclusive use in Nintendo-created titles, represents only a tiny portion of the total games Dolphin can play; but those games are some of the most popular and interesting games on the GameCube and Wii.



The Zelda µcode games, in release order


New-AX-HLE gave the greatest increase in audio accuracy and performance in the nearly twelve year history of the emulator, but all of the Zelda microcode games were completely unaffected. When Dolphin 4.0 released with New-AX-HLE as a core feature, it stuck out like a sore thumb that some of the most popular games on the consoles still had very lackluster HLE audio emulation. Just as before, users were forced to use LLE audio in order to get good audio emulation, at the expense of performance.

Obviously, the reason the AX-HLE rewrite was prioritized over Zelda-HLE was the sheer number of games. But there was a second reason: the old Zelda-HLE implementation was actually eons better than the old AX-HLE implementation. While AX-HLE was a mess of guesswork and wishful thinking, the Zelda-HLE microcode was more or less reverse-engineered from looking at the microcode binary. While there were some mistakes and implementation issues (it was designed around asynchronous audio, for instance), it at least approximated what the DSP was trying to do. AX-HLE, on the other hand, was one of the messiest pieces of patched together guesswork throughout the whole emulator.


// TODO: WTF is going on here?!?
// Volume control (ramping)
static inline u16 ADPCM_Vol(u16 vol, u16 delta)
{
        int x = vol;
        if (delta && delta < 0x5000)
                x += delta * 20 * 8; // unsure what the right step is
                //x += 1 * 20 * 8;
        else if (delta && delta > 0x5000)
                //x -= (0x10000 - delta); // this is to small, it's often 1
                x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario
                        // did not have time to go to zero before the were closed
                //x -= 1 * 20 * 16;

         // make lower limits
        if (x < 0) x = 0;
        //if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make
                // any sense?

        // make upper limits
        //if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also
                // has a volume target?
        //if (x >= 0x7fff) x = 0x7fff; // this seems a little high
        //if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000
        if (x >= 0x8000) x = 0x8000; // clamp to 32768;
        return x; // update volume
}

Part of the old AX HLE implementation. The actual correct implementation of this function would be:
return vol + delta;


By being more competent and used in far less games, Zelda-HLE ended up falling to delroth's backburner. But better it may have been, therewere still some incredibly frustrating issues throughout Zelda-HLE. In any of the games, volume balancing was almost always guaranteed to be wrong, effects wouldn't play, music would get desynced or stop altogether, and because of how the GameCube/Wii can use the DSP for timing events, sometimes games would outright hang when an audio event was missed or played at the wrong time. Many people probably don't remember the Wind Waker Item Hang cheatcode that was included with Dolphin for years, but it was made to work around this exact kind of issue!


Rewriting Zelda-HLE

Spurred on by Zelda-HLE being broken in Dolphin's updated audio dumping, magumagu started working on some Zelda-HLE cleanups in March 2014. The main goal was to remove the asynchronous audio processing so that it could be dumped correctly along with the rest of HLE audio. On top of fixing that, magumagu's changes greatly increased the stability of the Zelda-HLE games. Without any further work, music looping bugs in games like Mario Kart: Double Dash!! and Super Mario Galaxy 1/2 disappeared.


Not only is the music too quiet, it outright disappears!

Music looping and playing properly in the finished implementation.


The effects were farther reaching in the long term. The Legend of Zelda: The Wind Waker and Twilight Princess were far less likely to hang on transitions while Super Mario Galaxy would no longer hang after the Grand Star cutscenes even during slowdown. Because this change fixed so much, it renewed interest in improving the quality of Zelda µcode HLE in Dolphin. More cleanups followed the first one, and while they didn't have the same end-effect, they did at least make things a little bit nicer in one of the darker spots of Dolphin's codebase.

Despite the improvements, it was very clear to everyone that just cleaning up the existing code would not be enough to bring Zelda-HLE up to par with the rest of audio emulation in Dolphin.


The Main Issues

  • There were readability issues that prevented it from being easily changed. When Zelda-HLE was originally written, the code was directly transcribed from its assembly code to C++. Little effort was put into understanding what the code was doing; it was akin to someone translating a book from one language to another without knowing anything about one of the languages. In fact, understanding the original assembly code was sometimes easier than looking at the pre-existing implementation!
  • Features were outright broken, such as audio generated on the DSP. It didn't seem to work at all in the old implementation. This was very apparent throughout several games across many µcode versions.


Super Mario Sunshine Generated Audio: Before rewrite.

Super Mario Sunshine Generated Audio: After Rewrite.


  • There were a lot of things hard-coded to how Wind Waker, the game the implementation was based upon, worked. For example, games can tell the µcode what channel a sound should be mixed to. However, the old Zelda-HLE implementation would just ignore this completely and always assume that the first channel was the left audio channel, and the second the right audio channel, simply because this is how Wind Waker did it. As expected, this was a bad idea. Rather than fixing the issue, users were recommended to use the MONO audio output in these games.


Four Swords Adventures in stereo on the old implementation.

Once finished, the new implementation actually listens to the game, fixing the issue.


  • Documentation was not adequate for a rewrite; meaning everything would have to be reverse engineered from the ground up.
  • Finally, a lot of features were just completely missing. However, the original developers realized this, and instead of properly implementing the features, they were worked around in completely wild ways. As an example, of the 15 audio related commands in the Zelda µcode, the old HLE implementation only implemented 2.


Tackling the Behemoth

On April 1st, 2014, delroth started his long project to rewrite Zelda-HLE from scratch. Almost all of the pre-existing code was torn down, and everything would be rebuilt from a clean slate, based on extensive analysis of the Zelda µcode behavior. The focus was put on a single game at first: The Legend of Zelda: The Wind Waker. The same game that served as the seed for the first implementation was the initial focus of the second one, nearly a decade later.

Progress was expectedly slow at first. Rather than jumping head first and attempting to make Wind Waker play sounds, delroth focused on reverse engineering and actually understanding what Wind Waker was attempting to do. By reusing the tools that were written for delroth's AX HLE reverse engineering, the Zelda TWW UCode was documented and re-discovered from scratch.


ReverseEngineer

This is what it looks like to reverse engineer a DSP


For several months, not even The Legend of Zelda: The Wind Waker was booting in the New-Zelda-HLE branch. This was to be expected; rather than focusing on making the game play first and foremost, delroth was going through the DSP and implementing things as accurately as he could. While the end-goal was perfect audio, rushing toward that goal could lead to another case of the old implementation riddled with holes and flaws. As the months rolled on, Wind Waker finally took its first steps into playability. It didn't immediately crash due to the incomplete audio implementation! By December 2014, Wind Waker would even play some sounds! Barely. Some of the earliest examples of audio sound like something straight out of Dolphin 1.0.3.


Broken Resampling early on in the new implementation.

Broken Audio Loop early on in the new implementation. Outright creepy!


By this point, the implementation was actually fairly complete. Testers started going through The Wind Waker to try and find audio that sounded incorrect and samples that were not yet implemented. Several instruments in Wind Waker's miniboss theme triggered unknown samples which made it pretty much unplayable with all the popups! As things went on further and samples were implemented, the new implementation surpassed the old one. Even this early on, the fruit's of the reverse engineering labor were apparent.


The beeping plaguing many Zelda-HLE games appeared in Wind Waker too!

Even at an early state, it played correctly in the reimplementation.


Emulating new features that the old implementation ignored was a challenge as well. Reverb and echo in Wind Waker weren't particularly complicated, but they did put up some fight before they were properly emulated.


An early attempt at emulating echo and reverb


Testers began digging for more and more subtle bugs as Wind Waker approached perfection. For some, even LLE audio was doubted and console comparisons were brought in for comparison. The fact that testers were arguing over very minor issues just showed how far Wind Waker had come in such a little amount of time. As such, delroth moved on to the next step of New-Zelda-HLE. As the Zelda µcode has multiple versions, focus was transferred toward implementing those iterations and figuring out what the differences were. The games released after Wind Waker had the µcode change a lot less than the earlier versions of the µcode, so there wasn't a huge delay between adding support and games playing. As a bonus, the reimplementation had fixed many of the bugs before support was even added to the other µcode!


Reverb was non-existent in the old implementation.

in Four Swords Adventures, it was fixed before the µcode was implemented!


The differences between the µcodes actually revealed some interesting tidbits into how the old implementation was failing as well. For example, all µcode versions after The Wind Waker automatically double the positional audio volume to compensate for audio volume lost in the position computations. One of the most notable bugs throughout the old Zelda-HLE implementation was the fact that a lot of the audio was too quiet. Upon checking into things, this bug only happened in games released after Wind Waker!


Mario Kart: Double Dash!! - Position Audio Before.

Mario Kart: Double Dash!! - Positional Audio After.



Multiplyby2

The later µcode used in Double Dash on the left automatically multiplies positional audio by two so it isn't too quiet. In the older µcode, the game had to handle this manually


Adding support for each µcode after Wind Waker's didn't take very long. Even Wii support wasn't too big of a deal; The biggest difference of the Wii version of the µcode was that it used MRAM to emulate ARAM because the Wii doesn't have a dedicated ARAM chip like the GameCube. Beyond that, the implementation is very similar to the last implementation of the µcode for the GameCube. Interestingly enough, Wii Zelda µcode suffered from the same problems as the GameCube counterpart.


Positional Audio in Super Mario Galaxy - Before.

Positional Audio in Super Mario Galaxy - After.


Twilight Princess Wii suffered from the same bugs as its GameCube counterpart.

Fixing the bugs in one of them fixed the bugs in both them!


With all of the post Wind Waker µcodes taken care of, delroth's attention turned to implementing the older µcodes. Support for the older versions of the µcode was actually harder than the newer ones as they had a lot more meaningful differences. For instance, while Super Mario Sunshine shared a lot of the same bugs with Wind Waker, it used an entirely different way of syncing the audio. Implementing the per-frame synchronization used in Super Mario Sunshine and Pikmin 1 (PAL) was necessary to accurately emulate their µcode.

The µcode versions prior to Super Mario Sunshine are known as the Zelda µcode "Light". It has several differences between it and the full µcode; mostly that it has less features overall. Pikmin (NTSC), Luigi's Mansion and Animal Crossing all fell into this group. Coincidentally, they were among the most stable of the Zelda-HLE games on the old implementation. Another special thing in Pikmin and Animal Crossing is specialized crypto features for connecting to the GBA over a link cable. The old implementation didn't actually try to emulate these features, causing the NES games in Animal Crossing to be unplayable when transferred to a GBA emulator. Pikmin and Luigi's Mansion suffered from lots of strange issues even without that problem; and while none of the synchronization in these games was strenuous enough to cause hangs or crashes, sometimes the old implementation could do weird things.


WARNING: VERY LOUD.

After being reimplemented, things sound much nicer.



With all of the work put into New-Zelda-HLE, most of the games should output perfect or near perfect audio. There are a few minor problems that crop out in certain versions of the µcode, but none of them are going cause havoc like some of the old bugs. One of the biggest hurdles left is to fully implement support for the IPL (GameCube BIOS), which uses the earliest version of the Zelda µcode. In the old implementation of Zelda-HLE implementation, the NTSC IPL would refuse to emit anything else than loud beeps. It turns out that the NTSC IPL's early version of the Zelda µcode version has smaller voice parameter blocks, so the parameters are not always where the code would expect them to be. While full support was not added, to make things more bearable, some basic support for the NTSC-IPL was implemented. Now testers won't have to worry about blowing out their ears, even if the sounds aren't entirely right yet.


Beware: loud beep!

The NTSC-IPL still has a ways to go before it's perfect.



Future

With all of the systemic issues finally concluded, DSP-HLE has entered a new era. In the vast majority of games, Dolphin's HLE audio implementation is now at parity with the LLE audio. There are still some known bugs out there that are unique to HLE audio, but these bugs can be handled as single bugs, without the massive rewrites and labor that chases developers away! Now DSP-HLE is nearly perfect, and issues can be solved progressively over time, as an emulator should be.

  • Mario Kart: Double Dash!! Echo: Unlike the other Zelda µcode games, echo still doesn't seem to work right in Mario Kart: Double Dash!! It's hard to hear over the music, but there is definitely a difference between HLE and LLE audio.
  • µcode Switching for GBA-Link: For some games, HLE Audio will cut out randomly when using GBA-Link. This has been recorded in at least one GBA<->GCN game: Mega Man X: Command Mission.
  • Star Wars Rogue Squadron II/III: These games have severe problems in HLE audio that need to be fixed in the future.
  • 48KHz Games: This is a bit of a confusing bug. Apparently some GameCube games could use 48000 Hz audio instead of the standard 32000 Hz, and HLE audio doesn't handle that properly. -> Unexpectedly fixed hours after the article was posted.
  • Wii FMV Audio: Another strange one that probably comes down to a timing issue. In HLE audio, certain FMVs seem to not play audio some of the time. -> Unexpectedly fixed hours after the article was posted.
  • Pac-Man World Rally: No one has any idea why the sounds play so loud in this weird namco racer.


You can continue the discussion in the forum thread of this article.

Next entry

Previous entry

Related entries

Similar entries