
                                    AMPlayer
                                    --------


This is a module for playing Audio MPEG files through the computer sound
output. It will select the 8 or 16 bit output depending on the normal
configuration (and availability).


AMPlayer module
===============

This is the program that does the actual work. There are various reasons for
wanting to control the module directly instead of through the frontend:
1. You get better control, and can automate things using obey files, etc.
2. You may want to write your own frontend.
3. The fancy frontend uses memory and CPU time that might be better used
   elsewhere.

For these purposes, the module has several *commands and SWIs.

* Commands
==========

*AMPlay starts playing an Audio MPEG file. When -Q[ueue] is present, the file
will be played after the current one finishes, otherwise immediately.
When -C[ue] is present, the file will be started, but with the player in pause
mode.
Syntax: *AMPlay [-Queue|-Cue] <filename>

*AMStop stops the MPEG file playing
Syntax: *AMStop

*AMInfo prints information about the player module status, and about the file
playing, if any.
Syntax: *AMInfo

*AMVolume sets the playback volume. Range is 0 to 127, with 113 as initial
value.
Syntax: *AMVolume <number>

*AMLocate locates the specified point in the file currently playing/paused.
Syntax: *AMLocate <hours>[:<minutes>[:<seconds>]]


System variables
================

AMPlayer$Buffer$*
-----------------
You can set up the audio buffer size to use by setting one or more of these
variables. When the buffer is created, the size is determined by the best
match of the filename and one of these variables. The more characters that
match, the better, and the variable with the most will control the buffer
size.
The idea is that the best buffer size somewhat depends on the file access
speed. The faster the device, the smaller buffer you can get away with, and
the better response you will get from things like fast forward/rewind.
CDFS for example is very slow. Therefore, I have the following variables:
AMPlayer$Buffer$	20
AMPlayer$Buffer$SCSI	16
AMPlayer$Buffer$CDFS	40

The effect of this is, that CDFS in general will get size 40. SCSI is fast
and gets 16, and everything else (ADFS, RAMFS etc.) get 20.
It is possible (but not necessary in my case) to include part of the disc
name too (e.g. "AMPlayer$Buffer$ADFS::Winnie"), to make the buffer size
depend on the drive as well as the FS. Indeed, you can go further and
include some directory path as well, though that will probably be rarely
used (with softlinks perhaps?).

The size is given in "blocks". These are currently about 4.5K. The reason
for not giving it in Kbytes is, that it is highly likely that the buffer
requirement will change (decrease) in later versions, while still having
the same resulting buffer time. Giving it in blocks means you don't have
to change the value when the block size changes.


AMPlayer$FileBuffer
-------------------
If this variable is set, it is used to decide the size of the input buffer.
On slow filing systems, the sound may mute while fetching more data. Raising
the input buffer size will help. The size is given in kbytes. If the variable
doesn't exist, the default size is 32K.


AMPlayer$Volume
---------------
Setting this to some value between 0 and 127, will determine the default
volume when the module is later loaded. It only has effect before loading;
to change the volume afterwards, use *AMVolume.
If unset, volume 113 is used at startup.


SWI calls
=========

SWI AMPlayer_Play
-----------------
On entry	R0 = Flags
		     bit0	Queue only
		     bit1	Cue
		R1 ->Filename

On exit		All registers preserved

Bit0,1 of R0 determines the action:
0	Starts playing the file immediately, after stopping any other file
	currently playing.
1	Registers the filename as "next" file (see File info block below).
	The file will start when the current one finishes normally (i.e. not
	if it is stopped by an explicit call to AMPlayer_Stop or *AMStop).
	If there is no file currently playing, the behaviour is exactly as
	with bit0 of R0 clear.
2	Starts the file immediately, but frozen at frame 0 in pause mode.
	Use SWI AMPlayer_Pause to start playback (which will start with a
	very much shorter delay than if just using Play).
3	Currently undefined.

Note: There can only be one "next" file at any moment. The name registered
      can safely be changed to something else, just by calling this SWI again.
      It is possible to cancel the registered file by calling this SWI with
      bit0 of R0 set, and R1 pointing to a null string.

Note: Given sufficient playback buffer and medium access speed, the current
      file will continue seamlessly into the next one. This is good for live
      music, that has been divided into separate files.


SWI AMPlayer_Stop
-----------------
On entry	R0 = Flags
		     bit0	Cut to next file

On exit		All registers preserved

Stops playback. If bit0 of R0 is set, playback continues with a queued file
if any.


SWI AMPlayer_Pause
------------------
On entry	R0 = Flags
		     bit0	Continue

On exit		All registers preserved

Action is determined by bit0 of R0:
0	Halts playback immediately.
1	Continues playback immediately.

Pause mode may also be cancelled by stopping. If Stop is used to cut to the
next file, or if a different file is started, pause mode will continue to
be in effect, freezing the new file at time=0. This can be used to ensure
that playback starts at the instant of calling AMPlayer_Pause (as opposed to
calling AMPlayer_Play, which can have a delay while opening the file etc.)


SWI AMPlayer_Locate
-------------------
On entry	R0 = Flags
		R1 = Target time

On exit		All registers preserved

Locates the position of the target time, and continues playback (or pausing)
from there.
This has no effect unless the status is either Playing, Locating or Paused.
This may take some time, and the playback buffer may empty (which will mute
the sound).
The time given here corresponds to the elapsed time returned from
AMPlayer_Info below. This is true even when the elapsed time is wrong (see
below). So when, at time X, the Info call returns the wrong time Y, giving
time Y to this call will still start playing at the right time X.
Playback can only start on a frame boundary, so the resolution of the start
point is around 2 cs (for 128kbit/sec, 44.1kHz frames).


SWI AMPlayer_Info
-----------------
On entry	R0 = Flags

On exit		R0 = Player status
		Other registers depend on status, as follows:
			Status		Info
		R0=0	Dormant		None
		
		R0=1	Starting	R1 ->Filename
		
		R0=2	Locating	R1 ->Filename
					R2 ->File info block (see below)
					R3 ->Frame info block (see below)
					R4 = Target time
		
		R0=3	Playing		R1 ->Filename
					R2 ->File info block
					R3 ->Frame info block
		
		R0=4	Paused		R1 ->Filename
					R2 ->File info block
					R3 ->Frame info block
		
		R0=5	Stopping	R1 ->Filename
		
		R0=6	Changing	R1 ->New filename
		
		R0=7	Cueing		R1 ->Filename

Note: When locating, the current time can be read from the file info block,
      as it moves toward the target time returned in R4.
      
Note: The registers not mentioned above are preserved. This provides an easy
      way of determining the info returned, without decoding R0. In Basic:
      SYS "AMPlayer_Info",,"" TO ,Filename$,FIB%
      This will set Filename$ to either "" or the filename. Similarily, FIB%
      will be 0 if there is no info at this stage, or a pointer to it if there
      is.

Note: There is a brief period when the status might be returned as 2 or 3, but
      where there is no valid frame info block, because the first frame hasn't
      been read. In that case, R3 will be preserved, so the code mentioned
      above will still work.


SWI AMPlayer_Control
--------------------
On entry	R0 = Reason code
		Other registers depend on code:
		R0=0	Read/write main volume
			R1 = New volume (0-127), or -1 to read
			On exit:
			R1 = Old volume

		R0=1	Read/write size of audio buffer
			R1 = New size in bytes, or -1 to read
			On exit:
			R1 = Old size
			
			If the buffer isn't currently created, this controls
			how large it will be when it eventually is.
			If it exists, OS_ChangeDynamicArea is used to change
			the size. This may fail with an error, even if some
			of the job was done (this can happen when reducing the
			size, as the amount that can be released depends on
			what is currently being played).
			If it succeeds, the sound will be broken up at least
			once. So it is best to set the size while not playing.

		R0=2	Set stack check level
			R1 = New level (in words), or 0 for default
			
			When receiving a callback, the SVC stack depth is
			checked to see if the kernel is reasonably unthreaded.
			By using this call, you can control what is considered
			"reasonable". The default value is currently 64, i.e.
			if there are more than 64 words on the stack by the
			time of the callback, a new callback will be registered
			later instead. Setting this too low will cause the
			player to stall, and you can only stop it by killing
			the module.


Returned structures
===================

File info block
---------------
Offset	Meaning
0	Flags:
	bit0 set: Total time valid
	bit1 set: Elapsed time valid
	bit2 set: ID3 tag info pointers valid
	bit3 set: VU values valid
	bit4 set: Error message pointer valid
	bit5 set: Next filename pointer valid
	bit6-31 reserved
4	Buffer usage ratio in %
8	Projected total time in cs if bit0 of flags set (see below)
12	Time elapsed in cs if bit1 of flags set
16	Pointer to ID3 song title if bit2 of flags set (see below)
20	Pointer to ID3 artist if bit2 of flags set
24	Pointer to ID3 album name if bit2 of flags set
28	Pointer to ID3 year string if bit2 of flags set
32	Pointer to ID3 comment if bit2 of flags set
36	Left channel VU if bit3 set (see below)
40	Right channel VU if bit3 set
44	Main volume (0..127)
48	Pointer to most recent error/warning message if bit4 of flags set
52	Pointer to filename of the "next" file if bit 5 of R0 is set
56	ID3 genre (a number) if bit2 of flags set

Note: All the string-pointers are addresses of 0-terminated strings, which are
      to be read only. If you want to keep on referring to the same string,
      copy it into your own workspace. The ones pointed to can and will change
      according to the 'state of play'. This is not true for all of them, so
      don't rely on that either (i.e. call the SWI when you want to update
      your view of the current status). Also, it's important to check the
      appropriate flag bit before referring to any pointers. Not doing so can
      result in a program that seems to work, but fails in mysterious ways
      once in a while.

Note: The projected total time is correct if the current frame type (bitrate,
      etc.) stays constant until the end of the file. It will be wrong if
      either
      1. the file size is unknown, e.g. if playing a stream, or
      2. the frame type will later change in a way that alters the number of
         bytes per frame.
      None of the above exceptions are true in the vast majority of mpeg files.
      The first case is determined by the module, and bit0 of the flags will
      be clear. The second case cannot be known in advance, and it will also
      affect the elapsed time. No matter what happens, the time will always
      move forward, it just might not be counting centiseconds in these cases.

Note: Any of the ID3 tag strings may be null, indeed, the often are. This
      indicates that the tag field consists entirely of spaces.

Note: When the VU level is available, it is a number between 0 and 255. The
      value is from -42 to 0 dB, in 1/6th dB steps. The level is the peak of
      the average level since last calling this SWI.

Frame info block
----------------
Offset	Meaning
0	MPEG version as 3 ASCII chars and a 0 terminator, e.g. "2.0"
4	Layer type (1..3). 0 is unknown layer
8	Sampling frequency in Hz
12	Bitrate in kbit/sec
16	Mode
	0: Stereo
	1: Joint-stereo
	2: Dual channel
	3: Single channel
20	Number of channels
24	Flags
	bit0: Copyright
	bit1: Original
	bit2: CRC
28	Pointer to left channel DCT array
32	Pointer to right channel DCT array


Credits
=======

I have no documentation about the Audio MPEG format. So this module was
produced by porting several other free mpeg decoders, extracting the
necessary bits, then optimising it for RISC OS/ARM. The latter by rewriting
the algorithms in most cases. Therefore, there are concepts and code bits
by many different people left in this module. I will try to list the most
obvious "contributors", as I could certainly not have done it without access
to their work:

Michael Hipp & Oliver Fromme wrote mpg123, which was ported and used.
Tomislav Uzelac wrote "amp", similarily ported and learned from.
ISO MPEG Audio Subgroup Software Simulation Group, dist10 package was used
as the main source of information about the intended functionality, rather
than the actual implementation of it.

And thanks must go to the testers that put up with the early bugs
(in order of appearance)...
Gerph, Kira, unCiscy, JanM, Zhadnost, Cocodude, MoZes, Lennier, Xargle,
Ben, TwoFlower, Forrey, & Nick.

License
=======

I don't speak Legalese, so if any lawyers have trouble understanding the
following, please ask a grownup for help.

In its current form, this is free for copying and use by anyone, subject to
the following:
If you want to include any of this as part of a commercial product, you need
my permission.
It can be freely supplied with PD, Freeware, and similar programs, as long as
proper credit is given, and no money is charged for them in any way.

At some point, I will include the source, and this licence will probably be
replaced with the GPL, but that will not affect the status of previous
versions.


Contact
=======

Questions, etc. should be addressed to:

Thomas Olsson
tolsson@isa.dknet.dk
