# 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