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

Acknowledgements

All trademarks are acknowledged.

Contents

  1. Acknowledgements
  2. Contents
  3. Issue/revision history
  4. Introduction
  5. SWIs
    SharedSound_InstallHandler
    SharedSound_RemoveHandler
    SharedSound_HandlerInfo
    SharedSound_HandlerVolume
    SharedSound_HandlerSampleType
    SharedSound_HandlerPause
    SharedSound_SampleRate
    SharedSound_InstallDriver
    SharedSound_RemoveDriver
    SharedSound_DriverInfo
    SharedSound_DriverVolume
    SharedSound_DriverMixer
    SharedSound_CheckDriver
    SharedSound_ControlWord
    SharedSound_HandlerType

Issue / revision history

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.

Introduction

The purpose of this module is summarised as follows:

  1. To provide a well managed facility for shared use of digital sound output
  2. To provide a 16 bit linear facility for sound output independently from the hardware used to output this sound
  3. To provide good software support to potential users of digital sound output
  4. To provide a call back facility for CPU intensive handlers allowing the hardware drivers to operate with interrupts disabled.
  5. To give provision for effects and other processes to act on the sound buffers

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.

Implementation

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.

SWI Interface

/* 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

SharedSound_InstallHandler

This SWI is used to install multiple Sound Handlers into the system.

On entry
R0 = handler address
R1 = parameter to pass
R2 = flags
R3 = name of handler
If successful V flag is clear and R0 holds a handler number
If unsuccessful V flag is set and R0 points to a SWI error block.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

Flags
Only one flag is used in this version:
Bit 0 if set then use type in R4 otherwise set type to default.

Type
The handler types are defined as follows:
0 Immediate handler
1 Call back handler
2 Process handler

Use
Each handler is called with when the current driver is called if the handler is an immediate handler or on call back if the handler is either a call back or process handler. The handlers in each type are called in the order in which they were installed. Process handlers are called after call back handlers.

Each handler is passed a buffer to fill with the registers set as follows:

R0 = parameter
R1 = base address of buffer to fill
R2 = end of buffer
R3 = flags
Bit 0 If set then data must be mixed otherwise overwrite.
Bit 30 Set if the volume in R7 is not equal to 0xFFFFFFFF
Bit 31 Set if the volume in R7 is 0 (i.e. sound is muted)

All other bits are currently passed as 0, but it is anticipated that they may be used in the following way in future revisions of the module:
Bit 1 If set then use highest quality e.g. oversampling.
Bits 3-7 Overrun count
0 No overrun
1--30 Number of times buffer has been overrun
31 Buffer has been overrun by more than 30 times
Bit 29 Stereo. Reverse stereo if set.
R4 = sample frequency
R5 = sample period
R6 = fractional step as specified by SharedSound_SampleRate
R7 = LR volume
R8 = Pointer to table of helpful fill routines
R4, R5 and R6 are set by SharedSound to the current values as passed in from the lower level Sound Handler, thus may change between calls to the clients handler.

On exit
R3 contains flags as above, modified according to the action of the handler. The important requirement on exit is that bit 0 of R3 should be set if any data has been written into the buffer. In fact, SharedSound cannot currently cope with no data being filled, so zeros should always be written.

Helpful fill routines
On entry to the fill code, R8 points to a table in the following format:
[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:

On Entry:
R1 = Base address of buffer to be filled
R2 = End of the buffer to be filled
R3 = Flags (as on entry to the fill routine)
R4 = Base of buffer to fill from
R5 = End of buffer to fill from
R6 = Fractional step value
R7 = LR volume
R9 = Fractional accumulator

On Exit:
R0 = New flags (to be returned from fill handler in R3)
R1 = Next byte to be filled
R4 = Next byte to fill from
R9 = New Fractional accumulator
R2,R3,R5,R6,R7,R8,R12 preserved
All other registers corrupted.
The fill code will fill/mix from the given buffer to the output buffer. It will return either when the buffer is filled, or when it runs out of data.

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}

SharedSound_RemoveHandler

This SWI is used to remove a specific handler.

On entry
R0 = handler number
If successful V flag is clear and R0 holds a handler number
If unsuccessful V flag is set and R0 points to a SWI error block.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_HandlerInfo

This SWI is used to provide information on a specific handler.

On entry
R0 = handler number or 0 to read number of first handler.

On exit (if R0 = 0 on entry)
If successful V flag is clear and
R0 = number of next available handler or 0 for none
R1 - R5 corrupted
If unsuccessful V flag is set and R0 points to a SWI error block.

On exit (if R0 non-zero on entry)
If successful V flag is clear and
R0 = number of next available handler or 0 for none
R1 = Flags
R2 = name
R3 = Sample frequency
R4 = Sample type
R5 = Volume
If unsuccessful V flag is set and R0 points to a SWI error block.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_HandlerVolume

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.

On entry
R0 = Handler number
R1 = volume (bits 0-15 left channel, 16-31 right channel)

On exit
If successful V flag is clear and
R0 is preserved
R1 = current volume
If unsuccessful V flag is set and R0 points to a SWI error block.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_HandlerSampleType

Not implemented in this version.

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.

SharedSound_HandlerPause

Not implemented in this version.

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.

SharedSound_SampleRate

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.

On entry
R0 = >0 handler number
    = 0 to set the rate for the current output driver
R1 = sample frequency required

On exit
R1 = current sample frequency
R2 = current sample period
R3 = fractional step required to achieve this rate in 8.24 fixed point

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_InstallDriver

This is to allow other output devices to be installed.

On entry
R0 = 1 to install linear handler
    = 2 to install log handler
    or a pointer to a driver table address.
R1 = parameter to pass driver in R12 when called
R2 = flags
R3 = volume

On exit
All registers preserved.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_RemoveDriver

This is to allow other output devices to be installed. You cannot uninstall the linear driver.

On entry
R0 = 2 to deinstall log handler
    or a pointer to a driver table address.

On exit
All registers preserved.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_DriverInfo

This provides information for a particular driver.

On entry
R0 = Driver number

On exit
R0 = number of next available driver or 0 for none
R1 = flags
R2 = name
R3 = volume
R4 = Overrun count
All other registers preserved.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_DriverVolume

This sets the volume for a driver.

On entry
R0 = Driver number
R1 = new volume

On exit
R0 = current volume
All other registers preserved.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_DriverMixer

This sets or reads the mixer values for a driver, where relevant.

On entry
R0 = Driver number
R1 = function
0 = read
1 = set
R2 = mixer number
R3 = value (if set)

On exit
R0 preserved
R1 = number of mixers
R2 = value
R3 = name
All other registers preserved.

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_CheckDriver

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.

On entry:
none

On exit:
none

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_ControlWord

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.

On entry:
none

On exit:
R0 = Pointer to control word

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant

SharedSound_HandlerType

This is used to change the type of a handler from for example, and immediate handler to a call back handler.

On entry:
R0 = handler number
R1 = handler type (see Install handler for type number)

On exit:
none

Interrupts
Interrupts status is undefined
Fast interrupt are enabled

Processor mode
Processor is in SVC mode

Re-entrancy
SWI is not reentrant