610 lines
18 KiB
Groff
610 lines
18 KiB
Groff
.\" $NetBSD: audio.9,v 1.42 2008/04/30 13:10:58 martin Exp $
|
|
.\"
|
|
.\" Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
|
|
.\" All rights reserved.
|
|
.\"
|
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
|
.\" by Lennart Augustsson.
|
|
.\"
|
|
.\" Redistribution and use in source and binary forms, with or without
|
|
.\" modification, are permitted provided that the following conditions
|
|
.\" are met:
|
|
.\" 1. Redistributions of source code must retain the above copyright
|
|
.\" notice, this list of conditions and the following disclaimer.
|
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
|
.\" notice, this list of conditions and the following disclaimer in the
|
|
.\" documentation and/or other materials provided with the distribution.
|
|
.\"
|
|
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
.\" POSSIBILITY OF SUCH DAMAGE.
|
|
.\"
|
|
.Dd November 10, 2007
|
|
.Dt AUDIO 9
|
|
.Os
|
|
.Sh NAME
|
|
.Nm audio
|
|
.Nd interface between low and high level audio drivers
|
|
.Sh DESCRIPTION
|
|
The audio device driver is divided into a high level,
|
|
hardware independent layer, and a low level hardware
|
|
dependent layer.
|
|
The interface between these is the
|
|
.Va audio_hw_if
|
|
structure.
|
|
.Bd -literal
|
|
struct audio_hw_if {
|
|
int (*open)(void *, int);
|
|
void (*close)(void *);
|
|
int (*drain)(void *);
|
|
|
|
int (*query_encoding)(void *, struct audio_encoding *);
|
|
int (*set_params)(void *, int, int,
|
|
audio_params_t *, audio_params_t *,
|
|
stream_filter_list_t *, stream_filter_list_t *);
|
|
int (*round_blocksize)(void *, int, int, const audio_params_t *);
|
|
|
|
int (*commit_settings)(void *);
|
|
|
|
int (*init_output)(void *, void *, int);
|
|
int (*init_input)(void *, void *, int);
|
|
int (*start_output)(void *, void *, int, void (*)(void *),
|
|
void *);
|
|
int (*start_input)(void *, void *, int, void (*)(void *),
|
|
void *);
|
|
int (*halt_output)(void *);
|
|
int (*halt_input)(void *);
|
|
|
|
int (*speaker_ctl)(void *, int);
|
|
#define SPKR_ON 1
|
|
#define SPKR_OFF 0
|
|
|
|
int (*getdev)(void *, struct audio_device *);
|
|
int (*setfd)(void *, int);
|
|
|
|
int (*set_port)(void *, mixer_ctrl_t *);
|
|
int (*get_port)(void *, mixer_ctrl_t *);
|
|
|
|
int (*query_devinfo)(void *, mixer_devinfo_t *);
|
|
|
|
void *(*allocm)(void *, int, size_t, struct malloc_type *, int);
|
|
void (*freem)(void *, void *, struct malloc_type *);
|
|
size_t (*round_buffersize)(void *, int, size_t);
|
|
paddr_t (*mappage)(void *, void *, off_t, int);
|
|
|
|
int (*get_props)(void *);
|
|
|
|
int (*trigger_output)(void *, void *, void *, int,
|
|
void (*)(void *), void *, const audio_params_t *);
|
|
int (*trigger_input)(void *, void *, void *, int,
|
|
void (*)(void *), void *, const audio_params_t *);
|
|
int (*dev_ioctl)(void *, u_long, void *, int, struct lwp *);
|
|
int (*powerstate)(void *, int);
|
|
#define AUDIOPOWER_ON 1
|
|
#define AUDIOPOWER_OFF 0
|
|
};
|
|
|
|
typedef struct audio_params {
|
|
u_int sample_rate; /* sample rate */
|
|
u_int encoding; /* e.g. mu-law, linear, etc */
|
|
u_int precision; /* bits/subframe */
|
|
u_int validbits; /* valid bits in a subframe */
|
|
u_int channels; /* mono(1), stereo(2) */
|
|
} audio_params_t;
|
|
.Ed
|
|
.Pp
|
|
The high level audio driver attaches to the low level driver
|
|
when the latter calls
|
|
.Va audio_attach_mi .
|
|
This call should be
|
|
.Bd -literal
|
|
void
|
|
audio_attach_mi(ahwp, hdl, dev)
|
|
struct audio_hw_if *ahwp;
|
|
void *hdl;
|
|
struct device *dev;
|
|
.Ed
|
|
.Pp
|
|
The
|
|
.Va audio_hw_if
|
|
struct is as shown above.
|
|
The
|
|
.Va hdl
|
|
argument is a handle to some low level data structure.
|
|
It is sent as the first argument to all the functions
|
|
in
|
|
.Va audio_hw_if
|
|
when the high level driver calls them.
|
|
.Va dev
|
|
is the device struct for the hardware device.
|
|
.Pp
|
|
The upper layer of the audio driver allocates one buffer for playing
|
|
and one for recording.
|
|
It handles the buffering of data from the user processes in these.
|
|
The data is presented to the lower level in smaller chunks, called blocks.
|
|
If, during playback, there is no data available from the user process when
|
|
the hardware request another block a block of silence will be used instead.
|
|
Furthermore, if the user process does not read data quickly enough during
|
|
recording data will be thrown away.
|
|
.Pp
|
|
The fields of
|
|
.Va audio_hw_if
|
|
are described in some more detail below.
|
|
Some fields are optional and can be set to 0 if not needed.
|
|
.Bl -tag -width indent
|
|
.It Dv int open(void *hdl, int flags)
|
|
optional, is called when the audio device is opened.
|
|
It should initialize the hardware for I/O.
|
|
Every successful call to
|
|
.Va open
|
|
is matched by a call to
|
|
.Va close .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv void close(void *hdl)
|
|
optional, is called when the audio device is closed.
|
|
.It Dv int drain(void *hdl)
|
|
optional, is called before the device is closed or when
|
|
.Dv AUDIO_DRAIN
|
|
is called.
|
|
It should make sure that no samples remain in to be played that could
|
|
be lost when
|
|
.Va close
|
|
is called.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int query_encoding(void *hdl, struct audio_encoding *ae)
|
|
is used when
|
|
.Dv AUDIO_GETENC
|
|
is called.
|
|
It should fill the
|
|
.Va audio_encoding
|
|
structure and return 0 or, if there is no encoding with the
|
|
given number, return EINVAL.
|
|
.It Dv int set_params(void *hdl, int setmode, int usemode,
|
|
.Dv "audio_params_t *play, audio_params_t *rec,"
|
|
.Pp
|
|
.Dv "stream_filter_list_t *pfil, stream_filter_list_t *rfil)"
|
|
.Pp
|
|
Called to set the audio encoding mode.
|
|
.Va setmode
|
|
is a combination of the
|
|
.Dv AUMODE_RECORD
|
|
and
|
|
.Dv AUMODE_PLAY
|
|
flags to indicate which mode(s) are to be set.
|
|
.Va usemode
|
|
is also a combination of these flags, but indicates the current
|
|
mode of the device (i.e., the value of
|
|
.Va mode
|
|
in the
|
|
.Va audio_info
|
|
struct).
|
|
.Pp
|
|
The
|
|
.Va play
|
|
and
|
|
.Va rec
|
|
structures contain the encoding parameters that should be set.
|
|
The values of the structures may also be modified if the hardware
|
|
cannot be set to exactly the requested mode (e.g., if the requested
|
|
sampling rate is not supported, but one close enough is).
|
|
.Pp
|
|
If the hardware requires software assistance with some encoding
|
|
(e.g., it might be lacking mu-law support) it should fill the
|
|
.Va pfil
|
|
for playing or
|
|
.Va rfil
|
|
for recording with conversion information.
|
|
For example, if
|
|
.Va play
|
|
requests [8000Hz, mu-law, 8/8bit, 1ch] and the hardware does not
|
|
support 8bit mu-law, but 16bit slinear_le, the driver should call
|
|
.Dv pfil-\*[Gt]append()
|
|
with
|
|
.Va pfil ,
|
|
.Va mulaw_to_slinear16 ,
|
|
and audio_params_t representing [8000Hz, slinear_le, 16/16bit, 2ch].
|
|
If the driver needs multiple conversions, a conversion nearest to the
|
|
hardware should be set to the head of
|
|
.Va pfil
|
|
or
|
|
.Va rfil .
|
|
The definition of
|
|
.Dv stream_filter_list_t
|
|
follows:
|
|
.Bd -literal
|
|
typedef struct stream_filter_list {
|
|
void (*append)(struct stream_filter_list *,
|
|
stream_filter_factory_t,
|
|
const audio_params_t *);
|
|
void (*prepend)(struct stream_filter_list *,
|
|
stream_filter_factory_t,
|
|
const audio_params_t *);
|
|
void (*set)(struct stream_filter_list *, int,
|
|
stream_filter_factory_t,
|
|
const audio_params_t *);
|
|
int req_size;
|
|
struct stream_filter_req {
|
|
stream_filter_factory_t *factory;
|
|
audio_params_t param; /* from-param for recording,
|
|
to-param for playing */
|
|
} filters[AUDIO_MAX_FILTERS];
|
|
} stream_filter_list_t;
|
|
.Ed
|
|
.Pp
|
|
For playing,
|
|
.Va pfil
|
|
constructs conversions as follows:
|
|
.Bd -literal
|
|
(play) == write(2) input
|
|
| pfil-\*[Gt]filters[pfil-\*[Gt]req_size-1].factory
|
|
(pfil-\*[Gt]filters[pfil-\*[Gt]req_size-1].param)
|
|
| pfil-\*[Gt]filters[pfil-\*[Gt]req_size-2].factory
|
|
:
|
|
| pfil-\*[Gt]filters[1].factory
|
|
(pfil-\*[Gt]filters[1].param)
|
|
| pfil-\*[Gt]filters[0].factory
|
|
(pfil-\*[Gt]filters[0].param) == hardware input
|
|
.Ed
|
|
.Pp
|
|
For recording,
|
|
.Va rfil
|
|
constructs conversions as follows:
|
|
.Bd -literal
|
|
(rfil-\*[Gt]filters[0].param) == hardware output
|
|
| rfil-\*[Gt]filters[0].factory
|
|
(rfil-\*[Gt]filters[1].param)
|
|
| rfil-\*[Gt]filters[1].factory
|
|
:
|
|
| rfil-\*[Gt]filters[rfil-\*[Gt]req_size-2].factory
|
|
(rfil-\*[Gt]filters[rfil-\*[Gt]req_size-1].param)
|
|
| rfil-\*[Gt]filters[rfil-\*[Gt]req_size-1].factory
|
|
(rec) == read(2) output
|
|
.Ed
|
|
.Pp
|
|
If the device does not have the
|
|
.Dv AUDIO_PROP_INDEPENDENT
|
|
property the same value is passed in both
|
|
.Va play
|
|
and
|
|
.Va rec
|
|
and the encoding parameters from
|
|
.Va play
|
|
is copied into
|
|
.Va rec
|
|
after the call to
|
|
.Va set_params .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int round_blocksize(void *hdl, int bs, int mode,
|
|
.Dv "const audio_params_t *param)"
|
|
.Pp
|
|
optional, is called with the block size,
|
|
.Va bs ,
|
|
that has been computed by the upper layer,
|
|
.Va mode ,
|
|
.Dv AUMODE_PLAY
|
|
or
|
|
.Dv AUMODE_RECORD ,
|
|
and
|
|
.Va param ,
|
|
encoding parameters for the hardware.
|
|
It should return a block size, possibly changed according to the needs
|
|
of the hardware driver.
|
|
.It Dv int commit_settings(void *hdl)
|
|
optional, is called after all calls to
|
|
.Va set_params ,
|
|
and
|
|
.Va set_port ,
|
|
are done.
|
|
A hardware driver that needs to get the hardware in and out of command
|
|
mode for each change can save all the changes during previous calls and
|
|
do them all here.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int init_output(void *hdl, void *buffer, int size)
|
|
optional, is called before any output starts, but when the total
|
|
.Va size
|
|
of the output
|
|
.Va buffer
|
|
has been determined.
|
|
It can be used to initialize looping DMA for hardware that needs that.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int init_input(void *hdl, void *buffer, int size)
|
|
optional, is called before any input starts, but when the total
|
|
.Va size
|
|
of the input
|
|
.Va buffer
|
|
has been determined.
|
|
It can be used to initialize looping DMA for hardware that needs that.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int start_output(void *hdl, void *block, int blksize,
|
|
.Dv "void (*intr)(void*), void *intrarg)"
|
|
.Pp
|
|
is called to start the transfer of
|
|
.Va blksize
|
|
bytes from
|
|
.Va block
|
|
to the audio hardware.
|
|
The call should return when the data transfer has been initiated
|
|
(normally with DMA).
|
|
When the hardware is ready to accept more samples the function
|
|
.Va intr
|
|
should be called with the argument
|
|
.Va intrarg .
|
|
Calling
|
|
.Va intr
|
|
will normally initiate another call to
|
|
.Va start_output .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int start_input(void *hdl, void *block, int blksize,
|
|
.Dv "void (*intr)(void*), void *intrarg)"
|
|
.Pp
|
|
is called to start the transfer of
|
|
.Va blksize
|
|
bytes to
|
|
.Va block
|
|
from the audio hardware.
|
|
The call should return when the data transfer has been initiated
|
|
(normally with DMA).
|
|
When the hardware is ready to deliver more samples the function
|
|
.Va intr
|
|
should be called with the argument
|
|
.Va intrarg .
|
|
Calling
|
|
.Va intr
|
|
will normally initiate another call to
|
|
.Va start_input .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int halt_output(void *hdl)
|
|
is called to abort the output transfer (started by
|
|
.Va start_output )
|
|
in progress.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int halt_input(void *hdl)
|
|
is called to abort the input transfer (started by
|
|
.Va start_input )
|
|
in progress.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int speaker_ctl(void *hdl, int on)
|
|
optional, is called when a half duplex device changes between
|
|
playing and recording.
|
|
It can, e.g., be used to turn on
|
|
and off the speaker.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int getdev(void *hdl, struct audio_device *ret)
|
|
Should fill the
|
|
.Va audio_device
|
|
struct with relevant information about the driver.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int setfd(void *hdl, int fd)
|
|
optional, is called when
|
|
.Dv AUDIO_SETFD
|
|
is used, but only if the device has AUDIO_PROP_FULLDUPLEX set.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int set_port(void *hdl, mixer_ctrl_t *mc)
|
|
is called in when
|
|
.Dv AUDIO_MIXER_WRITE
|
|
is used.
|
|
It should take data from the
|
|
.Va mixer_ctrl_t
|
|
struct at set the corresponding mixer values.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int get_port(void *hdl, mixer_ctrl_t *mc)
|
|
is called in when
|
|
.Dv AUDIO_MIXER_READ
|
|
is used.
|
|
It should fill the
|
|
.Va mixer_ctrl_t
|
|
struct.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int query_devinfo(void *hdl, mixer_devinfo_t *di)
|
|
is called in when
|
|
.Dv AUDIO_MIXER_DEVINFO
|
|
is used.
|
|
It should fill the
|
|
.Va mixer_devinfo_t
|
|
struct.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv "void *allocm(void *hdl, int direction, size_t size, struct malloc_type *type, int flags)"
|
|
.Pp
|
|
optional, is called to allocate the device buffers.
|
|
If not present
|
|
.Xr malloc 9
|
|
is used instead (with the same arguments but the first two).
|
|
The reason for using a device dependent routine instead of
|
|
.Xr malloc 9
|
|
is that some buses need special allocation to do DMA.
|
|
Returns the address of the buffer, or 0 on failure.
|
|
.It Dv void freem(void *hdl, void *addr, struct malloc_type *type)
|
|
optional, is called to free memory allocated by
|
|
.Va alloc .
|
|
If not supplied
|
|
.Xr free 9
|
|
is used.
|
|
.It Dv size_t round_buffersize(void *hdl, int direction, size_t bufsize)
|
|
optional, is called at startup to determine the audio
|
|
buffer size.
|
|
The upper layer supplies the suggested size in
|
|
.Va bufsize ,
|
|
which the hardware driver can then change if needed.
|
|
E.g., DMA on the ISA bus cannot exceed 65536 bytes.
|
|
.It Dv "paddr_t mappage(void *hdl, void *addr, off_t offs, int prot)"
|
|
.Pp
|
|
optional, is called for
|
|
.Xr mmap 2 .
|
|
Should return the map value for the page at offset
|
|
.Va offs
|
|
from address
|
|
.Va addr
|
|
mapped with protection
|
|
.Va prot .
|
|
Returns -1 on failure, or a machine dependent opaque value
|
|
on success.
|
|
.It Dv int get_props(void *hdl)
|
|
Should return the device properties; i.e., a combination of
|
|
AUDIO_PROP_xxx.
|
|
.It Dv int trigger_output(void *hdl, void *start, void *end,
|
|
.Dv "int blksize, void (*intr)(void*), void *intrarg,"
|
|
.Pp
|
|
.Dv "const audio_params_t *param)"
|
|
.Pp
|
|
optional, is called to start the transfer of data from the circular buffer
|
|
delimited by
|
|
.Va start
|
|
and
|
|
.Va end
|
|
to the audio hardware, parameterized as in
|
|
.Va param .
|
|
The call should return when the data transfer has been initiated
|
|
(normally with DMA).
|
|
When the hardware is finished transferring each
|
|
.Va blksize
|
|
sized block, the function
|
|
.Va intr
|
|
should be called with the argument
|
|
.Va intrarg
|
|
(typically from the audio hardware interrupt service routine).
|
|
Once started the transfer may be stopped using
|
|
.Va halt_output .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int trigger_input(void *hdl, void *start, void *end,
|
|
.Dv "int blksize, void (*intr)(void*), void *intrarg,"
|
|
.Pp
|
|
.Dv "const audio_params_t *param)"
|
|
.Pp
|
|
optional, is called to start the transfer of data from the audio hardware,
|
|
parameterized as in
|
|
.Va param ,
|
|
to the circular buffer delimited by
|
|
.Va start
|
|
and
|
|
.Va end .
|
|
The call should return when the data transfer has been initiated
|
|
(normally with DMA).
|
|
When the hardware is finished transferring each
|
|
.Va blksize
|
|
sized block, the function
|
|
.Va intr
|
|
should be called with the argument
|
|
.Va intrarg
|
|
(typically from the audio hardware interrupt service routine).
|
|
Once started the transfer may be stopped using
|
|
.Va halt_input .
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int dev_ioctl(void *hdl, u_long cmd, void *addr,
|
|
.Pp
|
|
.Dv "int flag, struct lwp *l)"
|
|
.Pp
|
|
optional, is called when an
|
|
.Xr ioctl 2
|
|
is not recognized by the generic audio driver.
|
|
Return 0 on success, otherwise an error code.
|
|
.It Dv int powerstate(void *hdl, int state)
|
|
.Pp
|
|
optional, is called on the first open and last close of the audio
|
|
device.
|
|
.Va state
|
|
may be one of
|
|
.Dv AUDIOPOWER_ON
|
|
or
|
|
.Dv AUDIOPOWER_OFF .
|
|
Returns 0 on success, otherwise an error code.
|
|
.El
|
|
.Pp
|
|
The
|
|
.Va query_devinfo
|
|
method should define certain mixer controls for
|
|
.Dv AUDIO_SETINFO
|
|
to be able to change the port and gain,
|
|
and
|
|
.Dv AUDIO_GETINFO
|
|
to read them, as follows.
|
|
.Pp
|
|
If the record mixer is capable of input from more than one source,
|
|
it should define
|
|
.Dv AudioNsource
|
|
in class
|
|
.Dv AudioCrecord .
|
|
This mixer control should be of type
|
|
.Dv AUDIO_MIXER_ENUM
|
|
or
|
|
.Dv AUDIO_MIXER_SET
|
|
and enumerate the possible input sources.
|
|
Each of the named sources for which the recording level can be set
|
|
should have a control in the
|
|
.Dv AudioCrecord
|
|
class of type
|
|
.Dv AUDIO_MIXER_VALUE ,
|
|
except the
|
|
.Qq mixerout
|
|
source is special,
|
|
and will never have its own control.
|
|
Its selection signifies,
|
|
rather,
|
|
that various sources in class
|
|
.Dv AudioCrecord
|
|
will be combined and presented to the single recording output
|
|
in the same fashion that the sources of class
|
|
.Dv AudioCinputs
|
|
are combined and presented to the playback output(s).
|
|
If the overall recording level can be changed,
|
|
regardless of the input source,
|
|
then this control should be named
|
|
.Dv AudioNmaster
|
|
and be of class
|
|
.Dv AudioCrecord .
|
|
.Pp
|
|
Controls for various sources that affect only the playback output,
|
|
as opposed to recording,
|
|
should be in the
|
|
.Dv AudioCinputs
|
|
class,
|
|
as of course should any controls that affect both playback and recording.
|
|
.Pp
|
|
If the play
|
|
mixer is capable of output to more than one destination,
|
|
it should define
|
|
.Dv AudioNselect
|
|
in class
|
|
.Dv AudioCoutputs .
|
|
This mixer control should be of type
|
|
.Dv AUDIO_MIXER_ENUM
|
|
or
|
|
.Dv AUDIO_MIXER_SET
|
|
and enumerate the possible destinations.
|
|
For each of the named destinations for which the output level can be set,
|
|
there should be
|
|
a control in the
|
|
.Dv AudioCoutputs
|
|
class of type
|
|
.Dv AUDIO_MIXER_VALUE .
|
|
If the overall output level can be changed,
|
|
which is invariably the case,
|
|
then this control should be named
|
|
.Dv AudioNmaster
|
|
and be of class
|
|
.Dv AudioCoutputs .
|
|
.Pp
|
|
There's one additional source recognized specially by
|
|
.Dv AUDIO_SETINFO
|
|
and
|
|
.Dv AUDIO_GETINFO ,
|
|
to be presented as monitor_gain,
|
|
and that is a control named
|
|
.Dv AudioNmonitor ,
|
|
of class
|
|
.Dv AudioCmonitor .
|
|
.Sh SEE ALSO
|
|
.Xr audio 4
|
|
.Sh HISTORY
|
|
This
|
|
.Nm
|
|
interface first appeared in
|
|
.Nx 1.3 .
|