IOC device 9 (Sound Buffer Change)

Discuss ADFFS development and download test releases
Post Reply
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

Several games require this, including:
  • Diggers
  • No Excuses
  • Rockfall
I initially stared coding this on IOMD by sitting between the hardware and SoundDMA, this seemed the logical route to take as essentially we're emulating SoundDMA and the MEMC sound DMA from RO3.1. After several rewrites to support both 8bit and 16bit/16bit Oversample, I've realised this probably isn't the best route to take as it's dependent on knowing SoundDMA's entry point - RISCOS doesn't provide a means to get previous device claimants.

The next obvious option is use SharedSound to register a 16bit sound handler, which is how ArcEm works. This however has drawbacks, as far as I can tell SharedSound isn't freely distributable prior to RO5/6 and there are no distribution points for it left on the 'net. It also means coding 8bit to 16bit conversion as it doesn't look like that part was coded in the module - it's hard to tell as it wasn't formally document when added to RO5.

Having read through the layer 0 (SoundDMA) and layer 1 (SoundChannels) source code on ROOL, I've realised that rather counter intuitively, if I sit further up the sound stack at layer 1 there's less to code. Layer 1 is totally 8 bit, even in the RO5 world, so we can happily inject sound into the sound buffer at this level in the knowledge that RISCOS will take care of any log to linear conversion and oversampling. The only issue it doesn't solve is differing sample rates between what the game requires and what the system supports, I'll come back to this later. Firstly, what are the advantages:
  1. I can legally get the RISCOS ChannelHandler entry point
  2. I can point the game's ChannelHander to a seperate circular audio buffer
  3. The game can use a different sound buffer size to the system (resolves 2067BC audio)
  4. I can emulate MEMC sound registers by filling SoundDMA's buffer with audio and simply not pass the call onto the registered ChannelHandler (resolves No Excuses and Rockfall)
  5. SoundDMA can be left to the 16bit conversion and oversampling
  6. We can ensure our circular buffer is at least one buffer ahead by either calling the previous ChannelHandler or game device 9 code more than once. This may also do away with the need to use timers for device 9
  7. The game won't need to run under the JIT on IOMD 26bit
Proposed IOC device 9 implementation
Proposed IOC device 9 implementation
RISCOS SoundSystem.png (33.58 KiB) Viewed 9294 times
Phlamethrower
Posts: 15
Joined: Wed Jun 12, 2013 9:29 pm

Re: IOC device 9 (Sound Buffer Change)

Post by Phlamethrower »

JonAbbott wrote:This however has drawbacks, as far as I can tell SharedSound isn't freely distributable prior to RO5/6 and there are no distribution points for it left on the 'net.
Hmm, I would have thought SharedSound would have been included in ROOL's pre-RO5 !System distribution, but that doesn't seem to be the case. I guess I should add it.
It also means coding 8bit to 16bit conversion as it doesn't look like that part was coded in the module - it's hard to tell as it's wasn't formally document when added to RO5.
I'm not sure if log to linear conversion was ever meant to be supported - I can't find any obvious mention of it in the spec. There's some linear to log conversion code, but that's only there to support systems which lack 16 bit sound hardware (and it will effectively take over the 8 bit sound system to make sure it works correctly)

But you're right though, installing your own channel handler is probably the cleanest way of inserting log samples into the sound system. It's just a shame that there's no real way of dealing with the sample rate conversion apart from going log-linear-log or giving up and using nearest-neighbour sampling.
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

Phlamethrower wrote:installing your own channel handler is probably the cleanest way of inserting log samples into the sound system.
I have noticed one quirk though, claiming the Channel Handler and then passing the call onto SoundChannels' fill code entry point doesn't produce any sound. The sound does get buffered though and plays when you release the Channel Handler, I've not investigated why yet - I simply changed the code to replace the existing Channel handler fill entry point which does work. I suspect the voice mappings are being lost.
Phlamethrower wrote:It's just a shame that there's no real way of dealing with the sample rate conversion apart from going log-linear-log or giving up and using nearest-neighbour sampling.
What are your thoughts on this area?

You don't necessarily need to go log-linear-log - only if you're using a linear interpolator. However the sound output on the VIDC DAC is not a perfect logarithm, it's linear in 8 chords to produce a pseudo logarithm, so you'd have to account for the chords or introduce noise.

Using log-linear-log and nearest-neighbour would be the closest to an Arc, provided you applied a low-pass filter to emulate the original Arc's filtering on the audio output circuit. I seem to recall the filter being quite low, 11kHz or similar - I bypassed mine at the time as it filtered too much off the top end.

Anyone know what the filter was on an A3010?
steve3000
Posts: 198
Joined: Thu May 02, 2013 9:25 pm

Re: IOC device 9 (Sound Buffer Change)

Post by steve3000 »

Your thread makes interesting reading. If only I had some more time... but rammed with work and study until and of August.

Two suggestions:

1) the way I implemented QTM's Transparent sound system may be useful to review here. It sits between the RISC OS Channel handler and SoundDMA. Works on Arthur to RISC OS 5, so something's right. See QTM source code QTM_DMA .a3_TRNSsound - give me a shout if any questions.

2) please don't emulate the low pass filter - or at least default to off (or only use if sample rate >= 64 uS) - it crippled the sound output on the Archimedes, only games like Diggers need it as they chose to use the lowest output sample rate (why? who knows...). Thankfully the filter was easily bypassed, which a lot of folk did BITD...

Steve
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

steve3000 wrote:Your thread makes interesting reading. If only I had some more time... but rammed with work and study until and of August.
This is really your area of expertise, I'm just filling in whilst your off studying :D
steve3000 wrote:1) the way I implemented QTM's Transparent sound system may be useful to review here. It sits between the RISC OS Channel handler and SoundDMA. Works on Arthur to RISC OS 5, so something's right. See QTM source code QTM_DMA .a3_TRNSsound - give me a shout if any questions.
I didn't think to look at QTM, we both came to the same conclusion by the sound of it. I started coding it up yesterday although haven't got far yet.
steve3000 wrote:2) please don't emulate the low pass filter - or at least default to off
I'll leave the filter to last and will definately add a * command to control it. It's probably only going to be necessary when interpolating between sample frequences to cut down the aliasing.

I've still not decided on the interpolation method to use, back when I was coding SoundSynth II (which was never released) I coded a b-spline interpolator for frequency shifting. Whilst Googling to see if there had been any improvements on algorithms over the past 25 years, I came across optimized image band attenuation, which makes for an interesting read.
Phlamethrower
Posts: 15
Joined: Wed Jun 12, 2013 9:29 pm

Re: IOC device 9 (Sound Buffer Change)

Post by Phlamethrower »

JonAbbott wrote:
Phlamethrower wrote:It's just a shame that there's no real way of dealing with the sample rate conversion apart from going log-linear-log or giving up and using nearest-neighbour sampling.
What are your thoughts on this area?
Well, I didn't think about it too much before making that statement :) I'm just assuming that (a) most sample rate conversion filters would be designed around working in the linear space and (b) correctly dealing with the sign, chord and point components of the mu-law data (or at least not impairing the quality to a significant extent) would involve so much work that it would probably be easier to just do a log-linear-log conversion (at least for pre-ARMv7 machines, where lookup tables are the fastest way of doing the conversions). On ARMv7 with NEON it's quicker to do log-linear conversion (and presumably linear-log) in code, so it might be possible to come up with a filter which can work on mu-law data without doing a full translation to linear, but you'd end up spending a lot of time and effort working on something that would only benefit the machines that don't really need the extra performance.

Other alternatives would be to do the interpolation using pure lookup tables, but the lookup tables would probably end up being prohibitively large, or to write some kind of code generator to generate optimised code for the current in & out sample rates - but again I'd be doubtful that it would be faster than a log-linear-log approach (especially since you could use a code generator to generate the linear part of the filter, much like SoundDMA)
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

Have now coded this as follows:

Sound_Configure is taken over by ADFFS to prevent Channel Handlers replacing ADFFS' handler and defaults to returning the RISCOS 3.1 values for sample length and period until they're changed. It attempts to set the parameters as requested by the game and then gets the actual settings the system set - this allows the Level 1 fill routine to account for differences in sample length and period. I may change this past IOMD, so it doesn't set the sample length/period and leave the upsampler to handle it.

In the case of MEMC DMA audio being used (Rockfall, No Excuses), ADFFS' Level 1 fill will use the DMA address instead of it's circular buffer. If there's no DMA pending it calls either the game Channel Handler fill entry or the SoundChannels fill entry, pointing them to ADFFS' circular buffer (a double mapped DA), on return it will check there's enough samples in the circular buffer to fill the SoundDMA audio buffer, it does this by comparing length*period of both buffers and will call the fill entry again if there aren't enough.

Samples and then copied to the SoundDMA buffer and upsampled if SoundDMA is at a higher sample rate to what the game wants. Currently this is done via nearest neighbour as I've not looked at audio interpolation yet.

Have some debugging to do and need to get the IOC device 9 IRQ working before I confirm the results, although the music on 2067BC is now correct on the Pi and No Excuses is correct on StrongARM.
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

This has highlighted two issues:
  • Sound_Enable - can be used to shutdown SoundDMA's IRQ handling (eg No Excuses)
  • OS_UpdateMEMC - used for dodgy things, such as speeding up the OS ROM's or shutting down Sound/Video DMA (eg No Excuses, Rockfall)
Unless there's a reason to allow either SWI's through, I'm going to trap both past RO3.11. ADFFS requires SoundDMA (see the diagram above) to be always active to emulate MEMC Sound DMA and legacy SoundDMA buffer sizes, and OS_UpdateMEMC isn't relevant on the RISC PC onward.

There's also another problem, in that games that write directly to the RO3.11 sound buffer at 1F06000-1F08000 (Diggers) are going to be a problem on a 26bit RISC PC - they'll be overwriting the buffer that's currently being used for audio DMA if they're out of sync (as confirmed by Diggers). I need to figure a way around this before they'll work on RO3.71 on a RISC PC.
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

JonAbbott wrote:games that write directly to the RO3.11 sound buffer at 1F06000-1F08000 (Diggers) are going to be a problem on a 26bit RISC PC - they'll be overwriting the buffer that's currently being used for audio DMA if they're out of sync (as confirmed by Diggers)
One option here is to remap the physical pages RO is using elsewhere and map in new pages. As the games are hardcoded, they'll continue writing to the logical pages, leaving ADFFS' Channel Handler and SoundDMA to handle the physical buffer. I'll try it under emulation and see if it works.

Other options include modifying the games at runtime or modifying SoundDMA to use a different logical address, neither of which are particularly appealing.

Rockfall is proving problematic, it hangs if ADFFS doesn't pass through the original SoundDMA call to SoundChannels. I'm going to debug this week to see why, I suspect it's either register related or Rockfall is hardcoded in some way. Although I've disassembled Rockfall, I can't see how it's playing the music, there's no Sound_* calls in the code. It appears to be using SoundChannels so there should be a Voice being installed.

I also need to find out why some games are silent on the Pi, Cannon Fodder for example works on the Pi, 2067BC however is silent. I've confirmed the Channel Handler is being called and the SoundDMA buffer is being filled...it just happens to be silence*. It appears the Channel Handler is not writing to the buffer as passed in R10/R12, what I need to establish is if it's a game Channel Handler or SoundChannels' Channel Handler that's not writing correctly.

One difference is the buffer size, Cannon Fodder will correctly fill any buffer size it's given, 2067BC is hardcoded to a specific buffer size. ADFFS takes account of this and asks the Channel Handler to fill a buffer size as requested by the game. To determine if there's enough samples to fill the SoundDMA buffer, ADFFS calculates the duration of what's in the circular buffer vs the SoundDMA buffer (ie buffer size * sample period), if there's enough it will upsample to the SoundDMA buffer at the current RISCOS sample rate. If there isn't enough samples it will repeatedly call the Channel Handler until there is, at most this means two calls to the Channel Handler.

I suspect it's this repeated calling that's causing the problem, it possibly needs to preserve some registers that aren't currently documented. For example, if R9 isn't passed from SoundDMA to SoundChannels you get silence, I can't find any documentation relating to how SoundDMA calls a Channel Handler, so I'm documenting it as I work it out, the PRM doesn't contain a lot of detail on the interaction between SoundDMA and the Channel Handler and SoundChannels appears to rely on undocumented registers passed by SoundDMA.

* This turned out to be a bug in the RISCOS 5 sound stack and was resolved in builds past 16-09-14
JonAbbott
Posts: 2938
Joined: Thu Apr 11, 2013 12:13 pm
Location: Essex
Contact:

Re: IOC device 9 (Sound Buffer Change)

Post by JonAbbott »

JonAbbott wrote:Rockfall is proving problematic, it hangs if ADFFS' doesn't pass through the original SoundDMA call to SoundChannels. I'm going to debug this week to see why, I suspect it's either register related or Rockfall is hardcoded in some way.
It's hardcoded. It checks if the sound buffer address passed in R12=1F06000 or 1F07000 and immediately exists if not :roll:, this causes the game to hang as it's relying on timing code in the Voice fill.

EDIT: It's now fixed up in the game's boot script, which turns off the buffer address check.
Post Reply