News via
http://dsx86.patrickaalto.com/DSblog.html
For the past week I have been working on the new audio system for DS2x86. As I mentioned in the previous blog post, the code was mostly done by last Sunday, but the problem was that it sounded very bad and also seemed to cause the games to hang much more frequently than with the old audio code. The current status is that the new code is in use, and works at least as well (if not better) in the games that had audio also with the old code. The new code also has ADPCM audio supported, and it works fine in Warcraft, when the original code either failed to initialize or caused the game to crash very often.
I have been using four different games to test the new audio code, as each of these games use somewhat different SoundBlaster audio features:
Supaplex uses the plain simple DMA transfers, playing one digitized sample at a time. The samples have varying sample rates.
Doom uses auto-initialize DMA method, with a 1024-sample buffer but with an IRQ request after every 128 samples. The playing rate is 11kHz.
Warcraft uses auto-initialize DMA with 2048-sample buffer and IRQ request every 1024 samples, with 11kHz sample rate.
Duke Nukem 2 uses various ADPCM sample formats with simple DMA transfers, and with varying sample rates.
I am using 22050Hz audio playing rate in DS2x86. In the original audio code I had initialized the DSTwo audio transfers to 3*128 samples per transfer (with the ds2io_initb() call), as that was the closest I could get to be able to send a new audio buffer within my 60Hz main emulation timer handler. That number was derived from the fact that I would need to send 367.5 (= 22050/60) new samples during each 60Hz timer interval, and the need of the transfer size to be divisible by 128. Using the 60Hz timer for this also meant that I could not emulate an SB IRQ faster than at 60Hz (which again corresponds to 367.5 samples at 22050Hz). For example Doom wanted to get an SB IRQ after every 128 samples have been played. As Doom played the audio at 11kHz (which is very close to half the 22050Hz playing rate I use), I should have generated an SB IRQ after every 256 output samples, or at 85Hz (= 11000/128). To make the audio in Doom work, I had coded a special hack into my audio code where I slowed down the sample rate if a game wants SB IRQs faster than my code could provide them. Obviously this was not a proper solution, so in my new audio code I wanted to handle this situation better.
The basic idea of my new audio code is that the emulation interrupt runs at 4*60Hz (240Hz) and calls the screen and keyboard handling only during every fouth interrupt. The audio emulation is handled at 240Hz, which should be fast enough for any SB IRQ frequency a game would need. By last Sunday I had implemented this new audio code, using 128 samples per transfer in ds2io_initb(), with a separate 1024-sample ring buffer that is filled in the 240Hz interrupt. Since 22050/240 = 91.875, I had designed the ring buffer filling algorithm so that it tried to fill the buffer with approximately 96 samples in each 240Hz interrupt, with the actual amount adjusted by the number of DSTwo IO/ layer audio buffers in use. Since the DSTwo I/O layer has 4 audio buffers, I calculated the needed new samples as 128-ds2_checkAudiobuff()*32. And in every interrupt where the ring buffer had >=128 samples, I sent the buffer to the DSTwo I/O layer.
I thought this code was much better than the original one, however there were several problems that took me pretty much the whole of last week to fix:
Doom had a weird looping/stuttering problem, where parts of the audio kept repeating.
The audio had a horrible constant warble, regardless of what audio was playing.
All games (playing audio) kept hanging within a minute or two of playing them.
I first debugged the behaviour in Doom, as that was one of the main problematic games I tried to fix with the new code. At last I found that the problem was in how the timer and SB IRQs interract in the Doom code. For some peculiar reason, Doom does not fill the 1024-sample DMA buffer during the SB IRQ, but instead in the timer IRQ. And with my new 240Hz maximum SB IRQ frequency, when the DSTwo audio buffers were empty, Doom could get two SB IRQs with no timer IRQ in between, and thus it skipped one 128-sample block in the DMA buffer, which in turn caused that block to play some old data that was in the buffer. I fixed this problem by fine-tuning the amount by which I fill the ring buffer, and never continuing to fill it after it was time to send the SB IRQ to the emulated game.
The second problem with the warbling sound seemed to be simply caused by the 128-sample transfer size. The DSTwo I/O layer does not seem to work very well with anything less than 512-sample transfer sizes. I tested all sizes between 128 and 512, and the 512-sample transfer size sounds best by a wide margin. It would be better to use smaller transfer sizes, as the larger the transfer size the bigger
...
Catherine: Full Body’s English translation for the Vita