summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Reduce-Noise-and-Improve-Sound-Quality-(Implementing-a-New-Mixer).md116
1 files changed, 59 insertions, 57 deletions
diff --git a/Reduce-Noise-and-Improve-Sound-Quality-(Implementing-a-New-Mixer).md b/Reduce-Noise-and-Improve-Sound-Quality-(Implementing-a-New-Mixer).md
index 27282fe..3bc0433 100644
--- a/Reduce-Noise-and-Improve-Sound-Quality-(Implementing-a-New-Mixer).md
+++ b/Reduce-Noise-and-Improve-Sound-Quality-(Implementing-a-New-Mixer).md
@@ -1,24 +1,22 @@
-Many thanks to ipatix and Kurausukun. Without them, none of this would be possible.
+## Preface
+All GBA games use the sound driver that comes in the GBA SDK, m4a (also known as mp2k or "Sappy"). While not a bad engine, it does have the issue of being notoriously noisy. In response to this, [ipatix](https://github.com/ipatix) created a custom audio mixer for use in the m4a engine based on the custom engine of the Golden Sun games. This engine has two major benefits:
+1. Less noise. The SDK's audio mixer mixes every channel in 8-bit before also outputting at the final 8-bit (or 9-bit) DAC. What this means is that every additional DirectSound channel will add more noise, and with enough channels it can get pretty bad. This mixer avoids that issue by mixing the audio in a 16-bit space so that the only conversion to 8-bit is at the final DAC output; this means that no matter how many channels you have, the amount of noise will be the same (equivalent to having a single channel worth of noise in the original mixer).
+2. Better performance. The code is highly optimized and contains a self-modifying code loop that is loaded into RAM and allows for faster processing than the SDK mixer, even despite the new 16-bit arithmetic. For more details on how the mixer itself actually works, visit [ipatix's repository for the mixer](https://github.com/ipatix/gba-hq-mixer). However, none of this information is necessary if yo just want to use it in your game, and with that said, we can now move on to the actual tutorial.
-Anyway, these changes will allow the game to have reduced noise and output sound at a higher base frequency.
+## Implementing the Mixer
-In [src/m4a_1.s](https://github.com/pret/pokeemerald/blob/master/src/m4a_1.s), replace everything in that file with the code in the link below:
-[Mixer code](https://gist.github.com/Xhyzi/2a3eafac546788c1c158295d5b3d3526)
+### Replacing the Mixer Code
+Replace the [src/m4a_1.s](https://github.com/pret/pokeemerald/blob/master/src/m4a_1.s) file with [this one](https://raw.githubusercontent.com/Kurausukun/pokeemerald/newmix/src/m4a_1.s). This is a rewritten version of the file with a version of ipatix's mixer already inserted. The version in this file has been tailored specifically for pokeemerald and thus is slightly different than the version on his repository.
-This is the code for the mixer.
-
-In [src/m4a.c](https://github.com/pret/pokeemerald/blob/master/src/m4a.c), change the size of SoundMainRAM_Buffer to
-```c
-0xC00
-```
-Again in [src/m4a.c](https://github.com/pret/pokeemerald/blob/master/src/m4a.c), add this line right below the line mentioned above:
+In [src/m4a.c](https://github.com/pret/pokeemerald/blob/master/src/m4a.c), change the size of SoundMainRAM_Buffer to `0xB40`. This buffer is where the code itself is loaded into.
+Still in [src/m4a.c](https://github.com/pret/pokeemerald/blob/master/src/m4a.c), add this line right below the line mentioned above:
```c
BSS_CODE ALIGNED(4) u32 hq_buffer_ptr[size] = {0};
```
-With the aforementioned changes, it should look like this:
+This is the intermediate audio buffer in which the mixing will be done. With the aforementioned changes, the lines should look like this:
```c
-BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0xC00] = {0};
+BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0xB40] = {0};
BSS_CODE ALIGNED(4) u32 hq_buffer_ptr[size] = {0};
```
Here, `[size]` is the length of one frame of audio, which varies by the sample rate you use. Here is a list of the possible sample rates and their corresponding frame sizes:
@@ -36,60 +34,64 @@ Here, `[size]` is the length of one frame of audio, which varies by the sample r
40137Hz: 0x2A0
42048Hz: 0x2C0
```
-Just find the sample rate you're using and use its corresponding frame size as the size of the array. For example, for the default sample rate, the size of the array is 0xE0 (note that the size of the array actually needs to be the frame size x 4; this is why this array is of type `u32` rather than `char` or `u8`).
+Find the sample rate you are using and use its corresponding frame size as the size of the array. For example, for the default sample rate, the size of the array is 0xE0 (note that the size of the array actually needs to be the frame size x 4; this is why this array is of type `u32` rather than `char` or `u8`).
+
+We have finished "inserting" the mixer, but DO NOT COMPILE YET. It will fail, and that is because... we are out of memory!
-Additionally, if you are planning on building the `modern` target, you will need to change
+### Making Room in RAM
+The one downside to the new mixer is that it does require more RAM than the default mixer due to the increased code size and additional mixing buffer. IWRAM is precious in GBA programming, but it is also incredibly limited (you only have 32KB). Luckily for us, there are structures that we can move from IWRAM to EWRAM without affecting the gameplay--this will allow us to make room for our new mixer.
+
+We will be moving two structs that store data related to the RFU--that is, the Wireless Adapter. These two structs are `Rfu` and `gf_rfu_REQ_api`, both located in [src/link_rfu_2.c](https://github.com/pret/pokeemerald/blob/master/src/link_rfu_2.c). If you head to that file, you will see these two lines starting at line 44:
```c
-struct SoundInfo gSoundInfo = {0};
+u32 gf_rfu_REQ_api[RFU_API_BUFF_SIZE_RAM / 4];
+struct GFRfuManager Rfu;
```
-to
+To move these to EWRAM, change these two lines to:
```c
-EWRAM_DATA struct SoundInfo gSoundInfo = {0};
-```
-In the vanilla build, we rely on `sym_ewram.txt` to put that symbol in the EWRAM section, but the `modern` target does not use `sym_ewram.txt`, which means that if we do not add this macro, the linker will attempt to put it into IWRAM, which will overflow the section and cause it not to build.
-
-Next, remove the following from [common_syms/m4a.txt](https://github.com/pret/pokeemerald/blob/master/common_syms/m4a.txt):
-```
-gSoundInfo
+EWRAM_DATA u32 gf_rfu_REQ_api[RFU_API_BUFF_SIZE_RAM / 4] = {};
+EWRAM_DATA struct GFRfuManager Rfu = {};
```
-Add then add the following to the end of [sym_ewram.txt](https://github.com/pret/pokeemerald/blob/master/sym_ewram.txt):
+Next, go to [sym_common.txt](https://github.com/pret/pokeemerald/blob/master/sym_common.txt) and delete the line that says `.include "link_rfu_2.o"` on line 37:
+```diff
+ .include "AgbRfu_LinkManager.o"
+- .include "link_rfu_2.o"
+ .include "rtc.o"
```
-gSoundInfo:
- .space 0xFB0
-```
-That's the mixer stuff out of the way, and if it was done correctly, the game shouldn't be producing as much quantization noise.
+Finally, delete the file [common_syms/link_rfu_2.txt](https://github.com/pret/pokeemerald/blob/master/common_syms/link_rfu_2.txt).
-Optionally, you can also change the audio engine's sample rate if you're using higher-quality samples. However, be warned; this means that any fixed-frequency samples (most percussion samples) will be played at this frequency; if you keep these samples at a lower sample rate, they will get pitched up and not sound right. Resample your samples to the rate you're making the engine use, or use higher quality samples and downsample them to the proper rate.
+Now we are done--the ROM can finally be built and should work with the new mixer. But there are more things we can do.
-In [src/m4a.c](https://github.com/pret/pokeemerald/blob/master/src/m4a.c), look for:
-```c
- SoundInit(&gSoundInfo);
- MPlayExtender(gCgbChans);
- m4aSoundMode(SOUND_MODE_DA_BIT_8
- | SOUND_MODE_FREQ_13379
- | (12 << SOUND_MODE_MASVOL_SHIFT)
- | (5 << SOUND_MODE_MAXCHN_SHIFT));
-```
-For the sake of this tutorial, we'll be changing it to 36314Hz. So in that file, it would look like this:
-```c
- SoundInit(&gSoundInfo);
- MPlayExtender(gCgbChans);
- m4aSoundMode(SOUND_MODE_DA_BIT_8
- | SOUND_MODE_FREQ_36314
- | (12 << SOUND_MODE_MASVOL_SHIFT)
- | (5 << SOUND_MODE_MAXCHN_SHIFT));
-```
-There's another call to set the sound frequency on [line 375](https://github.com/pret/pokeemerald/blob/master/src/m4a.c#L375); while it appears this code never actually affects anything since it gets overridden by the above change, for fool-proofing and if you're paranoid like me, you can change that one to match as well.
+### Making _More_ Room in RAM (Optional)
+The changes we have made leave enough room in RAM for the mixer to work with no problems at the default engine rate. However, if you increase the engine rate, and thus increase the size of our new `hq_buffer_ptr` array, you may still run into memory issues. Luckily, there is one more thing we can do.
-Now all that's left, and this is optional as well, is fixing the reverb for your new sample rate. At rates other than the default rate of 13379Hz, the game's reverb sounds much less pronounced, and if you go high enough, it gets to the point that you can't even really tell it's there. This is honestly a matter of preference--some people think reverb sounds good, some people prefer not to have it--but for the sake of completeness and those who like reverb, I will explain how to get the reverb to sound like it does at the default sample rate.
+The Pokemon games use 5 DirectSound channels, but m4a always allocates space for the maximum amount, 12. This is wasteful; we can reclaim some free memory from this. Note that if you have edited the number of DirectSound channels in `m4a.c` to be larger than 5, you will want to use that value instead of 5 in the following replacements.
-I talked about it like it's a huge deal, but there's actually not too much we need to change--in fact, the only thing that needs changing is the size of the direct sound buffer the game uses. This is defined in two different files--[constants/m4a_constants.inc](https://github.com/pret/pokeemerald/blob/master/constants/m4a_constants.inc#L3) and [include/gba/m4a_internal.h](https://github.com/pret/pokeemerald/blob/master/include/gba/m4a_internal.h#L152). Opening these files, you will see that the buffer's default size is 1584, or 0x630 in hex. Where does this number come from, you may ask--well it comes from the frame size I mentioned up above. The buffer is designed to last 7 frames in the game, plus some extra space at the end, so multiply your frame size to get the value you need. If we recall that the frame size of the default sample rate is 0xE0, we can do 0xE0 x 7, and we will find that it equals 0x620--which is 0x10 less than the original value (this is the extra space I mentioned). So find your sample rate in the table again, multiply it by 7, and replace the value in those two files with your new value. But we're not quite done yet!
+In [constants/m4a_constants.inc](https://github.com/pret/pokeemerald/blob/master/constants/m4a_constants.inc), change `MAX_DIRECTSOUND_CHANNELS` from 12 to 5.
+Next, in [include/gba/m4a_internal.h](https://github.com/pret/pokeemerald/blob/master/include/gba/m4a_internal.h), change the same `MAX_DIRECTSOUND_CHANNELS ` define from 12 to 5 again. Now we have reclaimed all of the memory we can, and the mixer can go to higher rates without overflowing memory.
-The last thing we need to do is update the mixer code to use this new value. By default, it uses a hardcoded 0x630 value to match the default buffer. Changing that is actually going to be harder than expected due to the way immediate values work in ARM. The statement in question can be found on [line 890](https://gist.github.com/ShinyDragonHunter/6571250991f4b0f560206e2201e27e55#file-m4a_1-s-L890) of our new `src/m4a_1.s` file. If you're using anything under 36314Hz, you should be fine to just replace the value. However, for rates 36314Hz and above, the size of the buffer is too large to fit into the immediate field of a `mov` instruction. Generally, the solution to this is finding values that can be bit-shifted to equal your new size. This works nicely for 36314Hz's buffer size of 0x10A0--you'll find that 0x85 << 5 = 0x10A0. And luckily, ARM lets us bit-shift right in the operand, so we can do:
-```asm
-mov r0, #0x85
-str r6, [r9, r0, lsl#5]
+### Changing the PCM Buffer Size (for Accurate Reverb)
+Note that this section only applies if you have both increased the engine rate in m4a.c and have not disabled reverb. By default, the size of the PCM DMA buffer is enough to hold 7 frames of audio, which is how many frames the reverb algorithm expects to be there. If you increase the engine rate without increasing the PCM buffer size, the game will not crash or anything, but you will notice that the reverb sounds shorter, and depending on how high you set the engine rate, almost nonexistent. The fix for this is increasing the PCM buffer size to be able to hold 7 frames of audio in our new engine rate. You will notice that by default the buffer size is 1584; this value comes from the frame size (see the list in the [Replacing the Mixer Code](https://github.com/Kurausukun/pokeemerald/wiki#replacing-the-mixer-code) section) multiplied by 7, with 0x10 added for safety (this is unnecessary and we will not be doing it for our new sizes). Thus we can calculate the PCM buffer size that we need for every possible engine frequency:
+```
+5734Hz: 672
+7884Hz: 924 (This mode is not aligned to the buffer length and is not supported by the mixer)
+10512Hz: 1232
+13379Hz: 1568 (As mentioned, the actual value used by the default mixer adds 16 to this)
+15768Hz: 1848
+18157Hz: 2128
+21024Hz: 2464
+26758Hz: 3136
+31536Hz: 3696
+36314Hz: 4256
+40137Hz: 4704
+42048Hz: 4928
```
-In place of the original `str` command. For 40137Hz, 0x93 << 5 works. And for 42048Hz, you can do 0x134 << 4. Replace them just like the statement above, and you'll be good.
+Now all that we need to do is replace the definitions of the buffers. This must be done in the following two locations:
+1. In [constants/m4a_constants.inc](https://github.com/pret/pokeemerald/blob/master/constants/m4a_constants.inc), `PCM_DMA_BUF_SIZE` is set on line 3; change it there.
+2. In [include/gba/m4a_internal.h](https://github.com/pret/pokeemerald/blob/master/include/gba/m4a_internal.h), PCM_DMA_BUF_SIZE is set on line 169; change it there.
+
+### Mixer Config (Optional)
+Lastly, I would like to briefly explain the two options at the top of the mixer code, namely `ENABLE_REVERB` and `ENABLE_DMA`.
+
+`ENABLE_REVERB` does exactly what it sounds like; if set to 1, reverb will be applied as normal, sounding just like the original game. If set to 0, all reverb processing will be skipped, and the game will have no reverb. This is completely up to personal choice, but if you just want it to sound like the original game, leave it on.
-And I believe that's that. If everything was done correctly (or I didn't screw something up), you will have much less noise in your sound. \ No newline at end of file
+`ENABLE_DMA`, when set to 1, has the code use DMA3 for transferring data rather than doing a standard CPU copy. This results in faster and smaller code, but it can lead to the DMA being used for so long that it halts the CPU for an extra hblank cycle. This almost certainly will not affect you, and the results are not noticeable. If you do not know what exactly this means, just leave it on as-is. However, if you know that your use case does not allow DMA, you can set this to 0 to make the mixer do a CPU copy instead; note that this will make the code slightly larger (the code buffer size is set up for the worst-case scenario, so do not worry about that) and slightly slower (but the speed difference should not be noticeable). \ No newline at end of file