SharedSound Functional Specification |
| Distribution: | General Release |
| Title: | Shared Sound Functional Specification |
| Issue: | 1 |
| Author(s): | Andy Pierson |
| Robin Watts | |
| Date: | 03-Jun-2001 |
| Master Format: | HTML |
| Revision: | 1 |
| Last Issue: | 1, revision 1.0 |
All trademarks are acknowledged.
This document has appeared in several different forms, both as Acorn/Pace internal documentation and as an ESP document. Previous documents have often blurred the lines between what was intended, what was planned for future and what actually is implemented. This revised document is intended to supplant all previous documents by clearly defining what actually is implemented, and what is planned for the future.
The purpose of this module is summarised as follows:
SharedSound allows multiple sound handlers to output through one sound source.
The original intention of SharedSound was that it could be extended to provide a range of useful functions such as mixing, sample rate interpolation and volume scaling. Versions prior to version 1.0 never implemented these, and relied on clients to do the filling/mixing/interpolation as required.
While this technique avoided the memory bandwidth/CPU overheads that would have been caused by clients naively filling buffers and SharedSound merging them, it did force every SharedSound client to implement broadly the same set of fill routines.
As from version 1.0 of SharedSound, SharedSound now offers clients a set of standard fill routines that they can choose to use. This offers a number of benefits; clients are easier to write, less code is needlessly duplicated across components (with the consequent memory, cache and maintenance benefits this gives) and, not least, a degree of future proofing is given to extensions in the range of fill requests that can be made.
Some applications may choose not to call the SharedSound provided fillcode however; it may be advantageous for a handler to do its own mixing and volume scaling if it is already carrying out such functions as in the case of the MIDI Synthesiser. AMPlayer uses its custom fill routines as it also calculates the VU bar positions at this stage.
Some mixing is performed by SharedSound, however, to mix the 8 bit source and any immediate sound handlers with the call back handler buffer data.
Each client wishing to register with SharedSound, passes the address of a fill handler routine. SharedSound will then call this fill routine with appropriate register values which indicate the current state of play, in particular the current sample rate, along with associated values, as well as the current volume setting. The volume for a handler is calculated from the current system volume and the volume requested for the handler. This is passed as a Left and Right (Stereo) volume as two 16 bit words packed into a 32 bit word.
It was originally envisaged that the volume scaling would be dependent on the number of currently active handlers, but this has never been found to be necessary.
A public domain application !Mixer is available to control the relative volumes of the SharedSound sources within a given system. This reads that name and current volume of each handler and allows the user to set the volume of each handler.
This interface is already used from PCSound so that PC software designed to control the volumes of SoundBlaster devices actually controls the volumes levels for the PCSound handler installed in SharedSound.
SharedSound will typically sit on top of the current Sound_LinearHandler. The register values provided by the LinearHandler fill call are used as the basis for the parameters passed by SharedSound to the SharedSound handlers. In other words the fill buffer size, initial buffer status and current sample frequency are all derived from the LinearHandler.
The InstallDriver SWI has been implemented to allow SharedSound to sit on other sound output hardware devices such as the ESP DMI/PowerWave card.
Shared Sound never changes the system sample rate in response to new handlers (unless explicitly told to), but rather uses the rate currently set. If there in only one handler present or active (all other handlers paused) then it could change the system rate to suit the requests of the single handler.
Warm Silence Software Ltd have implemented a module, RateTracker, that monitors SharedSound and its registered handlers and sets the system sample rate to the highest rate currently in use. This would benefit from minor changes within SharedSound, or even being pulled into SharedSound.
As the possession of the Sound_LinearHandler (as with other handlers) is not in the control of SharedSound it is quite possible that SharedSound may lose control of the handler. To cope with this, SharedSound will check that it is in charge of the handler whenever it is called via its SWIs, and re-install if it has lost control. A specific SWI is provided which will check that SharedSound is still attached and this can be called by the handler if required.
The call back mechanism is designed to relieve pressure on interrupts by calling handlers in a state where interrupts can be enabled; to provide 'look ahead' so that data is available when the system is busy, to allow the soundDMA activity to overrun without crashing the computer and to provide overrun information to handlers so that they can take action to reduce CPU usage. Overrun detection and reporting is currently not implemented.
The current version of SharedSound provides three levels of handler activity. The first is an immediate handler. This is called when SharedSound is called by the SoundDMA and is therefore required to act with interrupts disabled and return as quickly as possible. Such handlers are likely to be simple 'fill buffer' handlers that do not require to process any data or a timing device that is using the Sound interrupt as a timer.
The second type of handler is a call back handler. This is called on a call back and can therefore operate with interrupts enabled and take more time to execute.
The third is a process handler. This is called in the same way as a call back handler but is called at the end of the call back process to allow it to add effects or process the current buffer in some way. Effects produced by the process handler therefore only apply to the callback handlers.
The call back buffer is set to 8 times the current SoundDMA buffer size and the handlers are called repeatedly to attempt to keep the call back buffer full. In other words, if there is sufficient CPU time then the call back buffer will have 8 buffers ready for adding to the SoundDMA buffer when called under interrupts.
When a handler is installed with SharedSound it can specify which sample rate it requires. SharedSound does not attempt to change the system rate but it does calculate the fractional stepping value required by the handler to achieve the rate required. If the system rate is changed by another application then SharedSound re-calculates the values for each handler and passes on both the new sample rate and the new fractional step to the handler on the next buffer fill.
Similarly, each handler's volume can be changed by an external application and the current volume is passed on to each handler.
SharedSound is currently used by the the Sound16 codecs for Replay, the PCSound support module, the MIDI Synthesiser, AMPlayer and many other applications.
/* Shared Sound SWI definitions */ #define SharedSound_base 0x4B440 // r0 r1 r2 r3 Status #define SharedSound_InstallHandler (Synth_base+0) // Handler Parameter Flags Name OK #define SharedSound_RemoveHandler (Synth_base+1) // Handler num OK #define SharedSound_HandlerInfo (Synth_base+2) // Handler num OK #define SharedSound_HandlerVolume (Synth_base+3) // Handler num Volume OK #define SharedSound_HandlerSampleType (Synth_base+4) // x #define SharedSound_HandlerPause (Synth_base+5) // x #define SharedSound_SampleRate (Synth_base+6) // Number Rate OK #define SharedSound_InstallDriver (Synth_base+7) // x #define SharedSound_RemoveDriver (Synth_base+8) // x #define SharedSound_DriverInfo (Synth_base+9) // x #define SharedSound_DriverVolume (Synth_base+10) // x #define SharedSound_DriverMixer (Synth_base+11) // x #define SharedSound_CheckDriver (Synth_base+12) // None OK #define SharedSound_ControlWord (Synth_base+13) // None OK #define SharedSound_HandlerType (Synth_base+14) // Handler num Type OK OK = implemented x = not Implemented
This SWI is used to install multiple Sound Handlers into the system.
Each handler is passed a buffer to fill with the registers set as follows:
| [R8,#0] | = | Flags word; currently 0. |
| [R8,#4] | = | Pointer to silence fill routine. |
| [R8,#8] | = | Pointer to data fill routine. |
This table may be extended in future to include more fill variants; it is anticipated that the presence/absence of these routines will be indicated by bits in the flag word.
The data fill routine is called in the following way:
The silence code is identical except that R4 and R5 are not used, and no data is read; the code fills/mixes zeros.
The silence/data fill code entry points understand all the currently defined flags in R3. The intention is that if future flags are added to R3 then the supplied fill routines will be extended to understand them too. This should provide a degree of forwards compatibility.
The overall sound fill code in a client can therefore look something like this (assuming the client is filling from a cyclic buffer):
fill
STMFD r13!,{r0-r2,r4-r12,r14}
MOV r12,r0
; We are filling from play_ptr to where?
LDR r4,play_ptr
LDR r5,fill_ptr
LDR r9,buffer_start
ADD r4,r4,r9
ADD r5,r5,r9
LDR r9,accumulator
; Are we wrapped, or unwrapped?
CMP r4,r5
BEQ out_of_data
BLO unwrapped
; |*****-------------------------********|
; ^fill ptr ^play ptr
LDR r5,buffer_end ; Fill from fill ptr to buffer_end
; Call the fill code
MOV R14,PC
LDR PC,[R8,#8]
; Did we run out of data?
CMP R4,R5 ;
LDRHS R4,buffer_start ; If r4 >= r5 then we used all the
; data in the buffer.
CMP R1,R2 ; Did we finish the fill?
BHS finish ; If r1 >= r2 then we finished.
LDR R5,fill_ptr ; No, so we ran out of data. Jump
CMP R5,#0 ; Check if there any data there -
BEQ out_of_data ; If not, play silence.
ADD R5,R5,R4
unwrapped
; |-----*************************--------|
; ^play ptr ^fill ptr
; Call the fill code
MOV R14,PC
LDR PC,[R8,#8]
; Either we finished the fill, or we ran out of data.
CMP R1,R2 ; Did we finish?
BEQ finish
out_of_data
; Fill with silence
MOV R14,PC
LDR PC,[R8,#4]
finish
LDR r3,buffer_start
STR R9,accumulator
SUB r4,r4,r3
STR R4,play_ptr
MOV r3,r0
LDMFD r13!,{r0-r2,r4-r12,PC}
This is to set the volume level for a particular handler. Shared sound can use this information to provide internal mixing or pass it on to the handler at each buffer fill.
This is to set the sample type for the data for a particular handler. It is intended that Shared sound will provide a buffer to the handler that is to be filled in this particular type. It will return the buffer fill type that it will be using. By default this will be Stereo, 16 bit Signed as in Sound Linear Handler.
It seems likely that this SWI will never now be implemented.
This is to turn a particular handler on or off without having to de-install it.
Its seems likely that this SWI will never now be implemented.
This SWI is used to set the sample rate used by a particular handler. Information is returned to indicate whether Shared Sound can provide this rate and if not what is the nearest rate it can provide. Along with this is a fractional step value to suggest how the handler can achieve it's required rate. Sample frequency is always supplied in 1/1024 Hz.
This is to allow other output devices to be installed.
This is to allow other output devices to be installed. You cannot uninstall the linear driver.
This provides information for a particular driver.
This sets the volume for a driver.
This sets or reads the mixer values for a driver, where relevant.
This is used to force SharedSound to make sure that current driver is still active. It is needed for situations where another application has taken control of the Sound Linear handler and note returned control to SharedSound.
This is used by the Replay Sound16 codecs as part of the multi-tasking Replay implementation. It provides a control word to both Replay and the Sound16 code allowing for a "1 audible handler of many" support.