Configuration:

- changed snd_config_get_id function to follow semantic of other get functions
  - added snd_config_test_id
  - added runtime pointer type (not persistent)
    - added snd_config_make_pointer, snd_config_set_pointer, snd_config_get_pointer
  - added type/contents checking for callback functions
    - changed 'void *private_data' to 'snd_config_t *private_data'
  - renamed card_strtype functions to card_driver
Control:
  - fixed passing parameters to snd_ctl_async
Async handlers:
  - added public snd_async_handler_get_signo function
Documentation:
  - moved all documentation to source files
This commit is contained in:
Jaroslav Kysela 2001-11-19 08:14:21 +00:00
parent ef035eacfe
commit c39882f602
52 changed files with 1573 additions and 1042 deletions

View file

@ -1032,7 +1032,9 @@ int main(int argc, char **argv)
}
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "host") == 0) {

View file

@ -1,220 +0,0 @@
/*! \page conf Configuration files
<P>Configuration files are using a simple format allowing the modern
data description like nesting and array assignments.</P>
\section conf_whitespace Whitespace
Whitespace is the collective name given to spaces (blanks), horizontal and
vertical tabs, newline characters, and comments. Whitespace can serve to
indicate where configuration tokens start and end, but beyond this function,
any surplus whitespace is discarded. For example, the two sequences
\code
a 1 b 2
\endcode
and
\code
a 1
b 2
\endcode
are lexically equivalent and parse identically to give the four tokens:
\code
a
1
b
2
\endcode
The ASCII characters representing whitespace can occur within literal
strings, int which case they are protected from the normal parsing process
(tey remain as part of the string). For example:
\code
name "John Smith"
\endcode
parses to two tokens, including the single literal-string token "John
Smith".
\section conf_linesplicing Line splicing with \
A special case occurs, if the final newline character encountered is
preceded by a backslash (\) in the string value definition. The backslash
and new line are both discarded, allowing two physical lines of text to be
treated as one unit.
\code
"John \
Smith"
\endcode
is parsed as "John Smith".
\section conf_comments Comments
A single-line comments are defined using character #. The comment can start
in any position, and extends until the next new line.
\code
a 1 # this is a comment
\endcode
\section conf_include Include another configuration file
A new configuration file can be included using <filename> syntax. The global
configuration directory can be referenced using <confdir:filename> syntax.
\code
</etc/alsa1.conf>
<confdir:pcm/surround.conf>
\endcode
\section conf_punctuators Punctuators
The configuration punctuators (also known as separators) are:
\code
{} [] , ; = . ' " new-line form-feed carriage-return whitespace
\endcode
\subsection conf_braces Braces
Open and close braces { } indicate the start and end of a compound
statement:
\code
a {
b 1
}
\endcode
\subsection conf_brackets Brackets
Open and close brackets indicate single array definition. The identificators
are automatically generated starting with zero.
\code
a [
"first"
"second"
]
\endcode
Above code is equal to
\code
a.0 "first"
a.1 "second"
\endcode
\subsection conf_comma_semicolon Comma and semicolon
The comma (,) or semicolon (;) can separate the value assignments. It is not
strictly required to use these separators, because any whitespace supplies
them.
\code
a 1;
b 1,
\endcode
\subsection conf_equal Equal sign
The equal sign (=) separates can separate variable declarations from
initialization lists:
\code
a=1
b=2
\endcode
Using the equal signs is not required, because any whitespace supplies
them.
\section conf_assigns Assigns
The configuration file defines id (key) and value pairs. The id (key) can be
composed from any ASCII digits or chars from a to z or A to Z, including
char _. The value can be either a string, integer or real number.
\subsection conf_single Single assign
\code
a 1 # is equal to
a=1 # is equal to
a=1; # is equal to
a 1,
\endcode
\subsection conf_compound Compound assign (definition using braces)
\code
a {
b = 1
}
a={
b 1,
}
\endcode
\section conf_compound1 Compound assign (one key definition)
\code
a.b 1
a.b=1
\endcode
\subsection conf_array Array assign (definition using brackets)
\code
a [
"first"
"second"
]
\endcode
\subsection conf_array1 Array assign (one key definition)
\code
a.0 "first"
a.1 "second"
\endcode
\section conf_summary Summary
\code
# Configuration file syntax
# Include a new configuration file
<filename>
# Simple assign
name [=] value [,|;]
# Compound assign (first style)
name [=] {
name1 [=] value [,|;]
...
}
# Compound assign (second style)
name.name1 [=] value [,|;]
# Array assign (first style)
name [
value0 [,|;]
value1 [,|;]
...
]
# Array assign (second style)
name.0 [=] value0 [,|;]
name.1 [=] value1 [,|;]
\endcode
*/

View file

@ -1,56 +0,0 @@
/*! \page confarg Configuration - runtime arguments
<P>The ALSA library can accept runtime arguments for some configuration
blocks. This extension is on top of the basic syntax of the configuration
files.<P>
\section confarg_define Defining arguments
Arguments are specified by id (key) @args and array values containing
the string names of arguments:
\code
@args [ CARD ] # or
@args.0 CARD
\endcode
\section confarg_type Defining argument type and default value
Arguments type is specified by id (key) @args and argument name. The type
and default value is specified in the compound:
\code
@args.CARD {
type string
default "abcd"
}
\endcode
\section confarg_refer Refering argument
Arguments are refered by dollar-sign ($) and name of argument:
\code
card $CARD
\endcode
\section confarg_example Example
\code
pcm.demo {
@args [ CARD DEVICE ]
@args.CARD {
type string
default "supersonic"
}
@args.DEVICE {
type integer
default 0
}
type hw
card $CARD
device $DEVICE
}
\endcode
*/

View file

@ -1,106 +0,0 @@
/*! \page conffunc Configuration - runtime functions
<P>The ALSA library accepts the runtime modification of configuration.
The several build-in functions are available.</P>
<P>The function is refered using id @func and function name. All other
values in the current compound are used as configuration for the function.
If compound func.<function_name> is defined in the root leafs, then library
and function from this compound configuration is used, otherwise the prefix
'snd_func_' is added to string and the code from the ALSA library is used.
The definition of function looks like:</P>
\code
func.remove_first_char {
lib "/usr/lib/libasoundextend.so"
func "extend_remove_first_char"
}
\endcode
\section conffunc_getenv The getenv function
The getenv function allows to get an environment value. The vars values
(array) defined the order and names for the environment values. When the
first environment value is found, then the function replaces the whole
compound by this result. If no value is found, then the default value is
used, if defined.
\code
card {
@func getenv
vars [ MY_CARD CARD C ]
default 0
}
\endcode
\section conffunc_igetenv The igetenv function
This function is same as getenv function, but the result value is converted
to integer.
\section conffunc_concat The concat function
The concat function merges all given string in the array named string into
one.
\code
filename {
@func concat
strings [
"/usr/share"
"/sound"
"/a.wav"
]
}
\endcode
\section conffunc_datadir The datadir function
This function return the configuration data directory (usually /usr/share/alsa)
as string as result. This function requires no other values.
\section conffunc_refer The refer function
This function substitutes the current compound with the compound named (key
name, value string) and filename (key file - optional, value string).
\code
{
@func refer
file /etc/my-alsa.conf
name pcm.lastone
}
\endcode
\section conffunc_card_strtype The card_strtype function
This function converts the given card number (key card, value integer) to card type
(string).
\section conffunc_card_id The card_id function
This function returns the card id string for the given card number (key card, value
integer).
\section conffunc_pcm_id The pcm_id function
This function returns the pcm id string for the given PCM device (key card,
value integer; key device, value integer; key subdevice (optional), value
integer).
\section conffunc_private_string The private_string function
This function returns the private data as string as result.
\section conffunc_private_card_strtype The private_card_strtype function
This function converts the private data (int) with card number to card type
(string).
\section conffunc_private_pcm_subdevice The private_pcm_subdevice function
This functions returns the subdevice number for the pcm handle specified by
the private data.
*/

View file

@ -5,8 +5,7 @@ GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = index.doxygen conf.doxygen confarg.doxygen \
conffunc.doxygen pcm.doxygen \
INPUT = index.doxygen \
../include/asoundlib.h \
../include/version.h \
../include/global.h \

View file

@ -1,441 +0,0 @@
/*! \page pcm PCM (digital audio) interface
<P>Although abbreviation PCM stands for Pulse Code Modulation, we are
understanding it as general digital audio processing with volume samples
generated in continuous time periods.</P>
<P>Digital audio is the most commonly used method of representing
sound inside a computer. In this method sound is stored as a sequence of
samples taken from the audio signal using constant time intervals.
A sample represents volume of the signal at the moment when it
was measured. In uncompressed digital audio each sample require one
or more bytes of storage. The number of bytes required depends on number
of channels (mono, stereo) and sample format (8 or 16 bits, mu-Law, etc.).
The length of this interval determines the sampling rate. Commonly used
sampling rates are between 8kHz (telephone quality) and
48kHz (DAT tapes).</P>
<P>The physical devices used in digital audio are called the
ADC (Analog to Digital Converter) and DAC (Digital to Analog Converter).
A device containing both ADC and DAC is commonly known as a codec.
The codec device used in a Sound Blaster cards is called a DSP which
is somewhat misleading since DSP also stands for Digital Signal Processor
(the SB DSP chip is very limited when compared to "true" DSP chips).</P>
<P>Sampling parameters affect the quality of sound which can be
reproduced from the recorded signal. The most fundamental parameter
is sampling rate which limits the highest frequency that can be stored.
It is well known (Nyquist's Sampling Theorem) that the highest frequency
that can be stored in a sampled signal is at most 1/2 of the sampling
frequency. For example, an 8 kHz sampling rate permits the recording of
a signal in which the highest frequency is less than 4 kHz. Higher frequency
signals must be filtered out before feeding them to ADC.</P>
<P>Sample encoding limits the dynamic range of a recorded signal
(difference between the faintest and the loudest signal that can be
recorded). In theory the maximum dynamic range of signal is number_of_bits *
6dB. This means that 8 bits sampling resolution gives dynamic range of
48dB and 16 bit resolution gives 96dB.</P>
<P>Quality has price. The number of bytes required to store an audio
sequence depends on sampling rate, number of channels and sampling
resolution. For example just 8000 bytes of memory is required to store
one second of sound using 8kHz/8 bits/mono but 48kHz/16bit/stereo takes
192 kilobytes. A 64 kbps ISDN channel is required to transfer a
8kHz/8bit/mono audio stream in real time, and about 1.5Mbps is required
for DAT quality (48kHz/16bit/stereo). On the other hand it is possible
to store just 5.46 seconds of sound in a megabyte of memory when using
48kHz/16bit/stereo sampling. With 8kHz/8bits/mono it is possible to store
131 seconds of sound using the same amount of memory. It is possible
to reduce memory and communication costs by compressing the recorded
signal but this is beyond the scope of this document. </P>
\section pcm_general_overview General overview
ALSA uses the ring buffer to store outgoing (playback) and incoming (capture,
record) samples. There are two pointers being mantained to allow
a precise communication between application and device pointing to current
processed sample by hardware and last processed sample by application.
The modern audio chips allow to program the transfer time periods.
It means that the stream of samples is divided to small chunks. Device
acknowledges to application when the transfer of a chunk is complete.
\section pcm_transfer Transfer methods in unix environments
In the unix environment, data chunk acknowledges are received via standard I/O
calls or event waiting routines (poll or select function). To accomplish
this list, the asynchronous notification of acknowledges should be listed
here. The ALSA implementation for these methods is described in
the \ref alsa_transfers section.
\subsection pcm_transfer_io Standard I/O transfers
The standard I/O transfers are using the read (see 'man 2 read') and write
(see 'man 2 write') C functions. There are two basic behaviours of these
functions - blocked and non-blocked (see the O_NONBLOCK flag for the
standard C open function - see 'man 2 open'). In non-blocked behaviour,
these I/O functions never stops, they return -EAGAIN error code, when no
data can be transferred (the ring buffer is full in our case). In blocked
behaviour, these I/O functions stop and wait until there is a room in the
ring buffer (playback) or until there are a new samples (capture). The ALSA
implementation can be found in the \ref alsa_pcm_rw section.
\subsection pcm_transfer_event Event waiting routines
The poll or select functions (see 'man 2 poll' or 'man 2 select' for further
details) allows to receive requests/events from the device while
an application is waiting on events from other sources (like keyboard, screen,
network etc.), too. The select function is old and deprecated in modern
applications, so the ALSA library does not support it. The implemented
transfer routines can be found in the \ref alsa_transfers section.
\subsection pcm_transfer_async Asynchronous notification
ALSA driver and library knows to handle the asynchronous notifications over
the SIGIO signal. This signal allows to interrupt application and transfer
data in the signal handler. For further details see the sigaction function
('man 2 sigaction'). The section \ref pcm_async describes the ALSA API for
this extension. The implemented transfer routines can be found in the
\ref alsa_transfers section.
\section pcm_open_behaviour Blocked and non-blocked open
The ALSA PCM API uses a different behaviour when the device is opened
with blocked or non-blocked mode. The mode can be specified with
\a mode argument in \link ::snd_pcm_open() \endlink function.
The blocked mode is the default (without \link ::SND_PCM_NONBLOCK \endlink mode).
In this mode, the behaviour is that if the resources have already used
with another application, then it blocks the caller, until resources are
free. The non-blocked behaviour (with \link ::SND_PCM_NONBLOCK \endlink)
doesn't block the caller in any way and returns -EBUSY error when the
resources are not available. Note that the mode also determines the
behaviour of standard I/O calls, returning -EAGAIN when non-blocked mode is
used and the ring buffer is full (playback) or empty (capture).
The operation mode for I/O calls can be changed later with
the \link snd_pcm_nonblock() \endlink function.
\section pcm_async Asynchronous mode
There is also possibility to receive asynchronous notification after
specified time periods. You may see the \link ::SND_PCM_ASYNC \endlink
mode for \link ::snd_pcm_open() \endlink function and
\link ::snd_async_add_pcm_handler() \endlink function for further details.
\section pcm_handshake Handshake between application and library
The ALSA PCM API design uses the states to determine the communication
phase between application and library. The actual state can be determined
using \link ::snd_pcm_state() \endlink call. There are these states:
\par SND_PCM_STATE_OPEN
The PCM device is in the open state. After the \link ::snd_pcm_open() \endlink open call,
the device is in this state. Also, when \link ::snd_pcm_hw_params() \endlink call fails,
then this state is entered to force application calling
\link ::snd_pcm_hw_params() \endlink function to set right communication
parameters.
\par SND_PCM_STATE_SETUP
The PCM device has accepted communication parameters and it is waiting
for \link ::snd_pcm_prepare() \endlink call to prepare the hardware for
selected operation (playback or capture).
\par SND_PCM_STATE_PREPARE
The PCM device is prepared for operation. Application can use
\link ::snd_pcm_start() \endlink call, write or read data to start
the operation.
\par SND_PCM_STATE_RUNNING
The PCM device is running. It processes the samples. The stream can
be stopped using the \link ::snd_pcm_drop() \endlink or
\link ::snd_pcm_drain \endlink calls.
\par SND_PCM_STATE_XRUN
The PCM device reached overrun (capture) or underrun (playback).
You can use the -EPIPE return code from I/O functions
(\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_writen() \endlink,
\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readi() \endlink)
to determine this state without checking
the actual state via \link ::snd_pcm_state() \endlink call. You can recover from
this state with \link ::snd_pcm_prepare() \endlink,
\link ::snd_pcm_drop() \endlink or \link ::snd_pcm_drain() \endlink calls.
\par SND_PCM_STATE_DRAINING
The device is in this state when application using the capture mode
called \link ::snd_pcm_drain() \endlink function. Until all data are
read from the internal ring buffer using I/O routines
(\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readn() \endlink),
then the device stays in this state.
\par SND_PCM_STATE_PAUSED
The device is in this state when application called
the \link ::snd_pcm_pause() \endlink function until the pause is released.
Not all hardware supports this feature. Application should check the
capability with the \link ::snd_pcm_hw_params_can_pause() \endlink.
\par SND_PCM_STATE_SUSPENDED
The device is in the suspend state provoked with the power management
system. The stream can be resumed using \link ::snd_pcm_resume() \endlink
call, but not all hardware supports this feature. Application should check
the capability with the \link ::snd_pcm_hw_params_can_resume() \endlink.
In other case, the calls \link ::snd_pcm_prepare() \endlink,
\link ::snd_pcm_drop() \endlink, \link ::snd_pcm_drain() \endlink can be used
to leave this state.
\section pcm_formats PCM formats
The full list of formats present the \link ::snd_pcm_format_t \endlink type.
The 24-bit linear samples uses 32-bit physical space, but the sample is
stored in low three bits. Some hardware does not support processing of full
range, thus you may get the significative bits for linear samples via
\link ::snd_pcm_hw_params_get_sbits \endlink function. The example: ICE1712
chips support 32-bit sample processing, but low byte is ignored (playback)
or zero (capture). The function \link ::snd_pcm_hw_params_get_sbits() \endlink
returns 24 in the case.
\section alsa_transfers ALSA transfers
There are two methods to transfer samples in application. The first method
is the standard read / write one. The second method, uses the direct audio
buffer to communicate with the device while ALSA library manages this space
itself. You can find examples of all communication schemes for playback
in \ref example_test_pcm "Sine-wave generator example". To complete the
list, we should note that \link ::snd_pcm_wait \endlink function contains
embedded poll waiting implementation.
\subsection alsa_pcm_rw Read / Write transfer
There are two versions of read / write routines. The first expects the
interleaved samples at input, and the second one expects non-interleaved
(samples in separated buffers) at input. There are these functions for
interleaved transfers: \link ::snd_pcm_writei \endlink,
\link ::snd_pcm_readi \endlink. For non-interleaved transfers, there are
these functions: \link ::snd_pcm_writen \endlink and \link ::snd_pcm_readn
\endlink.
\subsection alsa_mmap_rw Direct Read / Write transfer (via mmaped areas)
There are two functions for this kind of transfer. Application can get an
access to memory areas via \link ::snd_pcm_mmap_begin \endlink function.
This functions returns the areas (single area is equal to a channel)
containing the direct pointers to memory and sample position description
in \link ::snd_pcm_channel_area_t \endlink structure. After application
transfers the data in the memory areas, then it must be acknowledged
the end of transfer via \link ::snd_pcm_mmap_commit() \endlink function
to allow the ALSA library update the pointers to ring buffer. This sort of
communication is also called "zero-copy", because the device does not require
to copy the samples from application to another place in system memory.
\par
If you like to use the compatibility functions in mmap mode, there are
read / write routines equaling to standard read / write transfers. Using
these functions discards the benefits of direct access to memory region.
See the \link ::snd_pcm_mmap_readi() \endlink,
\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_readn() \endlink
and \link ::snd_pcm_writen() \endlink functions.
\section pcm_params Managing parameters
The ALSA PCM device uses two groups of PCM related parameters. The hardware
parameters contains the stream description like format, rate, count of
channels, ring buffer size etc. The software parameters contains the
software (driver) related parameters. The communicatino behaviour can be
controlled via these parameters, like automatic start, automatic stop,
interrupting (chunk acknowledge) etc. The software parameters can be
modified at any time (when valid hardware parameters are set). It includes
the running state as well.
\subsection pcm_hw_params Hardware related parameters
The ALSA PCM devices use the parameter refining system for hardware
parameters - \link ::snd_pcm_hw_params_t \endlink. It means, that
application choose the full-range of configurations at first and then
application sets single parameters until all parameters are elementary
(definite).
\par Access modes
ALSA knows about five access modes. The first three can be used for direct
communication. The access mode \link ::SND_PCM_ACCESS_MMAP_INTERLEAVED \endlink
determines the direct memory area and interleaved sample organization.
Interleaved organization means, that samples from channels are mixed together.
The access mode \link ::SND_PCM_ACCESS_MMAP_NONINTERLEAVED \endlink
determines the direct memory area and non-interleaved sample organization.
Each channel has a separate buffer in the case. The complex direct memory
organization represents the \link ::SND_PCM_ACCESS_MMAP_COMPLEX \endlink
access mode. The sample organization does not fit the interleaved or
non-interleaved access modes in the case. The last two access modes
describes the read / write access methods.
The \link ::SND_PCM_ACCESS_RW_INTERLEAVED \endlink access represents the read /
write interleaved access and the \link ::SND_PCM_ACCESS_RW_NONINTERLEAVED \endlink
represents the non-interleaved access.
\par Formats
The full list of formats is available in \link ::snd_pcm_format_t \endlink
enumeration.
\subsection pcm_sw_params Software related parameters
These parameters - \link ::snd_pcm_sw_params_t \endlink can be modified at
any time including the running state.
\par Minimum available count of samples
This parameter controls the wakeup point. If the count of available samples
is equal or greater than this value, then application will be activated.
\par Timestamp mode
The timestamp mode specifies, if timestamps are activated. Currently, only
\link ::SND_PCM_TSTAMP_NONE \endlink and \link ::SND_PCM_TSTAMP_MMAP
\endlink modes are known. The mmap mode means that timestamp is taken
on every period time boundary.
\par Minimal sleep
This parameters means the minimum of ticks to sleep using a standalone
timer (usually the system timer). The tick resolution can be obtained
via the function \link ::snd_pcm_hw_params_get_tick_time \endlink. This
function can be used to fine-tune the transfer acknowledge process. It could
be useful especially when some hardware does not support small transfer
periods.
\par Transfer align
The read / write transfers can be aligned to this sample count. The modulo
is ignored by device. Usually, this value is set to one (no align).
\par Start threshold
The start threshold parameter is used to determine the start point in
stream. For playback, if samples in ring buffer is equal or greater than
the start threshold parameters and the stream is not running, the stream will
be started automatically from the device. For capture, if the application wants
to read count of samples equal or greater then the stream will be started.
If you want to use explicit start (\link ::snd_pcm_start \endlink), you can
set this value greater than ring buffer size (in samples), but use the
constant MAXINT is not a bad idea.
\par Stop threshold
Similarly, the stop threshold parameter is used to automatically stop
the running stream, when the available samples crosses this boundary.
It means, for playback, the empty samples in ring buffer and for capture,
the filled (used) samples in ring buffer.
\par Silence threshold
The silence threshold specifies count of samples filled with silence
ahead of the current application pointer for playback. It is useable
for applications when an overrun is possible (like tasks depending on
network I/O etc.). If application wants to manage the ahead samples itself,
the \link ::snd_pcm_rewind() \endlink function allows to forget the last
samples in the stream.
\section pcm_status Obtaining device status
The device status is stored in \link ::snd_pcm_status_t \endlink structure.
These parameters can be obtained: the current stream state -
\link ::snd_pcm_status_get_state \endlink, timestamp of trigger -
\link ::snd_pcm_status_get_trigger_tstamp \endlink, timestamp of last
update \link ::snd_pcm_status_get_tstamp \endlink, delay in samples -
\link ::snd_pcm_status_get_delay \endlink, available count in samples -
\link ::snd_pcm_status_get_avail \endlink, maximum available samples -
\link ::snd_pcm_status_get_avail_max \endlink, ADC overrange count in
samples - \link ::snd_pcm_status_get_overrange \endlink. The last two
parameters - avail_max and overrange are reset to zero after the status
call.
\subsection pcm_status_fast Obtaining fast device status
The function \link ::snd_pcm_avail_update \endlink updates the current
available count of samples for writting (playback) or filled samples for
reading (capture).
<p>
The function \link ::snd_pcm_delay \endlink returns the delay in samples.
For playback, it means count of samples in the ring buffer before
the next sample will be sent to DAC. For capture, it means count of samples
in the ring buffer before the next sample will be captured from ADC.
\section pcm_action Managing the stream state
These functions directly and indirectly affecting the stream state:
\par snd_pcm_hw_params
The \link ::snd_pcm_hw_params \endlink function brings the stream state
to \link ::SND_PCM_STATE_SETUP \endlink
if successfully finishes, otherwise the state \link ::SND_PCM_STATE_OPEN
\endlink is entered.
\par snd_pcm_prepare
The \link ::snd_pcm_prepare \endlink function enters the
\link ::SND_PCM_STATE_PREPARED \endlink after a successfull finish.
\par snd_pcm_start
The \link ::snd_pcm_start \endlink function enters
the \link ::SND_PCM_STATE_RUNNING \endlink after a successfull finish.
\par snd_pcm_drop
The \link ::snd_pcm_drop \endlink function enters the
\link ::SND_PCM_STATE_SETUP \endlink state.
\par snd_pcm_drain
The \link ::snd_pcm_drain \endlink function enters the
\link ::SND_PCM_STATE_DRAINING \endlink, if
the capture device has some samples in the ring buffer otherwise
\link ::SND_PCM_STATE_SETUP \endlink state is entered.
\par snd_pcm_pause
The \link ::snd_pcm_pause \endlink function enters the
\link ::SND_PCM_STATE_PAUSED \endlink or
\link ::SND_PCM_STATE_RUNNING \endlink.
\par snd_pcm_writei, snd_pcm_writen
The \link ::snd_pcm_writei \endlink and \link ::snd_pcm_writen \endlink
functions can conditionally start the stream -
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
software parameter.
\par snd_pcm_readi, snd_pcm_readn
The \link ::snd_pcm_readi \endlink and \link ::snd_pcm_readn \endlink
functions can conditionally start the stream -
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
software parameter.
\section pcm_sync Streams synchronization
There are two functions allowing link multiple streams together. In the
case, the linking means that all operations are synchronized. Because the
drivers cannot guarantee the synchronization (sample resolution) on hardware
lacking this feature, the \link ::snd_pcm_info_get_sync \endlink function
returns synchronization ID - \link ::snd_pcm_sync_id_t \endlink, which is equal
for hardware synchronizated streams. When the \link ::snd_pcm_link \endlink
function is called, all operations managing the stream state for these two
streams are joined. The oposite function is \link ::snd_pcm_unlink \endlink.
\section pcm_examples Examples
The full featured examples with cross-links:
\par Sine-wave generator
\ref example_test_pcm "example code"
\par
This example shows various transfer methods for the playback direction.
\par Latency measuring tool
\ref example_test_latency "example code"
\par
This example shows the measuring of minimal latency between capture and
playback devices.
*/
/**
* \example ../test/pcm.c
* \anchor example_test_pcm
*/
/**
* \example ../test/latency.c
* \anchor example_test_latency
*/

View file

@ -51,8 +51,10 @@ typedef enum _snd_config_type {
SND_CONFIG_TYPE_REAL,
/** Character string */
SND_CONFIG_TYPE_STRING,
/** Pointer - runtime only - cannot be saved */
SND_CONFIG_TYPE_POINTER,
/** Compound */
SND_CONFIG_TYPE_COMPOUND,
SND_CONFIG_TYPE_COMPOUND = 1024,
} snd_config_type_t;
/** Config node handle */
@ -60,10 +62,13 @@ typedef struct _snd_config snd_config_t;
/** Config compound iterator */
typedef struct _snd_config_iterator *snd_config_iterator_t;
extern snd_config_t *snd_config;
int snd_config_top(snd_config_t **config);
int snd_config_load(snd_config_t *config, snd_input_t *in);
int snd_config_save(snd_config_t *config, snd_output_t *out);
int snd_config_update(void);
int snd_config_search(snd_config_t *config, const char *key,
snd_config_t **result);
@ -74,10 +79,10 @@ int snd_config_search_definition(snd_config_t *config,
snd_config_t **result);
int snd_config_expand(snd_config_t *config, snd_config_t *root,
const char *args, void *private_data,
const char *args, snd_config_t *private_data,
snd_config_t **result);
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
void *private_data, snd_config_t **result);
snd_config_t *private_data, snd_config_t **result);
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
int snd_config_delete(snd_config_t *config);
@ -88,17 +93,24 @@ int snd_config_make(snd_config_t **config, const char *key,
int snd_config_make_integer(snd_config_t **config, const char *key);
int snd_config_make_real(snd_config_t **config, const char *key);
int snd_config_make_string(snd_config_t **config, const char *key);
int snd_config_make_pointer(snd_config_t **config, const char *key);
int snd_config_make_compound(snd_config_t **config, const char *key, int join);
snd_config_type_t snd_config_get_type(snd_config_t *config);
int snd_config_set_id(snd_config_t *config, const char *id);
int snd_config_set_integer(snd_config_t *config, long value);
int snd_config_set_real(snd_config_t *config, double value);
int snd_config_set_string(snd_config_t *config, const char *value);
int snd_config_set_ascii(snd_config_t *config, const char *ascii);
int snd_config_set_pointer(snd_config_t *config, const void *ptr);
int snd_config_get_id(snd_config_t *config, const char **value);
int snd_config_get_integer(snd_config_t *config, long *value);
int snd_config_get_real(snd_config_t *config, double *value);
int snd_config_get_string(snd_config_t *config, const char **value);
int snd_config_get_ascii(snd_config_t *config, char **value);
int snd_config_get_pointer(snd_config_t *config, const void **value);
int snd_config_test_id(snd_config_t *config, const char *id);
snd_config_iterator_t snd_config_iterator_first(snd_config_t *node);
snd_config_iterator_t snd_config_iterator_next(snd_config_iterator_t iterator);
@ -115,12 +127,6 @@ snd_config_t *snd_config_iterator_entry(snd_config_iterator_t iterator);
#define snd_config_for_each(pos, next, node) \
for (pos = snd_config_iterator_first(node), next = snd_config_iterator_next(pos); pos != snd_config_iterator_end(node); pos = next, next = snd_config_iterator_next(pos))
snd_config_type_t snd_config_get_type(snd_config_t *config);
const char *snd_config_get_id(snd_config_t *config);
extern snd_config_t *snd_config;
int snd_config_update(void);
/* Misc functions */
int snd_config_get_bool_ascii(const char *ascii);

View file

@ -192,12 +192,6 @@ int snd_card_get_index(const char *name);
int snd_card_get_name(int card, char **name);
int snd_card_get_longname(int card, char **name);
int snd_sctl_build(snd_sctl_t **ctl, snd_ctl_t *handle, snd_config_t *config,
void *private_data, int mode);
int snd_sctl_free(snd_sctl_t *handle);
int snd_sctl_install(snd_sctl_t *handle);
int snd_sctl_remove(snd_sctl_t *handle);
int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode);
int snd_ctl_close(snd_ctl_t *ctl);
int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock);
@ -482,9 +476,23 @@ void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val);
/** \} */
/**
* \defgroup SControl Setup Control Interface
* \ingroup Control
* The setup control interface - set or modify control elements from a configuration file.
* \{
*/
int snd_sctl_build(snd_sctl_t **ctl, snd_ctl_t *handle, snd_config_t *config,
snd_config_t *private_data, int mode);
int snd_sctl_free(snd_sctl_t *handle);
int snd_sctl_install(snd_sctl_t *handle);
int snd_sctl_remove(snd_sctl_t *handle);
/** \} */
#ifdef __cplusplus
}
#endif
#endif /* __ALSA_CONTROL_H */

View file

@ -29,13 +29,14 @@
#define __ALSA_GLOBAL_H_
/**
* \defgroup Global Global defines
* Global defines
* \defgroup Global Global defines and functions
* Global defines and functions.
* \{
*/
#ifndef ATTRIBUTE_UNUSED
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) /**< don't print warning when attribute is not used */
/** do not print warning (gcc) when function parameter is not used */
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif
#ifdef PIC /* dynamic build */
@ -88,9 +89,9 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd,
snd_async_callback_t callback, void *private_data);
int snd_async_del_handler(snd_async_handler_t *handler);
int snd_async_handler_get_fd(snd_async_handler_t *handler);
int snd_async_handler_get_signo(snd_async_handler_t *handler);
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler);
/** \} */
#endif /* __ALSA_GLOBAL_H */

View file

@ -113,8 +113,6 @@ typedef struct sndrv_seq_event snd_seq_event_t;
#define SND_BIG_ENDIAN
#endif
extern int snd_async_signo;
struct _snd_async_handler {
enum {
SND_ASYNC_HANDLER_GENERIC,

View file

@ -6,7 +6,8 @@
* \author Takashi Iwai <tiwai@suse.de>
* \date 1998-2001
*
* Application interface library for the ALSA driver
* Application interface library for the ALSA driver.
* See the \ref pcm page for more details.
*
*
* This library is free software; you can redistribute it and/or modify
@ -34,7 +35,7 @@ extern "C" {
/**
* \defgroup PCM PCM Interface
* The PCM Interface.
* See the \ref pcm page for more details.
* \{
*/
@ -59,6 +60,7 @@ typedef struct _snd_pcm_subformat_mask snd_pcm_subformat_mask_t;
/** PCM class */
typedef enum _snd_pcm_class {
/** standard device */
SND_PCM_CLASS_GENERIC = 0,
/** multichannel device */
SND_PCM_CLASS_MULTI,
@ -388,8 +390,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm);
/**
* \defgroup PCM_Info Stream Information
* PCM Stream Information
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -422,8 +424,8 @@ void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val);
/**
* \defgroup PCM_HW_Params Hardware Parameters
* PCM Hardware Parameters
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -596,8 +598,8 @@ unsigned int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_par
/**
* \defgroup PCM_SW_Params Software Parameters
* PCM Software Parameters
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -637,8 +639,8 @@ snd_pcm_uframes_t snd_pcm_sw_params_get_silence_size(const snd_pcm_sw_params_t *
/**
* \defgroup PCM_Access Access Mask Functions
* PCM Access Mask Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -661,8 +663,8 @@ void snd_pcm_access_mask_reset(snd_pcm_access_mask_t *mask, snd_pcm_access_t val
/**
* \defgroup PCM_Format Format Mask Functions
* PCM Format Mask Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -685,8 +687,8 @@ void snd_pcm_format_mask_reset(snd_pcm_format_mask_t *mask, snd_pcm_format_t val
/**
* \defgroup PCM_SubFormat Subformat Mask Functions
* PCM Subformat Mask Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -709,8 +711,8 @@ void snd_pcm_subformat_mask_reset(snd_pcm_subformat_mask_t *mask, snd_pcm_subfor
/**
* \defgroup PCM_Status Status Functions
* PCM Status Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -735,8 +737,8 @@ snd_pcm_uframes_t snd_pcm_status_get_overrange(const snd_pcm_status_t *obj);
/**
* \defgroup PCM_Description Description Functions
* PCM Description Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -754,8 +756,8 @@ const char *snd_pcm_state_name(const snd_pcm_state_t state);
/**
* \defgroup PCM_Dump Debug Functions
* PCM Debug Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -771,8 +773,8 @@ int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out);
/**
* \defgroup PCM_Direct Direct Access (MMAP) Functions
* PCM Direct Access (MMAP) Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -791,8 +793,8 @@ snd_pcm_sframes_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframe
/**
* \defgroup PCM_Helpers Helper Functions
* PCM Helper Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -832,8 +834,8 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_channels, snd_pcm_ufram
/**
* \defgroup PCM_Hook Hook Extension
* PCM Hook Extension
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -861,8 +863,8 @@ int snd_pcm_hook_remove(snd_pcm_hook_t *hook);
/**
* \defgroup PCM_Scope Scope Plugin Extension
* PCM Scope Plugin Extension
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -920,8 +922,8 @@ int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope,
/**
* \defgroup PCM_Deprecated Deprecated Functions
* PCM Deprecated Functions
* \ingroup PCM
* See the \ref pcm page for more details.
* \{
*/
@ -940,4 +942,3 @@ snd_pcm_xrun_t snd_pcm_sw_params_get_xrun_mode(const snd_pcm_sw_params_t *params
#endif
#endif /* __ALSA_PCM_H */

View file

@ -30,7 +30,8 @@
#ifdef SND_ASYNC_RT_SIGNAL
/** async signal number */
int snd_async_signo;
static int snd_async_signo;
void snd_async_init(void) __attribute__ ((constructor));
void snd_async_init(void)
@ -43,7 +44,7 @@ void snd_async_init(void)
}
#else
/** async signal number */
int snd_async_signo = SIGIO;
static int snd_async_signo = SIGIO;
#endif
static LIST_HEAD(snd_async_handlers);
@ -68,12 +69,18 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo
* \param callback - Async callback
* \param private_data - Private data for async callback
* \result zero if success, otherwise a negative error code
*
* The function create the async handler. The ALSA extension
* for the standard SIGIO signal contains the multiplexer
* for multiple asynchronous notifiers using one sigaction
* callback.
*/
int snd_async_add_handler(snd_async_handler_t **handler, int fd,
snd_async_callback_t callback, void *private_data)
{
snd_async_handler_t *h;
int was_empty;
assert(handler);
h = malloc(sizeof(*h));
if (!h)
return -ENOMEM;
@ -107,6 +114,7 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd,
int snd_async_del_handler(snd_async_handler_t *handler)
{
int err = 0;
assert(handler);
list_del(&handler->glist);
if (list_empty(&snd_async_handlers)) {
struct sigaction act;
@ -139,6 +147,17 @@ int snd_async_del_handler(snd_async_handler_t *handler)
return err;
}
/**
* \brief Get signal number assigned to async handler
* \param handler Async handler
* \result signal number if success, otherwise a negative error code
*/
int snd_async_handler_get_signo(snd_async_handler_t *handler)
{
assert(handler);
return snd_async_signo;
}
/**
* \brief Get file descriptor assigned to async handler
* \param handler Async handler
@ -146,6 +165,7 @@ int snd_async_del_handler(snd_async_handler_t *handler)
*/
int snd_async_handler_get_fd(snd_async_handler_t *handler)
{
assert(handler);
return handler->fd;
}
@ -156,6 +176,7 @@ int snd_async_handler_get_fd(snd_async_handler_t *handler)
*/
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler)
{
assert(handler);
return handler->private_data;
}

View file

@ -5,7 +5,7 @@
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2000-2001
*
* Generic stdio-like input interface
* Tree based, full nesting configuration functions.
*/
/*
* Configuration helper functions
@ -29,6 +29,306 @@
*
*/
/*! \page conf Configuration files
<P>Configuration files are using a simple format allowing the modern
data description like nesting and array assignments.</P>
\section conf_whitespace Whitespace
Whitespace is the collective name given to spaces (blanks), horizontal and
vertical tabs, newline characters, and comments. Whitespace can serve to
indicate where configuration tokens start and end, but beyond this function,
any surplus whitespace is discarded. For example, the two sequences
\code
a 1 b 2
\endcode
and
\code
a 1
b 2
\endcode
are lexically equivalent and parse identically to give the four tokens:
\code
a
1
b
2
\endcode
The ASCII characters representing whitespace can occur within literal
strings, int which case they are protected from the normal parsing process
(tey remain as part of the string). For example:
\code
name "John Smith"
\endcode
parses to two tokens, including the single literal-string token "John
Smith".
\section conf_linesplicing Line splicing with \
A special case occurs, if the final newline character encountered is
preceded by a backslash (\) in the string value definition. The backslash
and new line are both discarded, allowing two physical lines of text to be
treated as one unit.
\code
"John \
Smith"
\endcode
is parsed as "John Smith".
\section conf_comments Comments
A single-line comments are defined using character #. The comment can start
in any position, and extends until the next new line.
\code
a 1 # this is a comment
\endcode
\section conf_include Include another configuration file
A new configuration file can be included using <filename> syntax. The global
configuration directory can be referenced using <confdir:filename> syntax.
\code
</etc/alsa1.conf>
<confdir:pcm/surround.conf>
\endcode
\section conf_punctuators Punctuators
The configuration punctuators (also known as separators) are:
\code
{} [] , ; = . ' " new-line form-feed carriage-return whitespace
\endcode
\subsection conf_braces Braces
Open and close braces { } indicate the start and end of a compound
statement:
\code
a {
b 1
}
\endcode
\subsection conf_brackets Brackets
Open and close brackets indicate single array definition. The identificators
are automatically generated starting with zero.
\code
a [
"first"
"second"
]
\endcode
Above code is equal to
\code
a.0 "first"
a.1 "second"
\endcode
\subsection conf_comma_semicolon Comma and semicolon
The comma (,) or semicolon (;) can separate the value assignments. It is not
strictly required to use these separators, because any whitespace supplies
them.
\code
a 1;
b 1,
\endcode
\subsection conf_equal Equal sign
The equal sign (=) separates can separate variable declarations from
initialization lists:
\code
a=1
b=2
\endcode
Using the equal signs is not required, because any whitespace supplies
them.
\section conf_assigns Assigns
The configuration file defines id (key) and value pairs. The id (key) can be
composed from any ASCII digits or chars from a to z or A to Z, including
char _. The value can be either a string, integer or real number.
\subsection conf_single Single assign
\code
a 1 # is equal to
a=1 # is equal to
a=1; # is equal to
a 1,
\endcode
\subsection conf_compound Compound assign (definition using braces)
\code
a {
b = 1
}
a={
b 1,
}
\endcode
\section conf_compound1 Compound assign (one key definition)
\code
a.b 1
a.b=1
\endcode
\subsection conf_array Array assign (definition using brackets)
\code
a [
"first"
"second"
]
\endcode
\subsection conf_array1 Array assign (one key definition)
\code
a.0 "first"
a.1 "second"
\endcode
\section conf_summary Summary
\code
# Configuration file syntax
# Include a new configuration file
<filename>
# Simple assign
name [=] value [,|;]
# Compound assign (first style)
name [=] {
name1 [=] value [,|;]
...
}
# Compound assign (second style)
name.name1 [=] value [,|;]
# Array assign (first style)
name [
value0 [,|;]
value1 [,|;]
...
]
# Array assign (second style)
name.0 [=] value0 [,|;]
name.1 [=] value1 [,|;]
\endcode
*/
/*! \page confarg Configuration - runtime arguments
<P>The ALSA library can accept runtime arguments for some configuration
blocks. This extension is on top of the basic syntax of the configuration
files.<P>
\section confarg_define Defining arguments
Arguments are specified by id (key) @args and array values containing
the string names of arguments:
\code
@args [ CARD ] # or
@args.0 CARD
\endcode
\section confarg_type Defining argument type and default value
Arguments type is specified by id (key) @args and argument name. The type
and default value is specified in the compound:
\code
@args.CARD {
type string
default "abcd"
}
\endcode
\section confarg_refer Refering argument
Arguments are refered by dollar-sign ($) and name of argument:
\code
card $CARD
\endcode
\section confarg_example Example
\code
pcm.demo {
@args [ CARD DEVICE ]
@args.CARD {
type string
default "supersonic"
}
@args.DEVICE {
type integer
default 0
}
type hw
card $CARD
device $DEVICE
}
\endcode
*/
/*! \page conffunc Configuration - runtime functions
<P>The ALSA library accepts the runtime modification of configuration.
The several build-in functions are available.</P>
<P>The function is refered using id @func and function name. All other
values in the current compound are used as configuration for the function.
If compound func.<function_name> is defined in the root leafs, then library
and function from this compound configuration is used, otherwise the prefix
'snd_func_' is added to string and the code from the ALSA library is used.
The definition of function looks like:</P>
\code
func.remove_first_char {
lib "/usr/lib/libasoundextend.so"
func "extend_remove_first_char"
}
\endcode
*/
#include <stdarg.h>
#include <wordexp.h>
#include <dlfcn.h>
@ -44,6 +344,7 @@ struct _snd_config {
long integer;
char *string;
double real;
const void *ptr;
struct {
struct list_head fields;
int join;
@ -895,6 +1196,9 @@ static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
case SND_CONFIG_TYPE_STRING:
string_print(n->u.string, 0, out);
break;
case SND_CONFIG_TYPE_POINTER:
SNDERR("cannot save runtime pointer type");
return -EINVAL;
case SND_CONFIG_TYPE_COMPOUND:
snd_output_putc(out, '{');
snd_output_putc(out, '\n');
@ -1024,11 +1328,14 @@ snd_config_type_t snd_config_get_type(snd_config_t *config)
/**
* \brief Return id of a config node
* \param config Config node handle
* \return node id
* \param value The result id
* \return 0 on success otherwise a negative error code
*/
const char *snd_config_get_id(snd_config_t *config)
int snd_config_get_id(snd_config_t *config, const char **id)
{
return config->id;
assert(config && id);
*id = config->id;
return 0;
}
/**
@ -1245,6 +1552,17 @@ int snd_config_make_string(snd_config_t **config, const char *id)
return snd_config_make(config, id, SND_CONFIG_TYPE_STRING);
}
/**
* \brief Build a pointer config node
* \param config Returned config node handle pointer
* \param id Node id
* \return 0 on success otherwise a negative error code
*/
int snd_config_make_pointer(snd_config_t **config, const char *id)
{
return snd_config_make(config, id, SND_CONFIG_TYPE_POINTER);
}
/**
* \brief Build an empty compound config node
* \param config Returned config node handle pointer
@ -1312,6 +1630,21 @@ int snd_config_set_string(snd_config_t *config, const char *value)
return 0;
}
/**
* \brief Change the value of a pointer config node
* \param config Config node handle
* \param ptr Value
* \return 0 on success otherwise a negative error code
*/
int snd_config_set_pointer(snd_config_t *config, const void *value)
{
assert(config);
if (config->type != SND_CONFIG_TYPE_POINTER)
return -EINVAL;
config->u.ptr = value;
return 0;
}
/**
* \brief Change the value of a config node
* \param config Config node handle
@ -1399,6 +1732,21 @@ int snd_config_get_string(snd_config_t *config, const char **ptr)
return 0;
}
/**
* \brief Get the value of a pointer config node
* \param config Config node handle
* \param ptr Returned value pointer
* \return 0 on success otherwise a negative error code
*/
int snd_config_get_pointer(snd_config_t *config, const void **ptr)
{
assert(config && ptr);
if (config->type != SND_CONFIG_TYPE_POINTER)
return -EINVAL;
*ptr = config->u.ptr;
return 0;
}
/**
* \brief Get the value in ASCII form
* \param config Config node handle
@ -1453,6 +1801,18 @@ int snd_config_get_ascii(snd_config_t *config, char **ascii)
return 0;
}
/**
* \brief Compare the config node id and given ASCII id
* \param config Config node handle
* \param id ASCII id
* \return the same value as result of the strcmp function
*/
int snd_config_test_id(snd_config_t *config, const char *id)
{
assert(config && id);
return strcmp(config->id, id);
}
/**
* \brief Dump a config tree contents
* \param config Config node handle
@ -1666,7 +2026,7 @@ int snd_config_search_alias(snd_config_t *config,
*/
int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t **result)
{
static int snd_config_hooks(snd_config_t *config, void *private_data);
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
SND_CONFIG_SEARCH(config, key, result, \
err = snd_config_hooks(config, NULL); \
if (err < 0) \
@ -1683,7 +2043,7 @@ int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t
*/
int snd_config_searcha_hooks(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
{
static int snd_config_hooks(snd_config_t *config, void *private_data);
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
SND_CONFIG_SEARCHA(root, config, key, result,
snd_config_searcha_hooks,
err = snd_config_hooks(config, NULL); \
@ -1745,14 +2105,14 @@ static struct finfo {
static unsigned int files_info_count = 0;
static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void *private_data)
static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_config_t *private_data)
{
void *h = NULL;
snd_config_t *c, *func_conf = NULL;
char *buf = NULL;
const char *lib = NULL, *func_name = NULL;
const char *str;
int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data) = NULL;
int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data) = NULL;
int err;
err = snd_config_search(config, "func", &c);
@ -1774,7 +2134,7 @@ static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void
}
snd_config_for_each(i, next, func_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id = n->id;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
@ -1833,7 +2193,7 @@ static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void
return 0;
}
static int snd_config_hooks(snd_config_t *config, void *private_data)
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data)
{
snd_config_t *n;
snd_config_iterator_t i, next;
@ -1846,7 +2206,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
hit = 0;
snd_config_for_each(i, next, n) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id = n->id;
long i;
err = safe_strtol(id, &i);
if (err < 0) {
@ -1877,7 +2237,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
* \param private_data Private data
* \return zero if success, otherwise a negative error code
*/
int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data)
int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data)
{
snd_config_t *n, *res = NULL;
snd_config_iterator_t i, next;
@ -1913,7 +2273,7 @@ int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t
snd_config_t *c = snd_config_iterator_entry(i);
const char *str;
if ((err = snd_config_get_string(c, &str)) < 0) {
SNDERR("Field %s is not a string", snd_config_get_id(c));
SNDERR("Field %s is not a string", c->id);
goto _err;
}
fi_count++;
@ -1927,7 +2287,7 @@ int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t
hit = 0;
snd_config_for_each(i, next, n) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id = n->id;
long i;
err = safe_strtol(id, &i);
if (err < 0) {
@ -2014,7 +2374,7 @@ int snd_determine_driver(int card, char **driver);
* \param private_data Private data
* \return zero if success, otherwise a negative error code
*/
int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data ATTRIBUTE_UNUSED)
int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data ATTRIBUTE_UNUSED)
{
int card = -1, err;
@ -2023,12 +2383,16 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
if (err < 0)
return err;
if (card >= 0) {
snd_config_t *n;
snd_config_t *n, *private_data = NULL;
const char *driver;
char *fdriver = NULL;
err = snd_determine_driver(card, &fdriver);
if (err < 0)
return err;
err = snd_config_make_string(&private_data, "string");
if (err < 0)
goto __err;
snd_config_set_string(private_data, fdriver);
if (snd_config_search(root, fdriver, &n) >= 0) {
if (snd_config_get_string(n, &driver) < 0)
continue;
@ -2043,7 +2407,10 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
} else {
driver = fdriver;
}
err = snd_config_hook_load(root, config, &n, (void *)driver);
err = snd_config_hook_load(root, config, &n, private_data);
__err:
if (private_data)
snd_config_delete(private_data);
if (fdriver)
free(fdriver);
if (err < 0)
@ -2255,14 +2622,14 @@ typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
snd_config_t *root,
snd_config_t **dst,
snd_config_walk_pass_t pass,
void *private_data);
snd_config_t *private_data);
#endif
static int snd_config_walk(snd_config_t *src,
snd_config_t *root,
snd_config_t **dst,
snd_config_walk_callback_t callback,
void *private_data)
snd_config_t *private_data)
{
int err;
snd_config_iterator_t i, next;
@ -2303,10 +2670,10 @@ static int _snd_config_copy(snd_config_t *src,
snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t **dst,
snd_config_walk_pass_t pass,
void *private_data ATTRIBUTE_UNUSED)
snd_config_t *private_data ATTRIBUTE_UNUSED)
{
int err;
const char *id = snd_config_get_id(src);
const char *id = src->id;
snd_config_type_t type = snd_config_get_type(src);
switch (pass) {
case SND_CONFIG_WALK_PASS_PRE:
@ -2370,10 +2737,10 @@ static int _snd_config_expand(snd_config_t *src,
snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t **dst,
snd_config_walk_pass_t pass,
void *private_data)
snd_config_t *private_data)
{
int err;
const char *id = snd_config_get_id(src);
const char *id = src->id;
snd_config_type_t type = snd_config_get_type(src);
switch (pass) {
case SND_CONFIG_WALK_PASS_PRE:
@ -2453,7 +2820,7 @@ static int _snd_config_evaluate(snd_config_t *src,
snd_config_t *root,
snd_config_t **dst ATTRIBUTE_UNUSED,
snd_config_walk_pass_t pass,
void *private_data)
snd_config_t *private_data)
{
int err;
if (pass == SND_CONFIG_WALK_PASS_PRE) {
@ -2461,7 +2828,7 @@ static int _snd_config_evaluate(snd_config_t *src,
const char *lib = NULL, *func_name = NULL;
const char *str;
int (*func)(snd_config_t **dst, snd_config_t *root,
snd_config_t *src, void *private_data) = NULL;
snd_config_t *src, snd_config_t *private_data) = NULL;
void *h = NULL;
snd_config_t *c, *func_conf = NULL;
err = snd_config_search(src, "@func", &c);
@ -2481,7 +2848,7 @@ static int _snd_config_evaluate(snd_config_t *src,
}
snd_config_for_each(i, next, func_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id = n->id;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
@ -2552,7 +2919,7 @@ static int _snd_config_evaluate(snd_config_t *src,
* \return zero if success, otherwise a negative error code
*/
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
void *private_data, snd_config_t **result)
snd_config_t *private_data, snd_config_t **result)
{
/* FIXME: Only in place evaluation is currently implemented */
assert(result == NULL);
@ -2569,7 +2936,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
continue;
snd_config_for_each(f, fnext, def) {
snd_config_t *fld = snd_config_iterator_entry(f);
const char *id = snd_config_get_id(fld);
const char *id = fld->id;
if (strcmp(id, "type") == 0)
continue;
if (strcmp(id, "default") == 0) {
@ -2578,7 +2945,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
err = snd_config_copy(&deflt, fld);
if (err < 0)
return err;
err = snd_config_set_id(deflt, snd_config_get_id(def));
err = snd_config_set_id(deflt, def->id);
if (err < 0) {
snd_config_delete(deflt);
return err;
@ -2818,7 +3185,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
snd_config_for_each(i, next, subs) {
snd_config_t *n = snd_config_iterator_entry(i);
snd_config_t *d;
const char *id = snd_config_get_id(n);
const char *id = n->id;
err = snd_config_search(defs, id, &d);
if (err < 0) {
SNDERR("Unknown parameter %s", id);
@ -2856,7 +3223,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
err = -EINVAL;
goto _err;
}
var = snd_config_get_id(def);
var = def->id;
err = snd_config_search(subs, var, &sub);
if (err >= 0)
snd_config_delete(sub);
@ -2936,7 +3303,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
* \return 0 on success otherwise a negative error code
*/
int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,
void *private_data, snd_config_t **result)
snd_config_t *private_data, snd_config_t **result)
{
int err;
snd_config_t *defs, *subs = NULL, *res;

View file

@ -38,7 +38,7 @@ pcm.!center_lfe {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.center_lfe." $DEV ":CARD=" $CARD

View file

@ -38,7 +38,7 @@ pcm.!front {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.front." $DEV ":CARD=" $CARD

View file

@ -58,7 +58,7 @@ pcm.!iec958 {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.iec958." $DEV ":"

View file

@ -38,7 +38,7 @@ pcm.!rear {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.rear." $DEV ":CARD=" $CARD

View file

@ -43,7 +43,7 @@ pcm.!surround40 {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.surround40." $DEV ":CARD=" $CARD

View file

@ -45,7 +45,7 @@ pcm.!surround51 {
strings [
"cards."
{
@func card_strtype
@func card_driver
card $CARD
}
".pcm.surround51." $DEV ":CARD=" $CARD

View file

@ -1,3 +1,11 @@
/**
* \file confmisc.c
* \brief Configuration helper functions
* \author Abramo Bagnara <abramo@alsa-project.org>
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2000-2001
*
* Configuration helper functions.
/*
* Miscellaneous configuration helper functions
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
@ -20,6 +28,40 @@
*
*/
/*! \page conffunc
\section Function reference
<UL>
<LI>The getenv function - snd_func_getenv() - allows to obtain
an environment value. The result is string.
<LI>The igetenv function - snd_func_igetenv() - allows to obtain
an environment value. The result is integer.
<LI>The concat function - snd_func_concat() - merges all specified
strings. The result is string.
<LI>The datadir function - snd_func_datadir() - returns the
data directory. The result is string.
<LI>The refer function - snd_func_refer() - copies the refered
configuration. The result is same as the refered node.
<LI>The card_driver function - snd_func_card_driver() - returns
the driver identification. The result is string.
<LI>The card_id function - snd_func_card_id() - returns
the card identification. The result is string.
<LI>The pcm_id function - snd_func_pcm_id() - returns
the pcm identification. The result is string.
<LI>The private_string - snd_func_private_string() - returns
string using private_data node.
<LI>The private_card_driver - snd_func_private_card_driver() -
returns the driver identification using private_data node.
The result is string.
<LI>The private_pcm_subdevice - snd_func_private_pcm_subdevice() -
returns the PCM subdevice number using the private_data node.
The result is string.
</UL>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -62,21 +104,24 @@ int snd_config_get_bool_ascii(const char *ascii)
int snd_config_get_bool(snd_config_t *conf)
{
long v;
const char *str;
const char *str, *id;
int err;
err = snd_config_get_id(conf, &id);
if (err < 0)
return err;
err = snd_config_get_integer(conf, &v);
if (err >= 0) {
if (v < 0 || v > 1) {
_invalid_value:
SNDERR("Invalid value for %s", snd_config_get_id(conf));
SNDERR("Invalid value for %s", id);
return -EINVAL;
}
return v;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
err = snd_config_get_bool_ascii(str);
@ -116,20 +161,24 @@ int snd_config_get_ctl_iface_ascii(const char *ascii)
int snd_config_get_ctl_iface(snd_config_t *conf)
{
long v;
const char *str;
const char *str, *id;
int err;
err = snd_config_get_id(conf, &id);
if (err < 0)
return err;
err = snd_config_get_integer(conf, &v);
if (err >= 0) {
if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) {
_invalid_value:
SNDERR("Invalid value for %s", snd_config_get_id(conf));
SNDERR("Invalid value for %s", id);
return -EINVAL;
}
return v;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
err = snd_config_get_ctl_iface_ascii(str);
@ -142,7 +191,25 @@ int snd_config_get_ctl_iface(snd_config_t *conf)
* Helper functions for the configuration file
*/
int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Get environment value
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node, with vars and default definition
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func getenv
vars [ MY_CARD CARD C ]
default 0
}
\endcode
*/
int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *n, *d;
snd_config_iterator_t i, next;
@ -178,9 +245,10 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
hit = 0;
snd_config_for_each(i, next, n) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *ptr, *env;
const char *id, *ptr, *env;
long i;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
SNDERR("field %s is not a string", id);
err = -EINVAL;
@ -209,9 +277,13 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
__ok:
err = res == NULL ? -ENOMEM : 0;
if (err >= 0) {
err = snd_config_make_string(dst, snd_config_get_id(src));
const char *id;
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
snd_config_set_string(*dst, res);
}
free(res);
}
__error:
@ -219,38 +291,80 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
free(def);
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_getenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_igetenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Get integer environment value
* \param dst The destination node (result type is integer)
* \param root The root source node
* \param src The source node, with vars and default definition
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func getenv
vars [ MY_DEVICE DEVICE D ]
default 0
}
\endcode
*/
int snd_func_igetenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *d;
const char *str;
const char *str, *id;
int err;
long v;
err = snd_func_getenv(&d, root, src, private_data);
if (err < 0)
return err;
err = snd_config_get_string(d, &str);
if (err < 0)
goto _end;
return err;
err = safe_strtol(str, &v);
if (err < 0)
goto _end;
err = snd_config_make_integer(dst, snd_config_get_id(src));
return err;;
err = snd_config_get_id(src, &id);
if (err < 0)
goto _end;
snd_config_set_integer(*dst, v);
err = 0;
_end:
return err;
err = snd_config_make_integer(dst, id);
if (err < 0)
return err;
snd_config_set_integer(*dst, v);
return 0;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_igetenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Merge given strings
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node, with strings definition
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example (result is string "a1b2c3" ]:
\code
{
@func concat
strings [ "a1" "b2" "c3" ]
default 0
}
\endcode
*/
int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *n;
snd_config_iterator_t i, next;
const char *id;
char *res = NULL, *tmp;
int idx = 0, len = 0, len1, err, hit;
@ -269,8 +383,10 @@ int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
snd_config_for_each(i, next, n) {
snd_config_t *n = snd_config_iterator_entry(i);
char *ptr;
const char *id = snd_config_get_id(n);
const char *id;
long i;
if (snd_config_get_id(n, &id) < 0)
continue;
err = safe_strtol(id, &i);
if (err < 0) {
SNDERR("id of field %s is not an integer", id);
@ -303,24 +419,52 @@ int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
err = -EINVAL;
goto __error;
}
err = snd_config_make_string(dst, snd_config_get_id(src));
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
snd_config_set_string(*dst, res);
}
free(res);
__error:
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_concat, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
/**
* \brief Get data directory
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node (unused)
* \return a positive value when success otherwise a negative error number
*
* Example (result is "/usr/share/alsa" using default paths):
\code
{
int err = snd_config_make_string(dst, snd_config_get_id(src));
@func datadir
}
\endcode
*/
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t *src, snd_config_t *private_data ATTRIBUTE_UNUSED)
{
int err;
const char *id;
err = snd_config_get_id(src, &id);
if (err < 0)
return err;
err = snd_config_make_string(dst, id);
if (err >= 0)
err = snd_config_set_string(*dst, DATADIR "/alsa");
return 0;
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_datadir, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
static int open_ctl(long card, snd_ctl_t **ctl)
{
@ -344,19 +488,53 @@ static int string_from_integer(char **dst, long v)
}
#endif
int snd_func_private_string(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
/**
* \brief Get string from private_data
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node (type string, id == "string")
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func private_string
}
\endcode
*/
int snd_func_private_string(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t *src, snd_config_t *private_data)
{
int err;
snd_config_t *n;
const char *str, *id;
if (private_data == NULL)
return snd_config_copy(dst, src);
err = snd_config_make_string(dst, snd_config_get_id(src));
if (err >= 0)
err = snd_config_set_string(*dst, (char *)private_data);
err = snd_config_test_id(private_data, "string");
if (err) {
SNDERR("field string not found");
return -EINVAL;
}
err = snd_config_get_string(private_data, &str);
if (err < 0) {
SNDERR("field string is not a string");
return err;
}
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
err = snd_config_set_string(*dst, str);
}
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_private_string, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
#ifndef DOC_HIDDEN
int snd_determine_driver(int card, char **driver)
{
snd_ctl_t *ctl = NULL;
@ -388,25 +566,77 @@ int snd_determine_driver(int card, char **driver)
snd_ctl_close(ctl);
return err;
}
#endif
int snd_func_private_card_strtype(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
/**
* \brief Get driver identification using private_data
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node (type = integer, id = "card")
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func private_card_driver
}
\endcode
*/
int snd_func_private_card_driver(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src,
snd_config_t *private_data)
{
char *driver;
snd_config_t *n;
const char *id;
int err;
long card;
if ((err = snd_determine_driver((long)private_data, &driver)) < 0)
err = snd_config_test_id(private_data, "card");
if (err) {
SNDERR("field card not found");
return -EINVAL;
}
err = snd_config_get_integer(n, &card);
if (err < 0) {
SNDERR("field card is not an integer");
return err;
err = snd_config_make_string(dst, snd_config_get_id(src));
}
if ((err = snd_determine_driver(card, &driver)) < 0)
return err;
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
err = snd_config_set_string(*dst, driver);
}
free(driver);
return err;
}
SND_DLSYM_BUILD_VERSION(snd_func_private_card_strtype, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_private_card_driver, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_card_strtype(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Get driver identification
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
snd_config_t *n;
@func card_driver
card 0
}
\endcode
*/
int snd_func_card_driver(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *n, *val;
char *str;
long v;
int err;
@ -433,16 +663,42 @@ int snd_func_card_strtype(snd_config_t **dst, snd_config_t *root, snd_config_t *
return v;
}
free(str);
return snd_func_private_card_strtype(dst, root, src, (void *)v);
err = snd_config_make_integer(&val, "card");
if (err < 0)
return err;
snd_config_set_integer(val, v);
err = snd_func_private_card_driver(dst, root, src, val);
snd_config_delete(val);
return err;
}
SND_DLSYM_BUILD_VERSION(snd_func_card_strtype, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_card_driver, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Get card identification
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func card_id
card 0
}
\endcode
*/
int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *n;
char *res = NULL;
snd_ctl_t *ctl = NULL;
snd_ctl_card_info_t *info;
const char *id;
long v;
int err;
@ -477,22 +733,46 @@ int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
err = -ENOMEM;
goto __error;
}
err = snd_config_make_string(dst, snd_config_get_id(src));
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
err = snd_config_set_string(*dst, res);
}
free(res);
__error:
if (ctl)
snd_ctl_close(ctl);
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_card_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
/**
* \brief Get pcm identification
* \param dst The destination node (result type is string)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func pcm_id
card 0
device 0
subdevice 0 # optional
}
\endcode
*/
int snd_func_pcm_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
{
snd_config_t *n;
snd_ctl_t *ctl = NULL;
snd_pcm_info_t *info;
const char *id;
long card, device, subdevice = 0;
int err;
@ -551,42 +831,94 @@ int snd_func_pcm_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
goto __error;
}
err = snd_config_make_string(dst, snd_config_get_id(src));
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_string(dst, id);
if (err >= 0)
err = snd_config_set_string(*dst, snd_pcm_info_get_id(info));
}
__error:
if (ctl)
snd_ctl_close(ctl);
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_pcm_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_private_pcm_subdevice(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
/**
* \brief Get pcm subdevice using private_data
* \param dst The destination node (result type is integer)
* \param root The root source node
* \param src The source node
* \param private_data The private_data node (type = pointer, id = "pcm_handle")
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func private_pcm_subdevice
}
\endcode
*/
int snd_func_private_pcm_subdevice(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
snd_config_t *src, snd_config_t *private_data)
{
char *res = NULL;
snd_pcm_info_t *info;
snd_config_t *n;
const char *id;
snd_pcm_t *pcm;
int err;
if (private_data == NULL)
return snd_config_copy(dst, src);
err = snd_config_test_id(private_data, "pcm_handle");
if (err) {
SNDERR("field pcm_handle not found");
return -EINVAL;
}
err = snd_config_get_pointer(private_data, (const void **)&pcm);
if (err < 0) {
SNDERR("field pcm_handle is not a pointer");
return err;
}
snd_pcm_info_alloca(&info);
err = snd_pcm_info((snd_pcm_t *)private_data, info);
err = snd_pcm_info(pcm, info);
if (err < 0) {
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
return err;
}
res = strdup(snd_pcm_info_get_id(info));
if (res == NULL)
return -ENOMEM;
err = snd_config_make_integer(dst, snd_config_get_id(src));
err = snd_config_get_id(src, &id);
if (err >= 0) {
err = snd_config_make_integer(dst, id);
if (err >= 0)
err = snd_config_set_integer(*dst, snd_pcm_info_get_subdevice(info));
free(res);
}
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_private_pcm_subdevice, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif
int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
/**
* \brief Copy the refered configuration node
* \param dst The destination node (result type is same as refered node)
* \param root The root source node (can be modified!!!)
* \param src The source node
* \param private_data The private_data node
* \return a positive value when success otherwise a negative error number
*
* Example:
\code
{
@func refer
file "/etc/myconf.conf" # optional
name "id1.id2.id3"
}
\endcode
*/
int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
snd_config_t *private_data)
{
snd_config_t *n;
const char *file = NULL, *name = NULL;
@ -631,17 +963,21 @@ int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src, vo
goto _end;
}
err = snd_config_load(root, input);
if (err < 0) {
snd_input_close(input);
if (err < 0)
goto _end;
}
}
err = snd_config_search_definition(root, NULL, name, dst);
if (err >= 0) {
const char *id;
err = snd_config_get_id(src, &id);
if (err >= 0)
err = snd_config_set_id(*dst, snd_config_get_id(src));
else
err = snd_config_set_id(*dst, id);
} else
SNDERR("Unable to find definition '%s'", name);
_end:
return err;
}
#ifndef DOC_HIDDEN
SND_DLSYM_BUILD_VERSION(snd_func_refer, SND_CONFIG_DLSYM_VERSION_EVALUATE);
#endif

View file

@ -444,7 +444,7 @@ int snd_async_add_ctl_handler(snd_async_handler_t **handler, snd_ctl_t *ctl,
was_empty = list_empty(&ctl->async_handlers);
list_add_tail(&h->hlist, &ctl->async_handlers);
if (was_empty) {
err = snd_ctl_async(ctl, getpid(), snd_async_signo);
err = snd_ctl_async(ctl, snd_async_get_signo(h), getpid());
if (err < 0) {
snd_async_del_handler(h);
return err;
@ -474,6 +474,7 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *lib = NULL, *open_name = NULL;
const char *id;
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
extern void *snd_control_open_symbols(void);
@ -491,9 +492,14 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
@ -504,7 +510,9 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -349,7 +349,9 @@ int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBU
int err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)

View file

@ -572,7 +572,9 @@ int _snd_ctl_shm_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_c
struct hostent *h;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)
@ -617,7 +619,9 @@ int _snd_ctl_shm_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_c
}
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "host") == 0) {

View file

@ -335,7 +335,10 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf,
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
err = safe_strtol(snd_config_get_id(n), &idx);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
err = safe_strtol(id, &idx);
if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
SNDERR("bad value index");
return -EINVAL;
@ -381,7 +384,7 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf,
return 0;
}
static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data)
{
snd_config_t *conf;
snd_config_iterator_t i, next;
@ -401,7 +404,9 @@ static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
return err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
@ -576,7 +581,7 @@ static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
* \param mode Build mode - SND_SCTL_xxxx
* \result zero if success, otherwise a negative error code
*/
int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, void *private_data, int mode)
int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
{
snd_sctl_t *h;
snd_config_iterator_t i, next;

View file

@ -41,6 +41,9 @@ struct snd_dlsym_link *snd_dlsym_start = NULL;
* \param name name, similar to dlopen
* \param mode mode, similar to dlopen
* \return pointer to handle
*
* The extension is a special code for the static build of
* the alsa-lib library.
*/
void *snd_dlopen(const char *name, int mode)
{
@ -55,6 +58,9 @@ void *snd_dlopen(const char *name, int mode)
* \brief Close the dynamic library, with ALSA extension
* \param handle handle, similar to dlclose
* \return zero if success, otherwise an error code
*
* The extension is a special code for the static build of
* the alsa-lib library.
*/
int snd_dlclose(void *handle)
{
@ -80,6 +86,8 @@ static int snd_dlsym_verify(void *handle, const char *name, const char *version)
if (handle == NULL)
return -EINVAL;
vname = alloca(1 + strlen(name) + strlen(version) + 1);
if (vname == NULL)
return -ENOMEM;
vname[0] = '_';
strcpy(vname + 1, name);
strcat(vname, version);
@ -89,11 +97,16 @@ static int snd_dlsym_verify(void *handle, const char *name, const char *version)
SNDERR("unable to verify version for symbol %s", name);
return res;
}
/**
* \brief Resolve the symbol, with ALSA extension
* \param handle handle, similar to dlsym
* \param name symbol name
* \param version symbol version
*
* This special version of dlsym function checks also
* the version of symbol. The version of a symbol should
* be defined using #SND_DLSYM_BUILD_VERSION macro.
*/
void *snd_dlsym(void *handle, const char *name, const char *version)
{
@ -101,8 +114,8 @@ void *snd_dlsym(void *handle, const char *name, const char *version)
#ifndef PIC
if (handle == &snd_dlsym_start) {
/* it's the funny part, we are looking for a symbol */
/* in a static library */
/* it's the funny part: */
/* we are looking for a symbol in a static library */
struct snd_dlsym_link *link = snd_dlsym_start;
while (link) {
if (!strcmp(name, link->dlsym_name))

View file

@ -46,6 +46,7 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_hwdep_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
@ -64,9 +65,14 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(hwdep_root, "hwdep_type", str, &type_conf);
@ -77,7 +83,9 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -152,7 +152,9 @@ int _snd_hwdep_hw_open(snd_hwdep_t **hwdep, char *name,
int err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)

View file

@ -1,5 +1,6 @@
/**
* \file pcm/pcm.c
* \ingroup PCM
* \brief PCM Interface
* \author Jaroslav Kysela <perex@suse.cz>
* \author Abramo Bagnara <abramo@alsa-project.org>
@ -11,6 +12,8 @@
* 44100 you'll hear 44100 frames per second. The size in bytes of a
* frame may be obtained from bits needed to store a sample and
* channels count.
*
* See the \ref pcm page for more details.
*/
/*
* PCM Interface - main file
@ -33,6 +36,449 @@
*
*/
/*! \page pcm PCM (digital audio) interface
<P>Although abbreviation PCM stands for Pulse Code Modulation, we are
understanding it as general digital audio processing with volume samples
generated in continuous time periods.</P>
<P>Digital audio is the most commonly used method of representing
sound inside a computer. In this method sound is stored as a sequence of
samples taken from the audio signal using constant time intervals.
A sample represents volume of the signal at the moment when it
was measured. In uncompressed digital audio each sample require one
or more bytes of storage. The number of bytes required depends on number
of channels (mono, stereo) and sample format (8 or 16 bits, mu-Law, etc.).
The length of this interval determines the sampling rate. Commonly used
sampling rates are between 8kHz (telephone quality) and
48kHz (DAT tapes).</P>
<P>The physical devices used in digital audio are called the
ADC (Analog to Digital Converter) and DAC (Digital to Analog Converter).
A device containing both ADC and DAC is commonly known as a codec.
The codec device used in a Sound Blaster cards is called a DSP which
is somewhat misleading since DSP also stands for Digital Signal Processor
(the SB DSP chip is very limited when compared to "true" DSP chips).</P>
<P>Sampling parameters affect the quality of sound which can be
reproduced from the recorded signal. The most fundamental parameter
is sampling rate which limits the highest frequency that can be stored.
It is well known (Nyquist's Sampling Theorem) that the highest frequency
that can be stored in a sampled signal is at most 1/2 of the sampling
frequency. For example, an 8 kHz sampling rate permits the recording of
a signal in which the highest frequency is less than 4 kHz. Higher frequency
signals must be filtered out before feeding them to ADC.</P>
<P>Sample encoding limits the dynamic range of a recorded signal
(difference between the faintest and the loudest signal that can be
recorded). In theory the maximum dynamic range of signal is number_of_bits *
6dB. This means that 8 bits sampling resolution gives dynamic range of
48dB and 16 bit resolution gives 96dB.</P>
<P>Quality has price. The number of bytes required to store an audio
sequence depends on sampling rate, number of channels and sampling
resolution. For example just 8000 bytes of memory is required to store
one second of sound using 8kHz/8 bits/mono but 48kHz/16bit/stereo takes
192 kilobytes. A 64 kbps ISDN channel is required to transfer a
8kHz/8bit/mono audio stream in real time, and about 1.5Mbps is required
for DAT quality (48kHz/16bit/stereo). On the other hand it is possible
to store just 5.46 seconds of sound in a megabyte of memory when using
48kHz/16bit/stereo sampling. With 8kHz/8bits/mono it is possible to store
131 seconds of sound using the same amount of memory. It is possible
to reduce memory and communication costs by compressing the recorded
signal but this is beyond the scope of this document. </P>
\section pcm_general_overview General overview
ALSA uses the ring buffer to store outgoing (playback) and incoming (capture,
record) samples. There are two pointers being mantained to allow
a precise communication between application and device pointing to current
processed sample by hardware and last processed sample by application.
The modern audio chips allow to program the transfer time periods.
It means that the stream of samples is divided to small chunks. Device
acknowledges to application when the transfer of a chunk is complete.
\section pcm_transfer Transfer methods in unix environments
In the unix environment, data chunk acknowledges are received via standard I/O
calls or event waiting routines (poll or select function). To accomplish
this list, the asynchronous notification of acknowledges should be listed
here. The ALSA implementation for these methods is described in
the \ref alsa_transfers section.
\subsection pcm_transfer_io Standard I/O transfers
The standard I/O transfers are using the read (see 'man 2 read') and write
(see 'man 2 write') C functions. There are two basic behaviours of these
functions - blocked and non-blocked (see the O_NONBLOCK flag for the
standard C open function - see 'man 2 open'). In non-blocked behaviour,
these I/O functions never stops, they return -EAGAIN error code, when no
data can be transferred (the ring buffer is full in our case). In blocked
behaviour, these I/O functions stop and wait until there is a room in the
ring buffer (playback) or until there are a new samples (capture). The ALSA
implementation can be found in the \ref alsa_pcm_rw section.
\subsection pcm_transfer_event Event waiting routines
The poll or select functions (see 'man 2 poll' or 'man 2 select' for further
details) allows to receive requests/events from the device while
an application is waiting on events from other sources (like keyboard, screen,
network etc.), too. The select function is old and deprecated in modern
applications, so the ALSA library does not support it. The implemented
transfer routines can be found in the \ref alsa_transfers section.
\subsection pcm_transfer_async Asynchronous notification
ALSA driver and library knows to handle the asynchronous notifications over
the SIGIO signal. This signal allows to interrupt application and transfer
data in the signal handler. For further details see the sigaction function
('man 2 sigaction'). The section \ref pcm_async describes the ALSA API for
this extension. The implemented transfer routines can be found in the
\ref alsa_transfers section.
\section pcm_open_behaviour Blocked and non-blocked open
The ALSA PCM API uses a different behaviour when the device is opened
with blocked or non-blocked mode. The mode can be specified with
\a mode argument in \link ::snd_pcm_open() \endlink function.
The blocked mode is the default (without \link ::SND_PCM_NONBLOCK \endlink mode).
In this mode, the behaviour is that if the resources have already used
with another application, then it blocks the caller, until resources are
free. The non-blocked behaviour (with \link ::SND_PCM_NONBLOCK \endlink)
doesn't block the caller in any way and returns -EBUSY error when the
resources are not available. Note that the mode also determines the
behaviour of standard I/O calls, returning -EAGAIN when non-blocked mode is
used and the ring buffer is full (playback) or empty (capture).
The operation mode for I/O calls can be changed later with
the \link snd_pcm_nonblock() \endlink function.
\section pcm_async Asynchronous mode
There is also possibility to receive asynchronous notification after
specified time periods. You may see the \link ::SND_PCM_ASYNC \endlink
mode for \link ::snd_pcm_open() \endlink function and
\link ::snd_async_add_pcm_handler() \endlink function for further details.
\section pcm_handshake Handshake between application and library
The ALSA PCM API design uses the states to determine the communication
phase between application and library. The actual state can be determined
using \link ::snd_pcm_state() \endlink call. There are these states:
\par SND_PCM_STATE_OPEN
The PCM device is in the open state. After the \link ::snd_pcm_open() \endlink open call,
the device is in this state. Also, when \link ::snd_pcm_hw_params() \endlink call fails,
then this state is entered to force application calling
\link ::snd_pcm_hw_params() \endlink function to set right communication
parameters.
\par SND_PCM_STATE_SETUP
The PCM device has accepted communication parameters and it is waiting
for \link ::snd_pcm_prepare() \endlink call to prepare the hardware for
selected operation (playback or capture).
\par SND_PCM_STATE_PREPARE
The PCM device is prepared for operation. Application can use
\link ::snd_pcm_start() \endlink call, write or read data to start
the operation.
\par SND_PCM_STATE_RUNNING
The PCM device is running. It processes the samples. The stream can
be stopped using the \link ::snd_pcm_drop() \endlink or
\link ::snd_pcm_drain \endlink calls.
\par SND_PCM_STATE_XRUN
The PCM device reached overrun (capture) or underrun (playback).
You can use the -EPIPE return code from I/O functions
(\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_writen() \endlink,
\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readi() \endlink)
to determine this state without checking
the actual state via \link ::snd_pcm_state() \endlink call. You can recover from
this state with \link ::snd_pcm_prepare() \endlink,
\link ::snd_pcm_drop() \endlink or \link ::snd_pcm_drain() \endlink calls.
\par SND_PCM_STATE_DRAINING
The device is in this state when application using the capture mode
called \link ::snd_pcm_drain() \endlink function. Until all data are
read from the internal ring buffer using I/O routines
(\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readn() \endlink),
then the device stays in this state.
\par SND_PCM_STATE_PAUSED
The device is in this state when application called
the \link ::snd_pcm_pause() \endlink function until the pause is released.
Not all hardware supports this feature. Application should check the
capability with the \link ::snd_pcm_hw_params_can_pause() \endlink.
\par SND_PCM_STATE_SUSPENDED
The device is in the suspend state provoked with the power management
system. The stream can be resumed using \link ::snd_pcm_resume() \endlink
call, but not all hardware supports this feature. Application should check
the capability with the \link ::snd_pcm_hw_params_can_resume() \endlink.
In other case, the calls \link ::snd_pcm_prepare() \endlink,
\link ::snd_pcm_drop() \endlink, \link ::snd_pcm_drain() \endlink can be used
to leave this state.
\section pcm_formats PCM formats
The full list of formats present the \link ::snd_pcm_format_t \endlink type.
The 24-bit linear samples uses 32-bit physical space, but the sample is
stored in low three bits. Some hardware does not support processing of full
range, thus you may get the significative bits for linear samples via
\link ::snd_pcm_hw_params_get_sbits \endlink function. The example: ICE1712
chips support 32-bit sample processing, but low byte is ignored (playback)
or zero (capture). The function \link ::snd_pcm_hw_params_get_sbits() \endlink
returns 24 in the case.
\section alsa_transfers ALSA transfers
There are two methods to transfer samples in application. The first method
is the standard read / write one. The second method, uses the direct audio
buffer to communicate with the device while ALSA library manages this space
itself. You can find examples of all communication schemes for playback
in \ref example_test_pcm "Sine-wave generator example". To complete the
list, we should note that \link ::snd_pcm_wait \endlink function contains
embedded poll waiting implementation.
\subsection alsa_pcm_rw Read / Write transfer
There are two versions of read / write routines. The first expects the
interleaved samples at input, and the second one expects non-interleaved
(samples in separated buffers) at input. There are these functions for
interleaved transfers: \link ::snd_pcm_writei \endlink,
\link ::snd_pcm_readi \endlink. For non-interleaved transfers, there are
these functions: \link ::snd_pcm_writen \endlink and \link ::snd_pcm_readn
\endlink.
\subsection alsa_mmap_rw Direct Read / Write transfer (via mmaped areas)
There are two functions for this kind of transfer. Application can get an
access to memory areas via \link ::snd_pcm_mmap_begin \endlink function.
This functions returns the areas (single area is equal to a channel)
containing the direct pointers to memory and sample position description
in \link ::snd_pcm_channel_area_t \endlink structure. After application
transfers the data in the memory areas, then it must be acknowledged
the end of transfer via \link ::snd_pcm_mmap_commit() \endlink function
to allow the ALSA library update the pointers to ring buffer. This sort of
communication is also called "zero-copy", because the device does not require
to copy the samples from application to another place in system memory.
\par
If you like to use the compatibility functions in mmap mode, there are
read / write routines equaling to standard read / write transfers. Using
these functions discards the benefits of direct access to memory region.
See the \link ::snd_pcm_mmap_readi() \endlink,
\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_readn() \endlink
and \link ::snd_pcm_writen() \endlink functions.
\section pcm_params Managing parameters
The ALSA PCM device uses two groups of PCM related parameters. The hardware
parameters contains the stream description like format, rate, count of
channels, ring buffer size etc. The software parameters contains the
software (driver) related parameters. The communicatino behaviour can be
controlled via these parameters, like automatic start, automatic stop,
interrupting (chunk acknowledge) etc. The software parameters can be
modified at any time (when valid hardware parameters are set). It includes
the running state as well.
\subsection pcm_hw_params Hardware related parameters
The ALSA PCM devices use the parameter refining system for hardware
parameters - \link ::snd_pcm_hw_params_t \endlink. It means, that
application choose the full-range of configurations at first and then
application sets single parameters until all parameters are elementary
(definite).
\par Access modes
ALSA knows about five access modes. The first three can be used for direct
communication. The access mode \link ::SND_PCM_ACCESS_MMAP_INTERLEAVED \endlink
determines the direct memory area and interleaved sample organization.
Interleaved organization means, that samples from channels are mixed together.
The access mode \link ::SND_PCM_ACCESS_MMAP_NONINTERLEAVED \endlink
determines the direct memory area and non-interleaved sample organization.
Each channel has a separate buffer in the case. The complex direct memory
organization represents the \link ::SND_PCM_ACCESS_MMAP_COMPLEX \endlink
access mode. The sample organization does not fit the interleaved or
non-interleaved access modes in the case. The last two access modes
describes the read / write access methods.
The \link ::SND_PCM_ACCESS_RW_INTERLEAVED \endlink access represents the read /
write interleaved access and the \link ::SND_PCM_ACCESS_RW_NONINTERLEAVED \endlink
represents the non-interleaved access.
\par Formats
The full list of formats is available in \link ::snd_pcm_format_t \endlink
enumeration.
\subsection pcm_sw_params Software related parameters
These parameters - \link ::snd_pcm_sw_params_t \endlink can be modified at
any time including the running state.
\par Minimum available count of samples
This parameter controls the wakeup point. If the count of available samples
is equal or greater than this value, then application will be activated.
\par Timestamp mode
The timestamp mode specifies, if timestamps are activated. Currently, only
\link ::SND_PCM_TSTAMP_NONE \endlink and \link ::SND_PCM_TSTAMP_MMAP
\endlink modes are known. The mmap mode means that timestamp is taken
on every period time boundary.
\par Minimal sleep
This parameters means the minimum of ticks to sleep using a standalone
timer (usually the system timer). The tick resolution can be obtained
via the function \link ::snd_pcm_hw_params_get_tick_time \endlink. This
function can be used to fine-tune the transfer acknowledge process. It could
be useful especially when some hardware does not support small transfer
periods.
\par Transfer align
The read / write transfers can be aligned to this sample count. The modulo
is ignored by device. Usually, this value is set to one (no align).
\par Start threshold
The start threshold parameter is used to determine the start point in
stream. For playback, if samples in ring buffer is equal or greater than
the start threshold parameters and the stream is not running, the stream will
be started automatically from the device. For capture, if the application wants
to read count of samples equal or greater then the stream will be started.
If you want to use explicit start (\link ::snd_pcm_start \endlink), you can
set this value greater than ring buffer size (in samples), but use the
constant MAXINT is not a bad idea.
\par Stop threshold
Similarly, the stop threshold parameter is used to automatically stop
the running stream, when the available samples crosses this boundary.
It means, for playback, the empty samples in ring buffer and for capture,
the filled (used) samples in ring buffer.
\par Silence threshold
The silence threshold specifies count of samples filled with silence
ahead of the current application pointer for playback. It is useable
for applications when an overrun is possible (like tasks depending on
network I/O etc.). If application wants to manage the ahead samples itself,
the \link ::snd_pcm_rewind() \endlink function allows to forget the last
samples in the stream.
\section pcm_status Obtaining device status
The device status is stored in \link ::snd_pcm_status_t \endlink structure.
These parameters can be obtained: the current stream state -
\link ::snd_pcm_status_get_state \endlink, timestamp of trigger -
\link ::snd_pcm_status_get_trigger_tstamp \endlink, timestamp of last
update \link ::snd_pcm_status_get_tstamp \endlink, delay in samples -
\link ::snd_pcm_status_get_delay \endlink, available count in samples -
\link ::snd_pcm_status_get_avail \endlink, maximum available samples -
\link ::snd_pcm_status_get_avail_max \endlink, ADC overrange count in
samples - \link ::snd_pcm_status_get_overrange \endlink. The last two
parameters - avail_max and overrange are reset to zero after the status
call.
\subsection pcm_status_fast Obtaining fast device status
The function \link ::snd_pcm_avail_update \endlink updates the current
available count of samples for writting (playback) or filled samples for
reading (capture).
<p>
The function \link ::snd_pcm_delay \endlink returns the delay in samples.
For playback, it means count of samples in the ring buffer before
the next sample will be sent to DAC. For capture, it means count of samples
in the ring buffer before the next sample will be captured from ADC.
\section pcm_action Managing the stream state
These functions directly and indirectly affecting the stream state:
\par snd_pcm_hw_params
The \link ::snd_pcm_hw_params \endlink function brings the stream state
to \link ::SND_PCM_STATE_SETUP \endlink
if successfully finishes, otherwise the state \link ::SND_PCM_STATE_OPEN
\endlink is entered.
\par snd_pcm_prepare
The \link ::snd_pcm_prepare \endlink function enters the
\link ::SND_PCM_STATE_PREPARED \endlink after a successfull finish.
\par snd_pcm_start
The \link ::snd_pcm_start \endlink function enters
the \link ::SND_PCM_STATE_RUNNING \endlink after a successfull finish.
\par snd_pcm_drop
The \link ::snd_pcm_drop \endlink function enters the
\link ::SND_PCM_STATE_SETUP \endlink state.
\par snd_pcm_drain
The \link ::snd_pcm_drain \endlink function enters the
\link ::SND_PCM_STATE_DRAINING \endlink, if
the capture device has some samples in the ring buffer otherwise
\link ::SND_PCM_STATE_SETUP \endlink state is entered.
\par snd_pcm_pause
The \link ::snd_pcm_pause \endlink function enters the
\link ::SND_PCM_STATE_PAUSED \endlink or
\link ::SND_PCM_STATE_RUNNING \endlink.
\par snd_pcm_writei, snd_pcm_writen
The \link ::snd_pcm_writei \endlink and \link ::snd_pcm_writen \endlink
functions can conditionally start the stream -
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
software parameter.
\par snd_pcm_readi, snd_pcm_readn
The \link ::snd_pcm_readi \endlink and \link ::snd_pcm_readn \endlink
functions can conditionally start the stream -
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
software parameter.
\section pcm_sync Streams synchronization
There are two functions allowing link multiple streams together. In the
case, the linking means that all operations are synchronized. Because the
drivers cannot guarantee the synchronization (sample resolution) on hardware
lacking this feature, the \link ::snd_pcm_info_get_sync \endlink function
returns synchronization ID - \link ::snd_pcm_sync_id_t \endlink, which is equal
for hardware synchronizated streams. When the \link ::snd_pcm_link \endlink
function is called, all operations managing the stream state for these two
streams are joined. The oposite function is \link ::snd_pcm_unlink \endlink.
\section pcm_examples Examples
The full featured examples with cross-links:
\par Sine-wave generator
\ref example_test_pcm "example code"
\par
This example shows various transfer methods for the playback direction.
\par Latency measuring tool
\ref example_test_latency "example code"
\par
This example shows the measuring of minimal latency between capture and
playback devices.
*/
/**
* \example ../test/pcm.c
* \anchor example_test_pcm
*/
/**
* \example ../test/latency.c
* \anchor example_test_latency
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
@ -995,7 +1441,7 @@ int snd_async_add_pcm_handler(snd_async_handler_t **handler, snd_pcm_t *pcm,
was_empty = list_empty(&pcm->async_handlers);
list_add_tail(&h->hlist, &pcm->async_handlers);
if (was_empty) {
err = snd_pcm_async(pcm, snd_async_signo, getpid());
err = snd_pcm_async(pcm, snd_async_signo(h), getpid());
if (err < 0) {
snd_async_del_handler(h);
return err;
@ -1025,6 +1471,7 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_pcm_t **, const char *,
snd_config_t *, snd_config_t *,
@ -1045,9 +1492,14 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);
@ -1058,7 +1510,9 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
@ -4645,7 +5099,9 @@ int snd_pcm_slave_conf(snd_config_t *root, snd_config_t *conf,
va_end(args);
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "pcm") == 0) {

View file

@ -557,7 +557,9 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -430,7 +430,9 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -199,7 +199,9 @@ int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *slave = NULL, *sconf;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -474,7 +474,9 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
long fd = -1;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -335,7 +335,7 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
{
int err;
char buf[256];
const char *str;
const char *str, *id;
const char *lib = NULL, *install = NULL;
snd_config_t *type = NULL, *args = NULL;
snd_config_iterator_t i, next;
@ -347,7 +347,9 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
}
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0) {
@ -365,9 +367,14 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
SNDERR("type is not defined");
return -EINVAL;
}
err = snd_config_get_id(type, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(type, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(type));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
@ -378,7 +385,9 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
}
snd_config_for_each(i, next, type) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
@ -446,7 +455,9 @@ int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *hooks = NULL;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {
@ -620,6 +631,7 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
char ctl_name[16];
snd_ctl_t *ctl;
snd_sctl_t *sctl;
snd_config_t *pcm_conf = NULL;
snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
assert(conf);
assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
@ -638,9 +650,13 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
SNDERR("Cannot open CTL %s", ctl_name);
return err;
}
err = snd_sctl_build(&sctl, ctl, conf, pcm, 0);
err = snd_config_make_pointer(&pcm_conf, "pcm_handle");
if (err < 0)
return -ENOMEM;
goto _err;
snd_config_set_pointer(pcm_conf, pcm);
err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
if (err < 0)
goto _err;
err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
snd_pcm_hook_ctl_elems_hw_params, sctl);
if (err < 0)
@ -662,5 +678,7 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
if (h_close)
snd_pcm_hook_remove(h_close);
snd_sctl_free(sctl);
if (pcm_conf)
snd_config_delete(pcm_conf);
return err;
}

View file

@ -669,7 +669,9 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
int err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "card") == 0) {

View file

@ -335,7 +335,9 @@ int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -663,6 +663,7 @@ static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
{
char buf[256];
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL, *str = NULL;
snd_config_t *c, *type_conf;
int (*open_func)(snd_pcm_t *, const char *,
@ -679,16 +680,23 @@ static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
SNDERR("type is not defined");
goto _err;
}
err = snd_config_get_id(c, &id);
if (err < 0) {
SNDERR("unable to get id");
goto _err;
}
err = snd_config_get_string(c, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(c));
SNDERR("Invalid type for %s", id);
goto _err;
}
err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf);
if (err >= 0) {
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
@ -745,7 +753,9 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *scopes = NULL;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {
@ -791,8 +801,9 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
return 0;
snd_config_for_each(i, next, scopes) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *str;
const char *id, *str;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_config_get_string(n, &str) >= 0) {
err = snd_config_search_definition(root, "pcm_scope", str, &n);
if (err < 0) {

View file

@ -445,7 +445,9 @@ int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -684,7 +684,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
unsigned int channels_count = 0;
snd_config_for_each(i, inext, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slaves") == 0) {
@ -731,7 +733,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
snd_config_for_each(i, inext, bindings) {
long cchannel;
snd_config_t *m = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(m);
const char *id;
if (snd_config_get_id(m, &id) < 0)
continue;
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
SNDERR("Invalid channel number: %s", id);
@ -756,8 +760,11 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
idx = 0;
snd_config_for_each(i, inext, slaves) {
snd_config_t *m = snd_config_iterator_entry(i);
const char *id;
int channels;
slaves_id[idx] = snd_config_get_id(m);
if (snd_config_get_id(m, &id) < 0)
continue;
slaves_id[idx] = id;
err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
if (err < 0)
@ -773,7 +780,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
int slave = -1;
long val;
const char *str;
const char *id = snd_config_get_id(m);
const char *id;
if (snd_config_get_id(m, &id) < 0)
continue;
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
SNDERR("Invalid channel number: %s", id);
@ -782,7 +791,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
}
snd_config_for_each(j, jnext, m) {
snd_config_t *n = snd_config_iterator_entry(j);
id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "slave") == 0) {
@ -817,13 +828,13 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
goto _free;
}
if (slave < 0 || (unsigned int)slave >= slaves_count) {
SNDERR("Invalid or missing sidx");
SNDERR("Invalid or missing sidx for channel %s", id);
err = -EINVAL;
goto _free;
}
if (schannel < 0 ||
(unsigned int) schannel >= slaves_channels[slave]) {
SNDERR("Invalid or missing schannel");
SNDERR("Invalid or missing schannel for channel %s", id);
err = -EINVAL;
goto _free;
}

View file

@ -376,7 +376,9 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
snd_config_iterator_t i, next;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
SNDERR("Unknown field %s", id);

View file

@ -828,7 +828,9 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
int schannels = -1, srate = -1;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -545,7 +545,9 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
int srate = -1;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -796,10 +796,13 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
snd_config_t *in = snd_config_iterator_entry(i);
snd_config_iterator_t j, jnext;
long cchannel;
err = safe_strtol(snd_config_get_id(in), &cchannel);
const char *id;
if (!snd_config_get_id(in, &id) < 0)
continue;
err = safe_strtol(id, &cchannel);
if (err < 0 ||
cchannel < 0 || (unsigned int) cchannel > tt_csize) {
SNDERR("Invalid client channel: %s", snd_config_get_id(in));
SNDERR("Invalid client channel: %s", id);
return -EINVAL;
}
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
@ -808,7 +811,9 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
snd_config_t *jnode = snd_config_iterator_entry(j);
double value;
long schannel;
const char *id = snd_config_get_id(jnode);
const char *id;
if (snd_config_get_id(jnode, &id) < 0)
continue;
err = safe_strtol(id, &schannel);
if (err < 0 ||
schannel < 0 || (unsigned int) schannel > tt_ssize ||
@ -855,7 +860,9 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
unsigned int cused, sused;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {

View file

@ -1388,7 +1388,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "slave") == 0) {
@ -1436,7 +1438,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
snd_config_for_each(i, next, bindings) {
long cchannel = -1;
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
SNDERR("Invalid client channel in binding: %s", id);
@ -1455,9 +1459,11 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
snd_config_for_each(i, next, bindings) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
long cchannel;
long schannel = -1;
if (snd_config_get_id(n, &id) < 0)
continue;
cchannel = atoi(id);
err = snd_config_get_integer(n, &schannel);
if (err < 0) {

View file

@ -740,7 +740,9 @@ int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
struct hostent *h;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "server") == 0) {
@ -781,7 +783,9 @@ int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
}
snd_config_for_each(i, next, sconfig) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "host") == 0) {

View file

@ -71,6 +71,7 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
snd_rawmidi_params_t params;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_rawmidi_t **, snd_rawmidi_t **,
const char *, snd_config_t *, snd_config_t *, int) = NULL;
@ -90,9 +91,14 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(rawmidi_root, "rawmidi_type", str, &type_conf);
@ -103,7 +109,9 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -317,7 +317,9 @@ int _snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
int err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)

View file

@ -73,6 +73,7 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_seq_t **, const char *,
snd_config_t *, snd_config_t *,
@ -93,9 +94,14 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(seq_root, "seq_type", str, &type_conf);
@ -106,7 +112,9 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -515,7 +515,9 @@ int _snd_seq_hw_open(snd_seq_t **handlep, char *name,
snd_config_iterator_t i, next;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)

View file

@ -45,6 +45,7 @@ static int snd_timer_open_conf(snd_timer_t **timer,
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_timer_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
@ -63,9 +64,14 @@ static int snd_timer_open_conf(snd_timer_t **timer,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(timer_root, "timer_type", str, &type_conf);
@ -76,7 +82,9 @@ static int snd_timer_open_conf(snd_timer_t **timer,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -216,7 +216,9 @@ int _snd_timer_hw_open(snd_timer_t **timer, char *name,
int err;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)

View file

@ -44,6 +44,7 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_timer_query_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
@ -62,9 +63,14 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", snd_config_get_id(conf));
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(timer_root, "timer_query_type", str, &type_conf);
@ -75,7 +81,9 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {

View file

@ -100,7 +100,9 @@ int _snd_timer_query_hw_open(snd_timer_query_t **timer, char *name,
snd_config_iterator_t i, next;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id = snd_config_get_id(n);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "type") == 0)