MODULE SoundDevices; (** AUTHOR "TF"; PURPOSE "Abstract sound device driver / Generic sound support"; *)
IMPORT
Plugins, Modules;
VAR
devices* : Plugins.Registry;
CONST
ResOK* = 0;
ResQualityReduced* = 1; (** ResReducedQuality can be the res value of OpenPlayChannel if the playing quality
of the resulting channel may be reduced due to insufficient ressources *)
ResNoMoreChannels* = 10;
ResUnsupportedFrequency* = 20;
ResUnsupportedSubChannels* = 30;
ResUnsupportedSamplingRes* = 40;
ResUnsupportedFormat* = 50;
(* Channel types *)
ChannelPlay* = 0;
ChannelRecord* = 1;
(** Currently only PCM defined *)
FormatPCM* = 0;
TYPE
(** a generic buffer. data^ contains the data to be played or space for the data recorded.
len contains the length of the buffer.
len MOD 4 must be 0
Buffers may be extended by the client application for buffer management. A buffer may only be reused if
it is returned to the BufferListener.
*)
Buffer* = OBJECT
VAR
len* : LONGINT;
data* : POINTER TO ARRAY OF CHAR;
END Buffer;
(* a blocking buffer pool that can be used by a sound playing process to synchronize sound generation *)
BufferPool* = OBJECT
VAR head, num: LONGINT; buffer: POINTER TO ARRAY OF Buffer;
PROCEDURE &Init*(n: LONGINT);
BEGIN
head := 0; num := 0; NEW(buffer, n)
END Init;
PROCEDURE Add*(x: Buffer);
BEGIN {EXCLUSIVE}
AWAIT(num # LEN(buffer));
buffer[(head+num) MOD LEN(buffer)] := x;
INC(num)
END Add;
PROCEDURE Remove*(): Buffer;
VAR x: Buffer;
BEGIN {EXCLUSIVE}
AWAIT(num # 0);
x := buffer[head];
head := (head+1) MOD LEN(buffer);
DEC(num);
RETURN x
END Remove;
PROCEDURE NofBuffers*(): LONGINT;
BEGIN {EXCLUSIVE}
RETURN num
END NofBuffers;
END BufferPool;
(** A buffer listener is called when a play / or record buffer is completed,
the application should reuse eg. process the buffer *)
BufferListener* = PROCEDURE { DELEGATE } (buffer : Buffer);
(** Generic MixerChannel object. Allows to set and get volume information *)
MixerChannel* = OBJECT
(** Return the name (as UTF-8 Unicode) of this channel *)
PROCEDURE GetName*(VAR name : ARRAY OF CHAR);
BEGIN HALT(99) (* abstract *)
END GetName;
(** Return the description string (as UTF-8 Unicode) of this channel *)
PROCEDURE GetDesc*(VAR desc : ARRAY OF CHAR);
BEGIN HALT(99) (* abstract *)
END GetDesc;
(** Set the volume of the channel *)
(** 0 is silent, 255 is max *)
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN HALT(99) (* abstract *)
END SetVolume;
(** Get the volume of the channel *)
(** 0 is silent, 255 is max *)
PROCEDURE GetVolume*() : LONGINT;
BEGIN HALT(99) (* abstract *)
END GetVolume;
(** mute or unmute the channel *)
PROCEDURE SetMute*(mute : BOOLEAN);
BEGIN HALT(99) (* abstract *)
END SetMute;
(** get the "mute - state" of the channel *)
PROCEDURE GetIsMute*() : BOOLEAN;
BEGIN HALT(99) (* abstract *)
END GetIsMute;
END MixerChannel;
(** a MixerChangedProc delegate is called whenever a channel (volume / mute) is changed *)
MixerChangedProc* = PROCEDURE { DELEGATE } (channel : MixerChannel);
(** Generic channel *)
Channel* = OBJECT
(** Return if the channel is ChannelPlay or ChannelRecord *)
PROCEDURE GetChannelKind*() : LONGINT;
END GetChannelKind;
(** Set the current volume of the channel *)
(** Volume is a 8.8 bit fix-point value, 0 is silent *)
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN HALT(99) (* abstract *)
END SetVolume;
(** Get the current volume of the channel *)
PROCEDURE GetVolume*() : LONGINT;
BEGIN HALT(99) (* abstract *)
END GetVolume;
(** GetPosition return the current position in samples. MAY CHANGE TO HUGEINT*)
PROCEDURE GetPosition*() : LONGINT;
BEGIN HALT(99) (* abstract *)
END GetPosition;
(** Register a delegate that handles reuse / processing of buffers. Only one Buffer listener can be registered
per channel *)
PROCEDURE RegisterBufferListener*(bufferListener : BufferListener);
BEGIN HALT(99) (* abstract *)
END RegisterBufferListener;
(** Start playing / recording *)
PROCEDURE Start*;
BEGIN HALT(99) (* abstract *)
END Start;
(** Queue another buffer for playing / recording *)
PROCEDURE QueueBuffer*(x : Buffer);
BEGIN HALT(99) (* abstract *)
END QueueBuffer;
(** Pause playing / recording, no buffers are returned *)
PROCEDURE Pause*;
BEGIN HALT(99) (* abstract *)
END Pause;
(** Stop the playing / recording and return all buffers *)
PROCEDURE Stop*;
BEGIN HALT(99) (* abstract *)
END Stop;
(** The channel is closed, the driver may release any ressources reserved for it. The object is still there
but can never be opened again*)
PROCEDURE Close*;
BEGIN HALT(99) (* abstract *)
END Close;
END Channel;
Driver* = OBJECT (Plugins.Plugin)
VAR
masterIn*, masterOut* : MixerChannel;
(** Generic functions *)
PROCEDURE Init*;
BEGIN
NEW(masterIn); NEW(masterOut)
END Init;
PROCEDURE Enable*;
END Enable;
PROCEDURE Disable*;
END Disable;
(** Capabilities *)
PROCEDURE NofNativeFrequencies*():LONGINT;
BEGIN HALT(99) (* abstract *)
END NofNativeFrequencies;
PROCEDURE GetNativeFrequency*(nr : LONGINT):LONGINT;
BEGIN HALT(99) (* abstract *)
END GetNativeFrequency;
PROCEDURE NofSamplingResolutions*():LONGINT;
BEGIN HALT(99) (* abstract *)
END NofSamplingResolutions;
PROCEDURE GetSamplingResolution*(nr : LONGINT):LONGINT;
BEGIN HALT(99) (* abstract *)
END GetSamplingResolution;
(** How many different sub channel settings are possible. Default implementation returns 2 for mono and stereo *)
PROCEDURE NofSubChannelSettings*():LONGINT;
BEGIN
RETURN 2
END NofSubChannelSettings;
(** Get sub channel setting nr. Default implementation returns mono and stereo *)
PROCEDURE GetSubChannelSetting*(nr : LONGINT):LONGINT;
BEGIN
IF nr = 0 THEN RETURN 1
ELSIF nr = 1 THEN RETURN 2
ELSE RETURN 1
END
END GetSubChannelSetting;
(** How many different wave formats are possible. Default implementation returns 1 *)
PROCEDURE NofWaveFormats*():LONGINT;
BEGIN
RETURN 1
END NofWaveFormats;
(** Get wave format nr. Default implementation returns FormatPCM *)
PROCEDURE GetWaveFormat*(nr : LONGINT):LONGINT;
BEGIN
RETURN FormatPCM
END GetWaveFormat;
(** Playing *)
(** Open a new channel for playing. If more than one channel is opened, the sound driver needs to mix the
channels in software or hardware, using the respective volumes. Sampling rate conversion must be done if needed.
The driver may respond with res = ResNoMoreChannels, if it can not open more channels. (The driver
SHOULD support more play channels (eg. 8 / 16 or more channels))
The driver can also respond with res = ResReducedQuality if the playback quality is reduced due to insufficient
ressources.
channel is the resulting Play channel, NIL if an error that prevents playing has occured.
(Applications only interested in the ability of playing and not in playback quality should only check for
channel # NIL and not for res = ResOk)
samplingRate is the desired samplingRate
samplingResolution = 8 / 16 / 24 / 32 (All drivers should support at least 8 and 16 bit)
nofSubChannes = 1 for Mono, 2 for Stereo, 4 for Quadro etc.
format is the wave format
*)
PROCEDURE OpenPlayChannel*(VAR channel : Channel; samplingRate, samplingResolution, nofSubChannels, format : LONGINT; VAR res : LONGINT);
END OpenPlayChannel;
(** Recording *)
(** Open a new channel for recording.
If more than one channel is opened, the sound driver copies the recorded data to all the recording
channels, using the respective volumes. Sampling rate conversion must be done if needed. Support for
multichannel recording is possible but NOT required. The driver may respond with res := ResNoMoreChannels, if
more than one recording channel is opened.
channel is the resulting Recorder channel, NIL if an error occured.
samplingRate is the desired samplingRate
samplingResolution = 8 / 16 / 24 / 32 (All drivers should support at least 8 and 16 bit)
nofSubChannes = 1 for Mono, 2 for Stereo, 4 for Quadro etc.
format is the wave format
*)
PROCEDURE OpenRecordChannel*(VAR channel : Channel; samplingRate, samplingResolution, nofSubChannels, format : LONGINT; VAR res : LONGINT);
END OpenRecordChannel;
(** Mixer *)
(** Register a listener for channel changes,
The number of listeners is not limited
*)
PROCEDURE RegisterMixerChangeListener*(mixChangedProc : MixerChangedProc);
END RegisterMixerChangeListener;
(** Unregister a previously registered channel change listener *)
PROCEDURE UnregisterMixerChangeListener*(mixChangedProc : MixerChangedProc);
END UnregisterMixerChangeListener;
(** Return channel object
channel 0 is always present and is specified as the master output volume
channel 1 is always present and is specified as the master input volume
Drivers may ignore channel 0 or 1 but need to return a generic "Channel" object for these channel numbers
GetMixerChannel returns NIL if the channelNr is invalid
*)
PROCEDURE GetMixerChannel*(channelNr : LONGINT; VAR channel : MixerChannel);
BEGIN
IF channelNr = 0 THEN channel := masterOut
ELSIF channelNr = 1 THEN channel := masterIn
ELSE channel := NIL
END
END GetMixerChannel;
(** Returns the number of mixer channels available, at least 2 *)
PROCEDURE GetNofMixerChannels*() : LONGINT;
BEGIN
RETURN 2
END GetNofMixerChannels;
END Driver;
(** Returns the default sound device. (Blocks until at least one sound device is installed *)
PROCEDURE GetDefaultDevice*() : Driver;
VAR p : Plugins.Plugin;
BEGIN
p := devices.Await("");
ASSERT(p IS Driver);
RETURN p(Driver)
END GetDefaultDevice;
PROCEDURE Cleanup;
BEGIN
Plugins.main.Remove(devices)
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup);
NEW(devices, "SoundDevices", "Sound drivers")
END SoundDevices.