| mp4-sa-> sfront reference manual-> audio drivers |
Sections
|
Audio Driver Functionsasys_osetup  asys_isetup  asys_iosetup  asys_preamble  asys_putbuf  asys_getbuf  asys_main  asys_orun   asys_iorun  ksyncinit  ksync  asys_oshutdown  asys_ishutdown  asys_ioshutdown  |
IntroductionIn this chapter, we describe how to add support for new audio file formats and audio hardware devices to sfront. Adding a new format or device involves writing an audio driver that is included in the C file sfront creates.
Users select the new audio driver through the
We begin the chapter by discussing the structure of an audio driver. We describe the functions that an audio driver declares to handle initialization, data movement, and synchronization. We also describe how to register a new driver with sfront, so that a command-line invocation results in the inclusion of the audio driver into the sa.c file. |
|
Driver StructureAn audio driver is a file containing a set of C functions. This file is embedded into the sfront executable during compilation, and is copied into the C program sfront creates. Look in sfront/src/lib/asys/ to see examples of audio driver files. The right panel shows the set of functions contained in an audio driver. In this chapter, we describe the semantics for these functions. Not all audio drivers use all of the functions listed on the right hand panel. Several factors determine the subset of functions that are in use:
Naming conventionsAudio drivers must be careful when defining functions, variables, and pre-processor symbols for its own use, to avoid name-space collisions. Audio drivers may define elements that begin with asysn_drivername_  and ASYS_DRIVERNAME_ , where drivername is the name of the audio driver file (sans extension) located in sfront/src/lib/asys/ If ASYS_KSYNC exists, elements that begin with sync_  and SYNC_  are also permissible. In addition, if ASYS_HASOUTPUT exists, elements that begin with asyso_ or ASYSO_ are permissible. If ASYS_HASINPUT exists, elements that begin with asysi_ or ASYSI_ are permissible. If both ASYS_HASINPUT and ASYS_HASOUTPUT exist, elements that begin with asysio_ or ASYSIO_ are permissible. |
Audio Driver FunctionsInitialization int asys_osetup(); int asys_isetup(); int asys_iosetup(); Passive Audio Output int asys_preamble(); int asys_putbuf(); Passive Audio Input int asys_getbuf(); Active Drivers /* declared by driver */ int asys_main(); /* called by driver */ int asys_orun(); int asys_iorun(); Synchronization int ksyncinit(); int ksync(); Shutdown int asys_oshutdown(); int asys_ishutdown(); int asys_ioshutdown(); |
Initialization
Users select audio input and output drivers through the
Depending on the selection, a particular audio driver may be required to supply audio input, audio output, or both audio input and audio output. An audio driver determines its role in an sa.c file by detecting if certain pre-processor symbols have been defined, using the pre-processor utilities  ifdef  or  defined . Specifically, if both ASYS_HASINPUT and ASYS_HASOUTPUT symbols are defined, an audio driver should perform audio input and audio output. If only one of these symbols is defined, the audio driver should only perform audio input (ASYS_HASINPUT defined) or audio output (ASYS_HASOUTPUT defined). Initialization functionsAn audio driver declares an initialization function, which is called at the start of program execution. The exact form of the initialization function depends on the status of ASYS_HASINPUT and ASYS_HASOUTPUT symbols; see the right panel for details. The initialization function should perform the initial setup needed for the audio file type or the audio hardware. If this setup is successful, the function should return ASYS_DONE, a pre-defined constant. If driver setup failed, the function should return ASYS_ERROR. The arguments passed by the initialization function describe the audio input and output environment; the argument list may include:
The exact argument list depends on the function type. Audio data typesThe audio driver states its data type preference as part of the registration procedure. This preference is coded in the osample and isample variables, which may take on the pre-defined constant values ASYS_SHORT (for 16-bit signed integers) or ASYS_FLOAT (for 32-bit floats). The pre-defined constant ASYS_OTYPENAME has the same value as osample, and the pre-defined constant ASYS_ITYPENAME has the same value as isample. The correct C keywords for the input and output datatypes are held in the symbols ASYS_OTYPE and ASYS_ITYPE, and may be used in variable declarations. Driver identity symbolsIn some situations, an audio driver may need to know the presence of other drivers active in the system. The symbols ASYS_OUTDRIVER_XX, ASYS_INDRIVER_YY, and CSYS_CDRIVER_ZZ are defined if an audio output driver, audio input driver, or a control driver is present in the system. The actual strings for XX, YY, and ZZ are the fully-capitalized versions of the driver names (either the full symbol following -cin, -ain, and -aout, or the extension for filename drivers). Run-time optionsAn audio driver may define command-line options. Users types these options when executing an sa.c program that contains the driver, to dynamically configure driver parameters (for example, setting a maximum file size). The right panel describes audio driver parameters in detail. |
Initalization Functions
The audio driver declares
one of three functions for
initialization, depending
on user requests. This
function will be called
once, at the start of
program execution.
If the user requests only
output driver service, the
symbol ASYS_HASOUTPUT is
defined, and the driver
should declare:
int asys_osetup(long srate,
long ochannels,
long osample,
char * oname,
long toption)
If the user requests only
input driver service, the
symbol ASYS_HASINPUT is
defined, and the driver
should declare:
int asys_isetup(long srate,
long ichannels,
long isample,
char * iname,
long toption)
If the user requests both
input and output driver
service, both ASYS_HASINPUT
and ASYS_HASOUTPUT are
defined, and the driver
should declare:
int asys_iosetup(long srate,
long ichannels,
long ochannels,
long isample,
long osample,
char * iname,
char * oname,
long toption)
These functions should return
ASYS_DONE if the driver is
able to operate, and ASYS_ERROR
if the initialization failed.
See left panel for description
of function parameters.
Audio Driver Parameters
An audio driver may parse the
command line of its sa.c file
it is contained in, by accessing:
int asys_argc;
char ** asys_argv
An audio driver may define its own
command-line flags, so that users
can configure drivers at runtime.
A driver must use this convention:
-asys_xxx[_yyy] [p1 p2]
where:
xxx is the driver name
yyy is the option name (if needed)
p# is a parameter for the
option. options can have
zero, one, or more than one
parameters. a parameter can
not start with a - (except
as the sign of a number)
and may not contain spaces.
For example, the linux driver
could define:
-asys_linux_size p1
as an option for setting the size
of the output data, where
-asys_linux_size 8bit
-asys_linux_size 16bit
are the legal parameter values.
|
Passive Audio OutputIn this section, we describe the functions asys_preamble and asys_putbuf (see right panel for function definitions). Passive audio drivers define these functions to handle audio output. A passive audio driver does not always define the audio output functions: in some cases, the user may only wish to perform audio input. Passive audio drivers should define the audio output functions only if the symbol ASYS_HASOUTPUT is defined. Basic operationThe asys_preamble function is called once, at the start of audio playback. The audio driver uses this call to allocate a buffer for audio data, whose location and size it returns to the calling program. The first call to asys_putbuf returns this buffer to the audio driver, filled with audio sample data. The audio driver outputs the audio data, and returns a memory buffer to be filled with more samples. This cycle repeats until the program ends. Starting SilenceAn audio output driver that controls a soundcard device should output a period of silence to the soundcard during the asys_preamble call. The silent period provides an extra margin of time that sfront may use to compute new sound samples without overruning the soundcard. The silent period also sets the latency of the system. The appropriate length to choose for the silence period depends on the application. Interactive applications demand latencies of a few milliseconds; audio playback applications work best with several hundred milliseconds of latency. The pre-defined symbol ASYS_LATENCYTYPE indicates the application type, taking on values ASYS_HIGHLATENCY (audio streaming) or ASYS_LOWLATENCY (real-time interaction). Audio drivers may use the application type to determine an appropriate latency value, or may use the suggested latency value ASYS_LATENCY, which is a floating-point constant with units of seconds. If the pre-defined symbol ASYS_USERLATENCY is set to 1, the value of ASYS_LATENCY is a user suggestion; if ASYS_USERLATENCY is set to 0, the value is a system default value. Buffer ManagementPassive audio output drivers set the nominal buffer size of an audio output operation, by choosing the return value of *osize in the asys_preamble and asys_putbuf functions. In a real-time system, the buffer size should be set to be a small integral fraction of the starting silence period. For example, if a silent period of 3 ms is chosen, a good choice for the buffer size is the amount of storage needed to hold 0.75 ms of audio. This approach maximizes the efficiency of sfront execution, while minimizing the risk that a single late buffer can overrun the system. Compute-Ahead IssuesIf an sa.c program is set up as a real-time system, the penalty for computing audio buffers too slowly is obvious: if the interval between asys_putbuf calls is too long, the audio buffer may underrun, producing clicks and glitches. However, computing audio buffers too quickly also brings problems. For interactive applications, computing ahead too quickly increases the latency between user action and sound output. For streaming applications, computing too far ahead results in too much memory usage to hold the future audio. To avoid these problems, real-time audio drivers should take steps to limit the compute-ahead of the program. One way to limit the compute-ahead is to allocate a fixed number of buffers during the asys_preamble call, whose combined audio time equals the starting silence period. Buffers from this pool are passed to asys_putbuf to be filled, then passed to the soundcard to be played, which returns them once the audio plays out. If no free buffers are available to return to asys_putbuf, the audio driver sleeps until the soundcard returns a buffer. This scheme limits the compute-ahead of the program, without the use of explicit timekeeping; it works best with soundcard interfaces such as OSS which maintain fixed buffer pools internally. An alternative way to limit the compute-ahead is to explicitly keep track of elapsed time inside the audio driver, using operating system time functions. Audio drivers that use this approach may find it easiest to perform compute-ahead monitoring in the ksync function, described in a later section of this chapter. |
asys_preamble
int asys_preamble(
ASYS_OTYPE * asys_obuf[]
long * osize
)
Called once, at the start of
audio playback. When called,
*asys_obuf will be set to NULL,
and *osize will be set to 0.
On return, *asys_obuf must
point to an array of ASYS_OTYPE,
and *osize must be set to the
number of elements in the array
to be filled with audio. The
*osize value must be evenly
divisible by the number of
audio output channels, and
must not be zero.
Return value should be ASYS_DONE
if preamble succeeded, ASYS_ERROR
to terminate the calling program.
asys_putbuf
int asys_putbuf(
ASYS_OTYPE * asys_obuf[]
long * osize
)
asys_obuf points to an array
of audio samples to send to
the audio output. It is
guaranteed to be filled on
channel-contiguous boundaries.
asys_obuf points to the buffer
returned by asys_putbuf() on
the previous call, or for the
first call to asys_putbuf(),
the buffer returned by
sys_preamble().
In most cases, *osize will be
identical to the requested
*osize from the last call.
In some cases (such as the
end of the orchestra) it may
be smaller (but never greater).
On return, *asys_obuf must point
to an array of ASYS_OTYPEs, and
*osize must be set to the number
of elements in the array to be
filled with audio. The *osize
value must be divisible by the
number of audio output channels,
and must not be zero.
Return value should be ASYS_DONE
if preamble succeeded, ASYS_ERROR
to terminate the calling program.
ASYS_TIMEOPTIONThe pre-defined constant ASYS_TIMEOPTION codes the temporal mode of the program. It may reflect the presence of the -render, -playback, or -timesync option on the sfront command line; if these flags do not appear on the command line, sfront chooses a default value. The -render option requests off-line file processing: ASYS_TIMEOPTION has the value ASYS_RENDER. The -playback and -timesync options requests real-time processing: ASYS_TIMEOPTION takes on the value ASYS_PLAYBACK or ASYS_TIMESYNC in this case. If an audio output driver that interfaces to a soundcard finds that -render is in effect, it is probably a user error: the program may ignore the request, or may force an exit via an ASYS_ERROR return during initialization. |
Passive Audio InputPassive audio input drivers route audio input data from a sound file or audio device into the input_bus of a SAOL program. The function asys_getbuf embodies the passive audio input driver. See the right panel for a complete description of this function. An audio driver should only define the asys_getbuf function if the ASYS_HASINPUT symbol is defined, indicating the driver was selected via the -ain sfront command-line argument. Buffer managementAs described on the right panel, the calling program does not request a specific number of audio samples, and does not provide a buffer to fill. Instead, the audio input driver is free to create an input buffering scheme that works well with the underlying file format or hardware device. Compute-Ahead IssuesIf an audio input driver supports a real-time input device, the asys_getbuf may be called before new input samples are ready. In this case, the audio input driver should not return control to the calling program until the new samples are ready. If possible, the driver should block while waiting for new data, so that other processes may run on the machine. Note that by holding control in the fashion, the audio input driver acts to prevent the SAOL program from computing ahead too far. As a result, real-time passive audio drivers usually do not need to provide explicit compute-ahead protection if both ASYS_HASINPUT and ASYS_HASOUTPUT are defined. |
asys_getbuf
int asys_getbuf(
ASYS_ITYPE * asys_ibuf[],
long * isize
);
The function asys_getbuf is
called when the sa.c program
needs new input_bus samples
in order to continue SAOL
processing.
On the first call to asys_getbuf,
*asys_ibuf will be set to NULL
and *isize will be set to 0. Upon
return, *asys_ibuf should point
to a buffer of *isize audio
samples, filled with data from
the input device. ASYS_ITYPE is
is the sample type indicated by
isample during initialization.
On subsequent calls to asys_getbuf,
*asys_ibuf and *isize hold the
values provided in the previous
call. Upon return, *asys_ibuf
should point to a buffer of fresh
audio samples, of size *isize.
The contents of the buffer that
asys_getbuf should not be changed
by the audio driver until the next
call to asys_getbuf.
An *isize return value of 0
indicates EOF.
asys_getbuf() should return
ASYS_DONE if it was possible
to process the request,
including the EOF case.
ASYS_ERROR should be returned
for non-EOF error conditions
(filesystem error, etc).
|
Active DriversThe passive audio driver concept is not a good match for a soundcard interface that uses callback semantics, such as MacOS X coreaudio. In a callback architecture, applications supply a callback function to the soundcard interface as part of the initialization process. The soundcard interface invokes the callback function whenever an audio data transfer is needed. To support callback semantics, sfront provides an active driver interface as an alternative to normal passive drivers. An audio driver identifies itself as passive or active as part of the registration procedure.
Users specify active drivers using the same
An active driver may be selected for input while a passive driver is selected for output, or vice versa; in addition, the same active driver type may be specified as the input and the output driver. However, two different different active drivers may not be selected for use at the same. asys_mainActive output drivers declare the single function asys_main, shown on the right panel. This function is called after the SAOL program is initialized, and after all asys_setup functions are called. Once asys_main is called, it is the responsibility of the active audio driver to control SAOL program execution; the driver exercises this control by calling a service function to compute audio samples. The audio driver exits the asys_main function once the SAOL program completes execution. We first discuss active audio drivers that interact with SAOL programs that do not use the input_bus. In this case, the symbol ASYS_ACTIVE_O is defined, and the service function asys_orun is provided to compute new audio samples. See the right panel for a full description of asys_orun. To support a callback system, an active audio driver would define a callback function for the soundcard interface to call. This callback function would in turn call asys_orun to generate the required number of audio samples on demand. In this approach, the asys_main function would go to sleep once the callback function was installed. The callback function would awaken the asys_main function once the SAOL program ended, so that the asys_main function could return control to the calling program. The callback function becomes aware of the end of the SAOL program by checking the return value of the asys_orun function, which returns ASYS_EXIT once the program ends. |
asys_mainvoid asys_main(void) This function is called after SAOL program initialization is complete, and after all asys_setup functions are called. When asys_main returns, asys_shutdown functions are called, and the program exits. asys_orun
int asys_orun(
ASYS_OTYPE obuf[],
long * osize)
asys_orun is a service
function, provided for use
by the active audio driver
if the SAOL program does
not use the input_bus. This
function exists if the
symbol ASYS_ACTIVE_O exists.
asys_orun takes the sample
buffer pointer obuf as an
argument, writes at most
the next *osize channel-
interleaved sample values
into the buffer, and returns.
The *osize value received by
asys_orun must correspond to
an integral number of audio
sample periods. The return
value of *osize is the actual
number of ASYS_OTYPE
samples written into obuf.
If the SAOL program is still
running, asys_orun returns
ASYS_DONE; if the SAOL program
has finished, asys_orun returns
ASYS_EXIT.
|
SAOL programs that use input_busIf the SAOL program uses the input_bus, sfront provides the asys_iorun service function for active drivers to use (see right panel for details). This function accepts two audio buffers: an input array to route to input_bus, and an output array to hold generated output_bus samples. An active audio driver senses the presence of the asys_iorun function by testing to see if the symbol ASYS_ACTIVE_IO is defined. If ASYS_ACTIVE_IO is defined, ASYS_ACTIVE_O is not defined, and asys_orun is not available. If an active audio driver is expected to provide audio input and audio output from its device, the symbols ASYS_HASINPUT and ASYS_HASOUTPUT are defined. In this case, the driver simply passes the audio input to asys_iorun as it becomes available, and routes audio output to its device each time asys_iorun returns. However, if ASYS_HASINPUT or ASYS_HASOUTPUT is not defined, the user has specified a passive driver for input or output. In this case, the active driver calls the passive driver functions, in keeping with the semantics of the audio passive driver interface. For example, if ASYS_HASINPUT is not defined, the active audio driver calls the asys_getbuf function, and passes the buffer returned by this function to asys_iorun. If ASYS_HASOUTPUT is not defined, the active audio driver calls asys_preamble to initialize the passive audio output driver, and then uses asys_putbuf to process the audio buffers returned by asys_iorun. Basic information about the passive driver, such as channel size and sample data format, may be discovered by examining pre-defined constants such as ASYS_OCHAN and ASYS_OTYPE. See the initialization section for a description of these constants. |
asys_iorun
int asys_iorun(
ASYS_ITYPE ibuf[],
long * isize,
ASYS_OTYPE obuf[],
long * osize)
asys_iorun is a service
function, provided for use
by the active audio driver
if the SAOL program uses
the input_bus. This
function exists if the
symbol ASYS_ACTIVE_IO exists.
asys_iorun takes two
audio sample buffers as
input.
The buffer pointer
ibuf supplies audio input
data to route to the SAOL
input_bus. it contains
*isize samples, which must
correspond to an integral
number of sample periods.
asys_iorun also takes
the sample buffer pointer
obuf as an argument. it
writes, at most, the next
*osize channel-interleaved
sample values into the
buffer, and returns.
asys_iorun handles any
combination of *isize and
*osize buffers well --
it computes new audio
samples until ibuf is
exhausted, obus is fill,
or the SAOL program ends.
If the SAOL program is still
running, asys_iorun returns
ASYS_DONE; if the SAOL program
has finished, asys_iorun returns
ASYS_EXIT.
|
SynchronizationSAOL programs may read the k-rate standard name cputime, to sense the real-time performance of the program. This floating-point variables has a range of 0.0 to 1.0, and codes CPU utilization as in percentage terms. A cputime value of 1.0 indicates the edge of real-time performance: any additional load may cause a buffer overrun. Audio drivers can take responsibility for estimating the cputime value seen by the SAOL program. A driver requests this task as part of the registration procedure; if this request is granted, the audio driver finds the symbol ASYS_KSYNC defined. See the right panel for details about the selection algorithm sfront uses for cputime duties. If ASYS_KSYNC is defined, the audio driver defines the ksync and ksyncinit functions (see right panel for details). The ksync function is called once per execution cycle, after all audio samples have been computed. The return value of ksync is the new estimate of cputime. The ksyncinit function is called once, right before SAOL program execution begins. Computing cputimeTo compute cputime, audio drivers estimate the real time used to compute the audio samples, and normalize by the total amount of audio generated during the cycle. The predefined floating-point constant KTIME indicates the length of audio generated each execution cycle, in units of seconds. Compute-ahead issuesAudio drivers may need to actively limit the length of audio compute-ahead during program execution (we discuss compute-ahead issues in detail in an earlier section of this chapter). The ksync function is a convenient place to limit compute-head time. To limit compute-ahead, the audio driver compares the elapsed real time with the computer audio time during each ksync invocation, and sleeps (or less preferably, spins) if the compute-ahead time exceeds the desired latency of the system. A user may explicitly request this type of active compute-head limit, by using the -timesync sfront command-line option. If this option is selected, the constant ASYS_TIMEOPTION has the value ASYS_TIMESYNC. |
Selection AlgorithmSfront allocates cputime calculation chores using the following algorithm. If offline render mode is in effect (ASYS_TIMEOPTION set to ASYS_RENDER), the cputime is always set to 0, and no audio driver is assigned cputime duties. Elsewise, the audio output driver has first priority for cputime calculation, the audio input driver has second priority, and a default UNIX-only algorithm for cputime calculation is used if neither audio driver requests cputime duties. ksyncinitvoid ksyncinit(void) This function is called at the start of SAOL program execution. Audio drivers may use this call to initialize global state variables. Audio driver state variables and functions related to synchronization should use names that begin with sync_ ksyncfloat ksync(void) This function is called at the end of the audio output creation phase of each execution cycle. The return value for this function is the cputime value presented to the SAOL program during the next execution cycle. It should be a number in the range [0.0, 1.0] |
ShutdownAn audio driver declares a shutdown function, which is called at the end of SAOL program execution. Audio drivers use the shutdown function to cleanly close audio files and hardware devices. The name of the shutdown function depends on the status of ASYS_HASOUTPUT and ASYS_HASINPUT, as described on the right panel. |
Shutdown FunctionsThe audio driver declares one of three functions for shutdown, depending on its role as an input driver, output driver, or input and output driver. If the user requests only output driver service, the symbol ASYS_HASOUTPUT is defined, and the driver should declare: void asys_oshutdown(void) If the user requests only input driver service, the symbol ASYS_HASINPUT is defined, and the driver should declare: void asys_ishutdown(void) If the user requests both input and output driver service, both ASYS_HASINPUT and ASYS_HASOUTPUT are defined, and the driver should declare: void asys_ioshutdown(void) These functions should do an orderly shutdown of the audio hardware or file processing operations. |
|
RegistrationThe right panel shows how to register your audio driver with the sfront sources. Registration is necessary in order for sfront to add your control driver flag to the permissible arguments to the -ain and -aout command line options. |
Step 1: Create Libraries[1] Place your audio driver foo in sfront/src/lib/asys/mydriver.c [2] cd sfront/src/lib [3] Edit Makefile, and add name of your control driver to the ASYS list. [4] Type "make". This will create: sfront/src/asyslib.c sfront/src/asyslib.h which includes an embedded version of your driver. Search in the files for your driver name to verify. [5] Whenever you change your driver code in sfront/src/lib/asys you will need to remake the asyslib.c file. Step 2: Edit sfront/src/audio.c
In sfront/src/audio.c, make
these additions, to add driver
"mydriver".
[1] Add the constant definition
#define ADRIVER_MYDRIVER
top the top of the file. Give it
the current numerical value of
ADRIVER_END, and then increase
the value of ADRIVER_END by 1.
[2] Add a printf line to the
function
void printaudiohelp(void)
that describes the mydriver
flags. This will be printed out
when "sfront -help" is invoked.
[3] Add if statements to the
aoutfilecheck and ainfilecheck
functions, to register the
driver. If the driver is an
active driver, add the lines
aoutflow = ACTIVE_FLOW;
to the aoutfilecheck addition
and add
ainflow = ACTIVE_FLOW;
to the ainfilecheck addition.
Also in ainfilecheck, set
ainlatency to indicate
interactive input (like a
microphone):
ainlatency = LOW_LATENCY_DRIVER;
or streaming input (like a
fileread):
ainlatency = HIGH_LATENCY_DRIVER;
[4] Add an entry for your driver
to:
makeaudiotypeout
makeaudiotypein
makeaoutsync
makeainsync
makeaouttimedefault
makeaintimedefault
makeainparams
if needed.
[5] Add your driver to:
void makeaudiodriver(int num)
case DRIVER_MYDRIVER:
makemydriver();
break;
This call actually does the code
insertion into the sa.c file: it
calls the function created in
asyslib.c from your driver file,
which is named makemydriver.
Step 3: Compile sfrontType "make" in sfront/src to compile sfront with your driver. During driver development, you should edit your original driver source in sfront/src/lib/asys/mydriver.c then to test first: cd sfront/src/lib/ make and then: cd sfront/src make |
SummaryFor now, this is the end of the sfront reference manual. Soon, I hope to add new chapters describing sfront and sa.c internals. Return to Table of Contents. |
|
| mp4-sa-> sfront reference manual-> adding drivers |