THSound
=======
by:

Tony Houghton
271 Upper Weston Lane
Southampton
SO19 9HY

riscos@realh.co.uk
http://www.realh.co.uk

Version 2.21

THIS COPY OF !System IS NOT A COMPLETE STANDALONE VERSION. IT MUST BE
INSTALLED OVER THE TOP OF A WORKING !System.

This version is 32-bit compatible, but a 32-bit SharedCLibrary is not
required.

The purpose of THSound is twofold.

1. Its original set of SWIs allow many different samples to be played without
having to load each one as a module in its own right (often impractical due
to potential name clashes). All it does is take a pointer to some data in raw
8-bit VIDC format, or 8-bit signed linear (new) and use this to make a sound
voice. THSound is deliberately very basic for flexibility. It is up to your
program to manage memory for the sample data, and to attach the voice to
channels and play it using the standard Sound SWIs.

2. A new set of SWIs makes it easy to use the Replay codecs from your own
programs, either playing from memory or a file, so you can play Replay sound
files or industry standard RIFF Wave (.WAV) files.

I originally wrote THSound because I was writing a program which needed to
play a large number of samples, the data for these being embedded in large
files with other resources. I couldn't find a PD program which could do what
I wanted (other modules only seem to be able to load samples as files) so I
studied the PRM and THSound was born. Several years later I want to be able
to play 16-bit samples as well...

Conditions of use
-----------------
THSound is distributed under the terms of the GPL (see the file "COPYING").
Permission may be granted to distribute the module without source in
commercial products, provided it is sought.

Technical details
=================

Any errors generated by THSound (other than further OS calls it makes) will
have the error code &818700.

THSound contains three SWIs for sound sample handling; its SWI chunk is
&4B780 (officially registered with Acorn).

SWI's
-----

THSound_InstallSample		&4b780
Entry:	R0 = Address of sample (in VIDC 8-bit signed log format)
	R1 = Size of sample in bytes (word aligned)
	R2 = Slot to install in or 0 for next free slot (see
		Sound_InstallVoice)
Exit:	R0 = Sample's "handle" (pointer to workspace)
	R1 = Voice slot sample is installed in
	R2 = Voice slot (copy of R1)

It is recommended that sample data is kept in the RMA, using OS_Module for
memory management (Archimedes), or in a dynamic area (Risc PC). Having sample
data in application workspace could crash SoundDMA if the application is
paged out eg by Wimp_Poll while the sample is playing.

Each sample's voice is given the name THSVoice<hh> where <hh> is a unique hex
code.


THSound_RemoveSample		&4b781
Entry:	R0 = Sample's handle
Exit:	R0   Preserved

Use this to kill a THSound 8-bit sample, do not use Sound_RemoveVoice. The
sample's data is not freed, just its workspace (handle) allocated when it was
installed.


THSound_GetVoiceSlot		&4b782
Entry:	R0 = Sample's handle
Exit:	R0   Preserved
	R1 = Voice slot

Finds the sample's voice slot in case you haven't stored the value returned
by THSound_InstallVoice.


THSound_ChannelInUse		&4b783
Entry:	R0 = Channel number
Exit:	R0   Non-zero if a THSound sample is playing on specified channel.

New in version 1.10, remember to check the version if you use this SWI.

This reads data from the SCCB to test whether a THSound voice is
currently playing (it won't work for most other samples, because THSound
uses the SCCB in a slightly non-standard way). The necessary information
in the SCCB is invalid immediately after calling Sound_Control and may
wrongly show the channel not to be in use, so if you want to wait for a
channel to become free you should use something like:

  REM First wait for SCCB to be updated, showing channel is in use
  REPEAT
  SYS"THSound_ChannelInUse",1 TO inuse%
  UNTIL inuse%
  REM Now wait until channel is free
  REPEAT
  SYS"THSound_ChannelInUse",1 TO inuse%
  UNTIL NOT inuse%

The second loop can be after, say, a Wimp_Poll, but I suggest you put
the first loop immediately after a Sound_Control (or similar)
instruction.


New in version 1.20:

THSound_GetPollWord		&4b784
Entry:	R0 = Sample's handle
Exit:	R0   Preserved
	R1 = (Pointer to) poll word

This is more useful than THSound_ChannelInUse for multitasking programs.
Before playing a sample you can set the pollword to 0, and it will be
altered when the sample finishes playing or is interrupted by being detached
from its channel. Therefore you can use PollWordNonZero Wimp events to
receive notification.

If the sample finishes playing without being interrupted, -1 or &FFFFFFFF
will be written to the pollword, otherwise another non-zero value will be
written.

This SWI is now also used for Replay handles.


New in version 2.00:

THSound_InstallLinear		&4b785
Entry:	R0 = Address of sample (in 8-bit signed linear format)
	R1 = Size of sample in bytes (word aligned)
	R2 = Slot to install in or 0 for next free slot (see
		Sound_InstallVoice)
Exit:	R0 = Sample's "handle" (pointer to workspace)
	R1 = Voice slot sample is installed in
	R2 = Voice slot (copy of R1)

The same as THSound_InstallSample except that the sample data is signed
linear, useful for playing Armadeus samples.



The following SWIs are new to version 2.00 and act as a front-end to the
Replay codecs. Only one Replay file can be processed and played at a time.
Apart from THSound_RemoveReplay, which should be called when you've
finished with a sample, the process of playing a Replay sample consists of
calling each of the following SWIs in the order in which they're presented.
The SWIs must be used with care, because although they'll return cleanly with
an error in situations such as insufficient memory or a file not found, they
don't check that they're being called in the correct order and/or with
sensible parameters. See also THSound_GetPollWord.

The Replay codecs can only be used to play one file at a time. Playing a
second sample will interrupt the playing of a previous one. 8-bit samples
will continue to play, but their speed may be altered.

After any errors, the THSound Replay handle will be invalid and should not be
removed.


THSound_ReadReplayHeaders	&4b786
Entry:	R0 = Address of file loaded into memory
	R1 = Size of file
	R2 = Audio track number (usually 1)
Exit:	R0 = THSound Replay handle
	R1 = Codec name (string)
	R2 = Play rate (string represention of floating point value)
	R3 = Frames per second (integer)
	R4 = Frames per chunk (integer)
	R5 = Number of channels
	R6 = Multiplier
	R7 = Chunk size

Parses the file's headers, extracting useful information, some of which is
stored internally, some of which is returned. The play rate is returned as a
string, because it's a floating point value, and using FP code in C modules
is almost impossible, so you'll have to do some of the calculations yourself.
If < 256 it represents the sample period in micorseconds, otherwise it's the
frequency in Hz. Similarly, you'll need to save the fps, fpc and channels
values for later calculations.


THSound_ReadReplayFileHeaders	&4b787
Entry:	R0 = File path name
	R2 = Audio track number (usually 1)
Exit:	R0 = THSound Replay handle
	R1 = Codec name (string)
	R2 = Play rate (string represention of floating point value)
	R3 = Frames per second (integer)
	R4 = Frames per chunk (integer)
	R5 = Number of channels
	R6 = Multiplier

As THSound_ReadReplayHeaders, but works on the file itself rather than
loading it into memory.


THSound_RemoveReplay		&4b788
Entry:	R0 = THSound Replay handle
Exit:	R0   preserved

Clears the data associated with a THSound Replay handle, and any buffers
allocated by THSound, but not any allocated by the client.


THSound_PrepareReplayCodec	&4b789
Entry:	R0 = THSound Replay handle
	R1 = Codec address or 0
	R2 = "Mute" block address or 0
	R3 = Integer part of frequency
	R4 = Fractional part of frequency
	R5 = Quality

This loads the codec or tells THSound where it is, and starts the timing
check to find the system sound rate.

If R1 is zero, THSound will allocate memory for the codec and load it itself,
using the name found by THSound_ReadReplayHeaders or
THSound_ReadReplayFileHeaders. Or you can manage the memory and load the
codec yourself and set R1 to its address. In that case don't forget to ensure
StrongARM cache safety.

R2 gives you the same choice as R1. It's actually a control block, called
"mute" for historical reasons. If you allocate it yourself it must be 64
bytes.

R3 and R4 must be calculated from the play rate returned above:

If (play_rate < 256) Then play_rate = 1E6 / play_rate
R3 = Integer part of play_rate
R4 = Fractional part of play_rate in 24 bits ie
     (1 << 24) * (play_rate - R3)

R5 must be between 1 and 4 with 4 being the highest quality. It probably
doesn't make any difference at this stage, but it isn't well documented in
the Replay package. All modern machines should be able to handle the highest
quality without draining CPU power signifcantly.


THSound_SetUpReplayBuffers	&4b78a
Entry:	R0 = THSound Replay handle
	R1 = Sample buffer address or 0
	R2 = Size of sample buffer address
	R3 = File buffer address or 0
	R4 = File buffer size or 0

Whether you provide your own sample buffer or ask THSound to allocate it for
you, you must provide its size. This is calculated from values returned
earlier:

If (play_rate < 256) Then play_rate = 1E6 / play_rate
size = ((Int((fpc/fps * play_rate * channels * 1.01 * multiplier + 7) / 8) + 16)
       And Not(3)) * 2

You can set R3 to 0 and have THSound choose allocate it itself, or allocate
it yourself. This must be at least double the size of a chunk and at least as
large as the size calculated for R2 above, but not so large that the time
taken to load (half) that amount of data from a file in one go could be
disruptive.


THSound_CompleteReplayTimingCheck	&4b78b
Entry:	R0 = THSound Replay handle
Exit:	R1 = Performance hint

Completes the timing check started by THSound_PrepareReplayCodec. This could
take up to a second to complete.

If the performance hint is zero, then the codec doesn't use much CPU power,
otherwise it does and you might not want to use a high quality setting.


THSound_PlayReplay		&4b78c
Entry:	R0 = THSound Replay handle
	R1 = Quality

Plays a Replay sample after all the above calls have been completed.


THSound_StopReplay		&4b78d
Entry:	R0 = THSound Replay handle

Stops a Replay sample while it's playing. However, in my experience it's of
little use, because whenever I try to play a sample for a second time
without starting all over again from THSound_ReadReplay[File]Headers the
system tends to crash!


New in version 2.10
-------------------
The Replay codecs can be used to play other formats. The following SWIs can
be used in place of THSound_ReadReplay[File]Headers for raw data you provide.
For the value of fpc and fps in the above formula, just use 1. The multiplier
is the number of bits per sample.

THSound_PrepareGeneric		&4b78e
Entry:	R0 = Address of data in memory
	R1 = Size of data
	R2 = Unused
	R3 = Codec name
Exit:	R0 = THSound Replay handle
	R1 = Chunk size

THSound_PrepareGenericFile	&4b78f
Entry:	R0 = File name
	R1 = Size of data (because it may not go to EOF)
	R2 = Offset from start of file to sample data
	R3 = Codec name
Exit:	R0 = THSound Replay handle
	R1 = Chunk size


WAV files can now be played without MovieFS being loaded, although it may be
necessary to have MovieFS's codecs installed in ARMovie to support some
formats.

THSound_CheckWAV		&4b790
Entry:	R0 = Address of WAV in memory, must be word-aligned
	R1 = Total size of WAV
Exit:	R1 = Return code

THSound_CheckWAVFile		&4b791
Entry:	R0 = WAV filename
Exit:	R1 = Return code

Use these SWIs if you want to check whether THSound can play a WAV before
trying to initialise the process of playing it. The return codes are:

	0: This WAV is supported
	1: Should be supported, but codec is missing
	2: Too many channels
	3: Too many bits per sample
	4: Unknown compression format
	5: Not supported because it contains silent chunks
	6: Corrupt WAV or system error


THSound_ReadWAVChunks		&4b792
Entry:	R0 = Address of file loaded into memory
	R1 = Size of file
Exit:	R0 = THSound Replay handle
	R1 = Codec name (string)
	R2 = Play rate (string represention of floating point value)
	R3 = Frames per second (integer)
	R4 = Frames per chunk (integer)
	R5 = Number of channels
	R6 = Multiplier
	R7 = Chunk size

THSound_ReadWAVFileChunks	&4b793
Entry:	R0 = Filename
Exit:	R0 = THSound Replay handle
	R1 = Codec name (string)
	R2 = Play rate (string represention of floating point value)
	R3 = Frames per second (integer)
	R4 = Frames per chunk (integer)
	R5 = Number of channels
	R6 = Multiplier
	R7 = Chunk size

These do the same job as THSound_ReadReplay[File]Headers only for WAVs. After
using one of these to obtain a Replay handle, the procedure for playing the
sample etc is the same as for Replay.


THSound_Log			&4b794
Entry:	R0 = Message

Adds a message to THSound's log if enabled (see below), otherwise does
nothing. Handy for debugging. If you want the line terminated, have a LF
character on the end of the string.


Playing other formats
=====================
Other sound formats supported by MovieFS can be played as if they are
ARMovie files, provided MovieFS is loaded and they don't need a "Fetcher". If
MovieFS is installed, you should find a file called "AlienFiles" in
!ARMovie.Documents which contains details of how to get at a virtual ARMovie
file within a MovieFS image.


THPlay
======
THPlay is the front end to a short BASIC program (THPlay2) that can be used
to play a sample in the background. It must be given a sample file of format
DTTSamp, Armadeus, ARMovie or WAV, or a Data file of raw sample data in
8-bit VIDC format, using the -file parameter in its command line. THPlay
loads the sample into the RMA, starts playing it, then polls the Wimp until
it has finished playing or a time limit is reached. Then THPlay releases the
sample and its memory and quits.

Other parameters are optional and are:

Parameter	Default	Meaning
---------	-------	-------
-chan		1	Channel to play sample on
-vol		&17F	Volume to play sample at
-pitch		&2000	Pitch to play sample at
-maxtime	10	Maximum time in seconds before sample is killed

chan, vol and pitch are ignored for ARMovie and WAV files. pitch can be used
to override the value embedded in DTTSamp and Armadeus files.


Compiling
---------
This should be straightforward provided you have a typical Acorn C
installation. To enable logging for debugging, define MAKEDEBUG
(uncommenting it in the Makefile is the easiest way; then Stamp "log.h") and
when run it will create a file called "THSoundLog" in the current directory.

When using the Replay codecs, the buffers are checked and filled if necessary
every 10 cs. You can change this period by editing the top of the Makefile,
then Stamp "ticker32.s" (or "ticker26.s" for a 26-bit build) and "replay.c"
before recompiling. FWIW this is what changed between 2.10 and 2.11, formerly
the period was fixed at 1 cs. Now the period must be at least 2cs.
