Showing changes from revision #3 to #4:
Added | Removed | Changed
+------------------------+ +---------------------+ | 8-bit sound generators | | SharedSound clients | +------------------------+ +---------------------+ ^ | | ^ | v v | | +---------------+ +-------------+ SWIs SharedSound_HandlerVolume and | | SoundChannels | | SharedSound | SharedSound_DriverVolume are | +---------------+ +-------------+ applied here (in well-behaved | | | clients) *Volume is applied v v here (in well- +----------+ behaved generators) | SoundDMA | <--- *SoundGain is applied here +----------+ | v +--------------+ | Hardware/HAL | <--- *MixVolume is applied here +--------------+
*SoundGain takes a value between 0 and 7, which is the gain to apply to sounds produced by the 8-bit sound system when the samples are being converted to 16-bit. Each step is +3dB, so *SoundGain 0 (the default) is +0dB and *SoundGain 7 is +21dB. Note that any value other than 0 may result in clipping – see the details of how the 8-bit audio is scaled and mixed in the “SoundDMA, again” section.
*Volume takes a value between 1 and 127, which is the gain that any well-behaved 8-bit sound generators should use when they generate sound. It influences the return values of SWI Sound_SoundLog, SWI Sound_LogScale, and it also controls the contents of the linear-to-log and log amplication tables that are available at offsets +8 and +12 of the sound channel handler block (available via SWI Sound_Configure).
*Volume works by subtracting ((127-Volume) x 2) directly from the original 8-bit sample, and clamping the result to zero. I don’t believe it’s described in the PRM, but the 8-bit log data is encoded as follows:
bit 0: sign (0=positive, 1=negative) bits 4-1: point on chord (0-15) bits 7-5: chord (0-7)
An 8-bit sample can be converted to linear using the following formula:
linear = (16 << chord) - 16) + (point << chord)
Then just multiply by +1 or -1 depending on the sign bit. This gives a range of +/-3952.
So what this means with regards to *Volume is that for every 16 you subtract from the base volume of 127, the linear output will be halved. I.e.:
Volume | Multiplier |
---|---|
127 | 1 |
127-16 | 1/2 |
127-32 | 1/4 |
127-48 | 1/8 |
127-64 | 1/16 |
127-80 | 1/32 |
127-96 | 1/64 |
127-112 | 1/128 |
The interaction of the lower 4 bits of the volume is a bit more convoluted, since although they operate on the “point on chord” value, if that value underflows it will result in the chord being decreased as well.
The *Volume value can also be controlled via the Sound_Volume SWI.
The mapping from the 3 bit SoundDefault CMOS value to a volume uses the following formula:
volume = sounddefault*18 + 1
i.e. it specifies the volume in increments of 18, with a base of 1.
Note that although the log-to-linear formula given above produces a value in the range +/-3952, SoundDMA scales this value up during the 16-bit conversion so that you get the following range per channel (assuming a *SoundGain of 0):
Active chanels | Range | Multiplier |
---|---|---|
1 channel | +/-32604 | 33/4 |
2 channels | +/-16302 | 33/8 |
4 channels | +/-8151 | 33/16 |
8 channels | +/-4075.5 | 33/32 |
This keeps everything comfortably within the +/-32767 range of a 16-bit number. The overall flow for 8-bit data is as follows:
Note that the scale factors used mean that with a *SoundGain of 0, clamping never occurs. Also note that due to the way the stereo scale positions work, you’ll only ever see that full +/-32604 range in use if you have all channels set to 100% left or 100% right.
It’s worth pointing out that on ARMv7 machines a different, NEON-optimised routine is used for the log-to-linear conversion. It has all the same features as the old ARM version, but uses slightly more accurate scaling when expanding the range and applying *SoundGain. At a *SoundGain of 0, the relevant ranges are:
Active chanels | Range | Multiplier |
---|---|---|
1 channel | +/-32758 | 1061/128 |
2 channels | +/-16379 | 1061/256 |
4 channels | +/-8189.5 | 1061/512 |
8 channels | +/-4094.75 | 1061/1024 |
Like the ARM version, clipping and removal of fractional bits is only performed during the final 16 bit conversion.
*MixVolume is the volume control provided by this module. The gain value (5th argument) is specified in 1/16dB units. This controls the gain applied in the hardware mixer, i.e. it applies to the final mix of 16-bit linear data that SoundDMA produces. And since the gain is applied in hardware, depending on which hardware you’re using there’ll be different limits in place, in terms of min/max gain, and how granular the controls are. This information can be read back by the SoundCtrl_ExamineMixer SWI ; . I’ve also listed what I believe are the right values for the ARMiniX later on.
SoundControl Note was that introduced the volume sliders in the sound setup plugin in Configure control the mixer settings that are managed byRISCSoundControl . OS This 5, is so different won’t to be mentioned in the old Arc/RiscPC plugin where the single volume slider controlled the SoundDefault RISC CMOS OS value. 3.5 Also PRMs one that of come the problems with the tools current CD. version It of also the plugin is that it doesn’t look show like you any were documentation the has zero made point is – each slider could have 0dB at a different position, the only way to find it onto is to hit the “Default” button.ROOL wiki yet. But there is some up-to-date documentation in CVS at http://www.riscosopen.org/viewer/view/castle/RiscOS/Sources/Audio/SoundCtrl/Docs/
Note that the volume sliders in the sound setup plugin in Configure control the mixer settings that are managed by SoundControl. This is different to the old Arc/RiscPC plugin where the single volume slider controlled the SoundDefault CMOS value. Also one of the problems with the current version of the plugin is that it doesn’t show you were the zero point is – each slider could have 0dB at a different position, the only way to find it is to hit the “Default” button.
In terms of the ARMiniX, it looks like the range is -30dB to +0dB on the “headphones” slider (with 2dB granularity), and -18dB to +24dB on the “system” slider (with 6dB granularity). Note that both of those sliders affect the final output from the system. The “system” gain will be applied first by the hardware, and then the “headphones” gain, as the data moves through the different parts of the audio chip.
SharedSound has the SharedSound has the SharedSound_HandlerVolume SWI for setting the volume of the left & right channels for a specific client, and SharedSound_DriverVolume? for setting the volume of the output as a whole. As with *Volume, it’s the client’s responsibility to obey the values. Judging by the implementation of the built-in buffer fill code, the volume values are simple multipliers applied to the 16-bit data, with a resolution of 1/65536. I.e. with both the handler and driver volume set to maximum, maximum (65535), you actually get a multiplier of 1/65534 65534/65536 due to the way the handler and driver volumes are combined into a 16-bit volume level that’s passed to the client/fill code.
Note that when SharedSound clients produce their output, they mix it directly into the buffer containing the 16-bit data produced by SoundDMA’s log-to-linear code. This means they must perform their own clipping on the values, and if there are lots of clients active then the multiple clipping stages could easily result in major corruption of the audio data.