mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
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:
parent
ef035eacfe
commit
c39882f602
52 changed files with 1573 additions and 1042 deletions
|
|
@ -1032,7 +1032,9 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "host") == 0) {
|
if (strcmp(id, "host") == 0) {
|
||||||
|
|
|
||||||
220
doc/conf.doxygen
220
doc/conf.doxygen
|
|
@ -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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -5,8 +5,7 @@ GENERATE_MAN = NO
|
||||||
GENERATE_RTF = NO
|
GENERATE_RTF = NO
|
||||||
|
|
||||||
CASE_SENSE_NAMES = NO
|
CASE_SENSE_NAMES = NO
|
||||||
INPUT = index.doxygen conf.doxygen confarg.doxygen \
|
INPUT = index.doxygen \
|
||||||
conffunc.doxygen pcm.doxygen \
|
|
||||||
../include/asoundlib.h \
|
../include/asoundlib.h \
|
||||||
../include/version.h \
|
../include/version.h \
|
||||||
../include/global.h \
|
../include/global.h \
|
||||||
|
|
|
||||||
441
doc/pcm.doxygen
441
doc/pcm.doxygen
|
|
@ -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
|
|
||||||
*/
|
|
||||||
|
|
@ -51,8 +51,10 @@ typedef enum _snd_config_type {
|
||||||
SND_CONFIG_TYPE_REAL,
|
SND_CONFIG_TYPE_REAL,
|
||||||
/** Character string */
|
/** Character string */
|
||||||
SND_CONFIG_TYPE_STRING,
|
SND_CONFIG_TYPE_STRING,
|
||||||
|
/** Pointer - runtime only - cannot be saved */
|
||||||
|
SND_CONFIG_TYPE_POINTER,
|
||||||
/** Compound */
|
/** Compound */
|
||||||
SND_CONFIG_TYPE_COMPOUND,
|
SND_CONFIG_TYPE_COMPOUND = 1024,
|
||||||
} snd_config_type_t;
|
} snd_config_type_t;
|
||||||
|
|
||||||
/** Config node handle */
|
/** Config node handle */
|
||||||
|
|
@ -60,10 +62,13 @@ typedef struct _snd_config snd_config_t;
|
||||||
/** Config compound iterator */
|
/** Config compound iterator */
|
||||||
typedef struct _snd_config_iterator *snd_config_iterator_t;
|
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_top(snd_config_t **config);
|
||||||
|
|
||||||
int snd_config_load(snd_config_t *config, snd_input_t *in);
|
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_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,
|
int snd_config_search(snd_config_t *config, const char *key,
|
||||||
snd_config_t **result);
|
snd_config_t **result);
|
||||||
|
|
@ -74,10 +79,10 @@ int snd_config_search_definition(snd_config_t *config,
|
||||||
snd_config_t **result);
|
snd_config_t **result);
|
||||||
|
|
||||||
int snd_config_expand(snd_config_t *config, snd_config_t *root,
|
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);
|
snd_config_t **result);
|
||||||
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
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_add(snd_config_t *config, snd_config_t *leaf);
|
||||||
int snd_config_delete(snd_config_t *config);
|
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_integer(snd_config_t **config, const char *key);
|
||||||
int snd_config_make_real(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_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);
|
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_id(snd_config_t *config, const char *id);
|
||||||
int snd_config_set_integer(snd_config_t *config, long value);
|
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_real(snd_config_t *config, double value);
|
||||||
int snd_config_set_string(snd_config_t *config, const char *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_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_integer(snd_config_t *config, long *value);
|
||||||
int snd_config_get_real(snd_config_t *config, double *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_string(snd_config_t *config, const char **value);
|
||||||
int snd_config_get_ascii(snd_config_t *config, 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_first(snd_config_t *node);
|
||||||
snd_config_iterator_t snd_config_iterator_next(snd_config_iterator_t iterator);
|
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) \
|
#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))
|
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 */
|
/* Misc functions */
|
||||||
|
|
||||||
int snd_config_get_bool_ascii(const char *ascii);
|
int snd_config_get_bool_ascii(const char *ascii);
|
||||||
|
|
|
||||||
|
|
@ -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_name(int card, char **name);
|
||||||
int snd_card_get_longname(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_open(snd_ctl_t **ctl, const char *name, int mode);
|
||||||
int snd_ctl_close(snd_ctl_t *ctl);
|
int snd_ctl_close(snd_ctl_t *ctl);
|
||||||
int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __ALSA_CONTROL_H */
|
#endif /* __ALSA_CONTROL_H */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,14 @@
|
||||||
#define __ALSA_GLOBAL_H_
|
#define __ALSA_GLOBAL_H_
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \defgroup Global Global defines
|
* \defgroup Global Global defines and functions
|
||||||
* Global defines
|
* Global defines and functions.
|
||||||
* \{
|
* \{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ATTRIBUTE_UNUSED
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef PIC /* dynamic build */
|
#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);
|
snd_async_callback_t callback, void *private_data);
|
||||||
int snd_async_del_handler(snd_async_handler_t *handler);
|
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_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);
|
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler);
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
#endif /* __ALSA_GLOBAL_H */
|
#endif /* __ALSA_GLOBAL_H */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,6 @@ typedef struct sndrv_seq_event snd_seq_event_t;
|
||||||
#define SND_BIG_ENDIAN
|
#define SND_BIG_ENDIAN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int snd_async_signo;
|
|
||||||
|
|
||||||
struct _snd_async_handler {
|
struct _snd_async_handler {
|
||||||
enum {
|
enum {
|
||||||
SND_ASYNC_HANDLER_GENERIC,
|
SND_ASYNC_HANDLER_GENERIC,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
* \author Takashi Iwai <tiwai@suse.de>
|
* \author Takashi Iwai <tiwai@suse.de>
|
||||||
* \date 1998-2001
|
* \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
|
* This library is free software; you can redistribute it and/or modify
|
||||||
|
|
@ -34,7 +35,7 @@ extern "C" {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \defgroup PCM PCM Interface
|
* \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 */
|
/** PCM class */
|
||||||
typedef enum _snd_pcm_class {
|
typedef enum _snd_pcm_class {
|
||||||
/** standard device */
|
/** standard device */
|
||||||
|
|
||||||
SND_PCM_CLASS_GENERIC = 0,
|
SND_PCM_CLASS_GENERIC = 0,
|
||||||
/** multichannel device */
|
/** multichannel device */
|
||||||
SND_PCM_CLASS_MULTI,
|
SND_PCM_CLASS_MULTI,
|
||||||
|
|
@ -388,8 +390,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \defgroup PCM_Info Stream Information
|
* \defgroup PCM_Info Stream Information
|
||||||
* PCM Stream Information
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_HW_Params Hardware Parameters
|
||||||
* PCM Hardware Parameters
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_SW_Params Software Parameters
|
||||||
* PCM Software Parameters
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Access Access Mask Functions
|
||||||
* PCM Access Mask Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Format Format Mask Functions
|
||||||
* PCM Format Mask Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_SubFormat Subformat Mask Functions
|
||||||
* PCM Subformat Mask Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Status Status Functions
|
||||||
* PCM Status Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Description Description Functions
|
||||||
* PCM Description Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Dump Debug Functions
|
||||||
* PCM Debug Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Direct Direct Access (MMAP) Functions
|
||||||
* PCM Direct Access (MMAP) Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Helpers Helper Functions
|
||||||
* PCM Helper Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Hook Hook Extension
|
||||||
* PCM Hook Extension
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Scope Scope Plugin Extension
|
||||||
* PCM Scope Plugin Extension
|
|
||||||
* \ingroup PCM
|
* \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
|
* \defgroup PCM_Deprecated Deprecated Functions
|
||||||
* PCM Deprecated Functions
|
|
||||||
* \ingroup PCM
|
* \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
|
||||||
|
|
||||||
#endif /* __ALSA_PCM_H */
|
#endif /* __ALSA_PCM_H */
|
||||||
|
|
||||||
|
|
|
||||||
25
src/async.c
25
src/async.c
|
|
@ -30,7 +30,8 @@
|
||||||
|
|
||||||
#ifdef SND_ASYNC_RT_SIGNAL
|
#ifdef SND_ASYNC_RT_SIGNAL
|
||||||
/** async signal number */
|
/** async signal number */
|
||||||
int snd_async_signo;
|
static int snd_async_signo;
|
||||||
|
|
||||||
void snd_async_init(void) __attribute__ ((constructor));
|
void snd_async_init(void) __attribute__ ((constructor));
|
||||||
|
|
||||||
void snd_async_init(void)
|
void snd_async_init(void)
|
||||||
|
|
@ -43,7 +44,7 @@ void snd_async_init(void)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/** async signal number */
|
/** async signal number */
|
||||||
int snd_async_signo = SIGIO;
|
static int snd_async_signo = SIGIO;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static LIST_HEAD(snd_async_handlers);
|
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 callback - Async callback
|
||||||
* \param private_data - Private data for async callback
|
* \param private_data - Private data for async callback
|
||||||
* \result zero if success, otherwise a negative error code
|
* \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,
|
int snd_async_add_handler(snd_async_handler_t **handler, int fd,
|
||||||
snd_async_callback_t callback, void *private_data)
|
snd_async_callback_t callback, void *private_data)
|
||||||
{
|
{
|
||||||
snd_async_handler_t *h;
|
snd_async_handler_t *h;
|
||||||
int was_empty;
|
int was_empty;
|
||||||
|
assert(handler);
|
||||||
h = malloc(sizeof(*h));
|
h = malloc(sizeof(*h));
|
||||||
if (!h)
|
if (!h)
|
||||||
return -ENOMEM;
|
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 snd_async_del_handler(snd_async_handler_t *handler)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
assert(handler);
|
||||||
list_del(&handler->glist);
|
list_del(&handler->glist);
|
||||||
if (list_empty(&snd_async_handlers)) {
|
if (list_empty(&snd_async_handlers)) {
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
@ -139,6 +147,17 @@ int snd_async_del_handler(snd_async_handler_t *handler)
|
||||||
return err;
|
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
|
* \brief Get file descriptor assigned to async handler
|
||||||
* \param handler 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)
|
int snd_async_handler_get_fd(snd_async_handler_t *handler)
|
||||||
{
|
{
|
||||||
|
assert(handler);
|
||||||
return handler->fd;
|
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)
|
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler)
|
||||||
{
|
{
|
||||||
|
assert(handler);
|
||||||
return handler->private_data;
|
return handler->private_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
431
src/conf.c
431
src/conf.c
|
|
@ -5,7 +5,7 @@
|
||||||
* \author Jaroslav Kysela <perex@suse.cz>
|
* \author Jaroslav Kysela <perex@suse.cz>
|
||||||
* \date 2000-2001
|
* \date 2000-2001
|
||||||
*
|
*
|
||||||
* Generic stdio-like input interface
|
* Tree based, full nesting configuration functions.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Configuration helper 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 <stdarg.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
@ -44,6 +344,7 @@ struct _snd_config {
|
||||||
long integer;
|
long integer;
|
||||||
char *string;
|
char *string;
|
||||||
double real;
|
double real;
|
||||||
|
const void *ptr;
|
||||||
struct {
|
struct {
|
||||||
struct list_head fields;
|
struct list_head fields;
|
||||||
int join;
|
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:
|
case SND_CONFIG_TYPE_STRING:
|
||||||
string_print(n->u.string, 0, out);
|
string_print(n->u.string, 0, out);
|
||||||
break;
|
break;
|
||||||
|
case SND_CONFIG_TYPE_POINTER:
|
||||||
|
SNDERR("cannot save runtime pointer type");
|
||||||
|
return -EINVAL;
|
||||||
case SND_CONFIG_TYPE_COMPOUND:
|
case SND_CONFIG_TYPE_COMPOUND:
|
||||||
snd_output_putc(out, '{');
|
snd_output_putc(out, '{');
|
||||||
snd_output_putc(out, '\n');
|
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
|
* \brief Return id of a config node
|
||||||
* \param config Config node handle
|
* \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);
|
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
|
* \brief Build an empty compound config node
|
||||||
* \param config Returned config node handle pointer
|
* \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;
|
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
|
* \brief Change the value of a config node
|
||||||
* \param config Config node handle
|
* \param config Config node handle
|
||||||
|
|
@ -1399,6 +1732,21 @@ int snd_config_get_string(snd_config_t *config, const char **ptr)
|
||||||
return 0;
|
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
|
* \brief Get the value in ASCII form
|
||||||
* \param config Config node handle
|
* \param config Config node handle
|
||||||
|
|
@ -1453,6 +1801,18 @@ int snd_config_get_ascii(snd_config_t *config, char **ascii)
|
||||||
return 0;
|
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
|
* \brief Dump a config tree contents
|
||||||
* \param config Config node handle
|
* \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)
|
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, \
|
SND_CONFIG_SEARCH(config, key, result, \
|
||||||
err = snd_config_hooks(config, NULL); \
|
err = snd_config_hooks(config, NULL); \
|
||||||
if (err < 0) \
|
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)
|
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(root, config, key, result,
|
||||||
snd_config_searcha_hooks,
|
snd_config_searcha_hooks,
|
||||||
err = snd_config_hooks(config, NULL); \
|
err = snd_config_hooks(config, NULL); \
|
||||||
|
|
@ -1745,14 +2105,14 @@ static struct finfo {
|
||||||
|
|
||||||
static unsigned int files_info_count = 0;
|
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;
|
void *h = NULL;
|
||||||
snd_config_t *c, *func_conf = NULL;
|
snd_config_t *c, *func_conf = NULL;
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
const char *lib = NULL, *func_name = NULL;
|
const char *lib = NULL, *func_name = NULL;
|
||||||
const char *str;
|
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;
|
int err;
|
||||||
|
|
||||||
err = snd_config_search(config, "func", &c);
|
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_for_each(i, next, func_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
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;
|
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_t *n;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
|
@ -1846,7 +2206,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
|
||||||
hit = 0;
|
hit = 0;
|
||||||
snd_config_for_each(i, next, n) {
|
snd_config_for_each(i, next, n) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id = n->id;
|
||||||
long i;
|
long i;
|
||||||
err = safe_strtol(id, &i);
|
err = safe_strtol(id, &i);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
@ -1877,7 +2237,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
|
||||||
* \param private_data Private data
|
* \param private_data Private data
|
||||||
* \return zero if success, otherwise a negative error code
|
* \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_t *n, *res = NULL;
|
||||||
snd_config_iterator_t i, next;
|
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);
|
snd_config_t *c = snd_config_iterator_entry(i);
|
||||||
const char *str;
|
const char *str;
|
||||||
if ((err = snd_config_get_string(c, &str)) < 0) {
|
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;
|
goto _err;
|
||||||
}
|
}
|
||||||
fi_count++;
|
fi_count++;
|
||||||
|
|
@ -1927,7 +2287,7 @@ int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t
|
||||||
hit = 0;
|
hit = 0;
|
||||||
snd_config_for_each(i, next, n) {
|
snd_config_for_each(i, next, n) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id = n->id;
|
||||||
long i;
|
long i;
|
||||||
err = safe_strtol(id, &i);
|
err = safe_strtol(id, &i);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
@ -2014,7 +2374,7 @@ int snd_determine_driver(int card, char **driver);
|
||||||
* \param private_data Private data
|
* \param private_data Private data
|
||||||
* \return zero if success, otherwise a negative error code
|
* \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;
|
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)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
if (card >= 0) {
|
if (card >= 0) {
|
||||||
snd_config_t *n;
|
snd_config_t *n, *private_data = NULL;
|
||||||
const char *driver;
|
const char *driver;
|
||||||
char *fdriver = NULL;
|
char *fdriver = NULL;
|
||||||
err = snd_determine_driver(card, &fdriver);
|
err = snd_determine_driver(card, &fdriver);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
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_search(root, fdriver, &n) >= 0) {
|
||||||
if (snd_config_get_string(n, &driver) < 0)
|
if (snd_config_get_string(n, &driver) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -2043,7 +2407,10 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
|
||||||
} else {
|
} else {
|
||||||
driver = fdriver;
|
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)
|
if (fdriver)
|
||||||
free(fdriver);
|
free(fdriver);
|
||||||
if (err < 0)
|
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 *root,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
void *private_data);
|
snd_config_t *private_data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int snd_config_walk(snd_config_t *src,
|
static int snd_config_walk(snd_config_t *src,
|
||||||
snd_config_t *root,
|
snd_config_t *root,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_callback_t callback,
|
snd_config_walk_callback_t callback,
|
||||||
void *private_data)
|
snd_config_t *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
snd_config_iterator_t i, next;
|
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 *root ATTRIBUTE_UNUSED,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
void *private_data ATTRIBUTE_UNUSED)
|
snd_config_t *private_data ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
int err;
|
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);
|
snd_config_type_t type = snd_config_get_type(src);
|
||||||
switch (pass) {
|
switch (pass) {
|
||||||
case SND_CONFIG_WALK_PASS_PRE:
|
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 *root ATTRIBUTE_UNUSED,
|
||||||
snd_config_t **dst,
|
snd_config_t **dst,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
void *private_data)
|
snd_config_t *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
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);
|
snd_config_type_t type = snd_config_get_type(src);
|
||||||
switch (pass) {
|
switch (pass) {
|
||||||
case SND_CONFIG_WALK_PASS_PRE:
|
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 *root,
|
||||||
snd_config_t **dst ATTRIBUTE_UNUSED,
|
snd_config_t **dst ATTRIBUTE_UNUSED,
|
||||||
snd_config_walk_pass_t pass,
|
snd_config_walk_pass_t pass,
|
||||||
void *private_data)
|
snd_config_t *private_data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
if (pass == SND_CONFIG_WALK_PASS_PRE) {
|
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 *lib = NULL, *func_name = NULL;
|
||||||
const char *str;
|
const char *str;
|
||||||
int (*func)(snd_config_t **dst, snd_config_t *root,
|
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;
|
void *h = NULL;
|
||||||
snd_config_t *c, *func_conf = NULL;
|
snd_config_t *c, *func_conf = NULL;
|
||||||
err = snd_config_search(src, "@func", &c);
|
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_for_each(i, next, func_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
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
|
* \return zero if success, otherwise a negative error code
|
||||||
*/
|
*/
|
||||||
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
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 */
|
/* FIXME: Only in place evaluation is currently implemented */
|
||||||
assert(result == NULL);
|
assert(result == NULL);
|
||||||
|
|
@ -2569,7 +2936,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
||||||
continue;
|
continue;
|
||||||
snd_config_for_each(f, fnext, def) {
|
snd_config_for_each(f, fnext, def) {
|
||||||
snd_config_t *fld = snd_config_iterator_entry(f);
|
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)
|
if (strcmp(id, "type") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "default") == 0) {
|
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);
|
err = snd_config_copy(&deflt, fld);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_config_set_id(deflt, snd_config_get_id(def));
|
err = snd_config_set_id(deflt, def->id);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
snd_config_delete(deflt);
|
snd_config_delete(deflt);
|
||||||
return err;
|
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_for_each(i, next, subs) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
snd_config_t *d;
|
snd_config_t *d;
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id = n->id;
|
||||||
err = snd_config_search(defs, id, &d);
|
err = snd_config_search(defs, id, &d);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Unknown parameter %s", id);
|
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;
|
err = -EINVAL;
|
||||||
goto _err;
|
goto _err;
|
||||||
}
|
}
|
||||||
var = snd_config_get_id(def);
|
var = def->id;
|
||||||
err = snd_config_search(subs, var, &sub);
|
err = snd_config_search(subs, var, &sub);
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
snd_config_delete(sub);
|
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
|
* \return 0 on success otherwise a negative error code
|
||||||
*/
|
*/
|
||||||
int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,
|
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;
|
int err;
|
||||||
snd_config_t *defs, *subs = NULL, *res;
|
snd_config_t *defs, *subs = NULL, *res;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pcm.!center_lfe {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.center_lfe." $DEV ":CARD=" $CARD
|
".pcm.center_lfe." $DEV ":CARD=" $CARD
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pcm.!front {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.front." $DEV ":CARD=" $CARD
|
".pcm.front." $DEV ":CARD=" $CARD
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ pcm.!iec958 {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.iec958." $DEV ":"
|
".pcm.iec958." $DEV ":"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pcm.!rear {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.rear." $DEV ":CARD=" $CARD
|
".pcm.rear." $DEV ":CARD=" $CARD
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pcm.!surround40 {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.surround40." $DEV ":CARD=" $CARD
|
".pcm.surround40." $DEV ":CARD=" $CARD
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ pcm.!surround51 {
|
||||||
strings [
|
strings [
|
||||||
"cards."
|
"cards."
|
||||||
{
|
{
|
||||||
@func card_strtype
|
@func card_driver
|
||||||
card $CARD
|
card $CARD
|
||||||
}
|
}
|
||||||
".pcm.surround51." $DEV ":CARD=" $CARD
|
".pcm.surround51." $DEV ":CARD=" $CARD
|
||||||
|
|
|
||||||
472
src/confmisc.c
472
src/confmisc.c
|
|
@ -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
|
* Miscellaneous configuration helper functions
|
||||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
|
* 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 <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.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)
|
int snd_config_get_bool(snd_config_t *conf)
|
||||||
{
|
{
|
||||||
long v;
|
long v;
|
||||||
const char *str;
|
const char *str, *id;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_get_id(conf, &id);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
err = snd_config_get_integer(conf, &v);
|
err = snd_config_get_integer(conf, &v);
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
if (v < 0 || v > 1) {
|
if (v < 0 || v > 1) {
|
||||||
_invalid_value:
|
_invalid_value:
|
||||||
SNDERR("Invalid value for %s", snd_config_get_id(conf));
|
SNDERR("Invalid value for %s", id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
err = snd_config_get_string(conf, &str);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
err = snd_config_get_bool_ascii(str);
|
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)
|
int snd_config_get_ctl_iface(snd_config_t *conf)
|
||||||
{
|
{
|
||||||
long v;
|
long v;
|
||||||
const char *str;
|
const char *str, *id;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
err = snd_config_get_id(conf, &id);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
err = snd_config_get_integer(conf, &v);
|
err = snd_config_get_integer(conf, &v);
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) {
|
if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) {
|
||||||
_invalid_value:
|
_invalid_value:
|
||||||
SNDERR("Invalid value for %s", snd_config_get_id(conf));
|
SNDERR("Invalid value for %s", id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
err = snd_config_get_string(conf, &str);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
err = snd_config_get_ctl_iface_ascii(str);
|
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
|
* 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_t *n, *d;
|
||||||
snd_config_iterator_t i, next;
|
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;
|
hit = 0;
|
||||||
snd_config_for_each(i, next, n) {
|
snd_config_for_each(i, next, n) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id, *ptr, *env;
|
||||||
const char *ptr, *env;
|
|
||||||
long i;
|
long i;
|
||||||
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
|
continue;
|
||||||
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
|
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
|
||||||
SNDERR("field %s is not a string", id);
|
SNDERR("field %s is not a string", id);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
@ -209,9 +277,13 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
||||||
__ok:
|
__ok:
|
||||||
err = res == NULL ? -ENOMEM : 0;
|
err = res == NULL ? -ENOMEM : 0;
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
const char *id;
|
||||||
if (err >= 0)
|
err = snd_config_get_id(src, &id);
|
||||||
snd_config_set_string(*dst, res);
|
if (err >= 0) {
|
||||||
|
err = snd_config_make_string(dst, id);
|
||||||
|
if (err >= 0)
|
||||||
|
snd_config_set_string(*dst, res);
|
||||||
|
}
|
||||||
free(res);
|
free(res);
|
||||||
}
|
}
|
||||||
__error:
|
__error:
|
||||||
|
|
@ -219,38 +291,80 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
||||||
free(def);
|
free(def);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_getenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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;
|
snd_config_t *d;
|
||||||
const char *str;
|
const char *str, *id;
|
||||||
int err;
|
int err;
|
||||||
long v;
|
long v;
|
||||||
|
|
||||||
err = snd_func_getenv(&d, root, src, private_data);
|
err = snd_func_getenv(&d, root, src, private_data);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_config_get_string(d, &str);
|
err = snd_config_get_string(d, &str);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _end;
|
return err;
|
||||||
err = safe_strtol(str, &v);
|
err = safe_strtol(str, &v);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _end;
|
return err;;
|
||||||
err = snd_config_make_integer(dst, snd_config_get_id(src));
|
err = snd_config_get_id(src, &id);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto _end;
|
return err;
|
||||||
|
err = snd_config_make_integer(dst, id);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
snd_config_set_integer(*dst, v);
|
snd_config_set_integer(*dst, v);
|
||||||
err = 0;
|
return 0;
|
||||||
|
|
||||||
_end:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_igetenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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_t *n;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
char *res = NULL, *tmp;
|
char *res = NULL, *tmp;
|
||||||
int idx = 0, len = 0, len1, err, hit;
|
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_for_each(i, next, n) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
char *ptr;
|
char *ptr;
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id;
|
||||||
long i;
|
long i;
|
||||||
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
|
continue;
|
||||||
err = safe_strtol(id, &i);
|
err = safe_strtol(id, &i);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("id of field %s is not an integer", id);
|
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;
|
err = -EINVAL;
|
||||||
goto __error;
|
goto __error;
|
||||||
}
|
}
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
err = snd_config_get_id(src, &id);
|
||||||
if (err >= 0)
|
if (err >= 0) {
|
||||||
snd_config_set_string(*dst, res);
|
err = snd_config_make_string(dst, id);
|
||||||
|
if (err >= 0)
|
||||||
|
snd_config_set_string(*dst, res);
|
||||||
|
}
|
||||||
free(res);
|
free(res);
|
||||||
__error:
|
__error:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_concat, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
SND_DLSYM_BUILD_VERSION(snd_func_concat, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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
|
||||||
|
{
|
||||||
|
@func datadir
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
||||||
snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
|
snd_config_t *src, snd_config_t *private_data ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
int err = snd_config_make_string(dst, snd_config_get_id(src));
|
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)
|
if (err >= 0)
|
||||||
err = snd_config_set_string(*dst, DATADIR "/alsa");
|
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);
|
SND_DLSYM_BUILD_VERSION(snd_func_datadir, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||||
|
#endif
|
||||||
|
|
||||||
static int open_ctl(long card, snd_ctl_t **ctl)
|
static int open_ctl(long card, snd_ctl_t **ctl)
|
||||||
{
|
{
|
||||||
|
|
@ -344,19 +488,53 @@ static int string_from_integer(char **dst, long v)
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
int err;
|
||||||
|
snd_config_t *n;
|
||||||
|
const char *str, *id;
|
||||||
|
|
||||||
if (private_data == NULL)
|
if (private_data == NULL)
|
||||||
return snd_config_copy(dst, src);
|
return snd_config_copy(dst, src);
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
err = snd_config_test_id(private_data, "string");
|
||||||
if (err >= 0)
|
if (err) {
|
||||||
err = snd_config_set_string(*dst, (char *)private_data);
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_private_string, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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)
|
int snd_determine_driver(int card, char **driver)
|
||||||
{
|
{
|
||||||
snd_ctl_t *ctl = NULL;
|
snd_ctl_t *ctl = NULL;
|
||||||
|
|
@ -388,25 +566,77 @@ int snd_determine_driver(int card, char **driver)
|
||||||
snd_ctl_close(ctl);
|
snd_ctl_close(ctl);
|
||||||
return err;
|
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;
|
char *driver;
|
||||||
|
snd_config_t *n;
|
||||||
|
const char *id;
|
||||||
int err;
|
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;
|
return err;
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
}
|
||||||
if (err >= 0)
|
if ((err = snd_determine_driver(card, &driver)) < 0)
|
||||||
err = snd_config_set_string(*dst, driver);
|
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);
|
free(driver);
|
||||||
return err;
|
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
|
||||||
|
{
|
||||||
|
@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;
|
snd_config_t *n, *val;
|
||||||
char *str;
|
char *str;
|
||||||
long v;
|
long v;
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -433,16 +663,42 @@ int snd_func_card_strtype(snd_config_t **dst, snd_config_t *root, snd_config_t *
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
free(str);
|
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;
|
snd_config_t *n;
|
||||||
char *res = NULL;
|
char *res = NULL;
|
||||||
snd_ctl_t *ctl = NULL;
|
snd_ctl_t *ctl = NULL;
|
||||||
snd_ctl_card_info_t *info;
|
snd_ctl_card_info_t *info;
|
||||||
|
const char *id;
|
||||||
long v;
|
long v;
|
||||||
int err;
|
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;
|
err = -ENOMEM;
|
||||||
goto __error;
|
goto __error;
|
||||||
}
|
}
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
err = snd_config_get_id(src, &id);
|
||||||
if (err >= 0)
|
if (err >= 0) {
|
||||||
err = snd_config_set_string(*dst, res);
|
err = snd_config_make_string(dst, id);
|
||||||
|
if (err >= 0)
|
||||||
|
err = snd_config_set_string(*dst, res);
|
||||||
|
}
|
||||||
free(res);
|
free(res);
|
||||||
__error:
|
__error:
|
||||||
if (ctl)
|
if (ctl)
|
||||||
snd_ctl_close(ctl);
|
snd_ctl_close(ctl);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_card_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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)
|
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_config_t *n;
|
||||||
snd_ctl_t *ctl = NULL;
|
snd_ctl_t *ctl = NULL;
|
||||||
snd_pcm_info_t *info;
|
snd_pcm_info_t *info;
|
||||||
|
const char *id;
|
||||||
long card, device, subdevice = 0;
|
long card, device, subdevice = 0;
|
||||||
int err;
|
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));
|
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
|
||||||
goto __error;
|
goto __error;
|
||||||
}
|
}
|
||||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
err = snd_config_get_id(src, &id);
|
||||||
if (err >= 0)
|
if (err >= 0) {
|
||||||
err = snd_config_set_string(*dst, snd_pcm_info_get_id(info));
|
err = snd_config_make_string(dst, id);
|
||||||
|
if (err >= 0)
|
||||||
|
err = snd_config_set_string(*dst, snd_pcm_info_get_id(info));
|
||||||
|
}
|
||||||
__error:
|
__error:
|
||||||
if (ctl)
|
if (ctl)
|
||||||
snd_ctl_close(ctl);
|
snd_ctl_close(ctl);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_pcm_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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_pcm_info_t *info;
|
||||||
|
snd_config_t *n;
|
||||||
|
const char *id;
|
||||||
|
snd_pcm_t *pcm;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (private_data == NULL)
|
if (private_data == NULL)
|
||||||
return snd_config_copy(dst, src);
|
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);
|
snd_pcm_info_alloca(&info);
|
||||||
err = snd_pcm_info((snd_pcm_t *)private_data, info);
|
err = snd_pcm_info(pcm, info);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
|
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
res = strdup(snd_pcm_info_get_id(info));
|
err = snd_config_get_id(src, &id);
|
||||||
if (res == NULL)
|
if (err >= 0) {
|
||||||
return -ENOMEM;
|
err = snd_config_make_integer(dst, id);
|
||||||
err = snd_config_make_integer(dst, snd_config_get_id(src));
|
if (err >= 0)
|
||||||
if (err >= 0)
|
err = snd_config_set_integer(*dst, snd_pcm_info_get_subdevice(info));
|
||||||
err = snd_config_set_integer(*dst, snd_pcm_info_get_subdevice(info));
|
}
|
||||||
free(res);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_private_pcm_subdevice, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
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;
|
snd_config_t *n;
|
||||||
const char *file = NULL, *name = NULL;
|
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;
|
goto _end;
|
||||||
}
|
}
|
||||||
err = snd_config_load(root, input);
|
err = snd_config_load(root, input);
|
||||||
if (err < 0) {
|
snd_input_close(input);
|
||||||
snd_input_close(input);
|
if (err < 0)
|
||||||
goto _end;
|
goto _end;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(root, NULL, name, dst);
|
err = snd_config_search_definition(root, NULL, name, dst);
|
||||||
if (err >= 0)
|
if (err >= 0) {
|
||||||
err = snd_config_set_id(*dst, snd_config_get_id(src));
|
const char *id;
|
||||||
else
|
err = snd_config_get_id(src, &id);
|
||||||
|
if (err >= 0)
|
||||||
|
err = snd_config_set_id(*dst, id);
|
||||||
|
} else
|
||||||
SNDERR("Unable to find definition '%s'", name);
|
SNDERR("Unable to find definition '%s'", name);
|
||||||
_end:
|
_end:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifndef DOC_HIDDEN
|
||||||
SND_DLSYM_BUILD_VERSION(snd_func_refer, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
SND_DLSYM_BUILD_VERSION(snd_func_refer, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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);
|
was_empty = list_empty(&ctl->async_handlers);
|
||||||
list_add_tail(&h->hlist, &ctl->async_handlers);
|
list_add_tail(&h->hlist, &ctl->async_handlers);
|
||||||
if (was_empty) {
|
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) {
|
if (err < 0) {
|
||||||
snd_async_del_handler(h);
|
snd_async_del_handler(h);
|
||||||
return err;
|
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_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
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;
|
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||||
#ifndef PIC
|
#ifndef PIC
|
||||||
extern void *snd_control_open_symbols(void);
|
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");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,9 @@ int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBU
|
||||||
int err;
|
int err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
|
|
@ -572,7 +572,9 @@ int _snd_ctl_shm_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_c
|
||||||
struct hostent *h;
|
struct hostent *h;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
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_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "host") == 0) {
|
if (strcmp(id, "host") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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) {
|
if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
|
||||||
SNDERR("bad value index");
|
SNDERR("bad value index");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -381,7 +384,7 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf,
|
||||||
return 0;
|
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_t *conf;
|
||||||
snd_config_iterator_t i, next;
|
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;
|
return err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
|
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
|
* \param mode Build mode - SND_SCTL_xxxx
|
||||||
* \result zero if success, otherwise a negative error code
|
* \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_sctl_t *h;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
|
|
||||||
17
src/dlmisc.c
17
src/dlmisc.c
|
|
@ -41,6 +41,9 @@ struct snd_dlsym_link *snd_dlsym_start = NULL;
|
||||||
* \param name name, similar to dlopen
|
* \param name name, similar to dlopen
|
||||||
* \param mode mode, similar to dlopen
|
* \param mode mode, similar to dlopen
|
||||||
* \return pointer to handle
|
* \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)
|
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
|
* \brief Close the dynamic library, with ALSA extension
|
||||||
* \param handle handle, similar to dlclose
|
* \param handle handle, similar to dlclose
|
||||||
* \return zero if success, otherwise an error code
|
* \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)
|
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)
|
if (handle == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
vname = alloca(1 + strlen(name) + strlen(version) + 1);
|
vname = alloca(1 + strlen(name) + strlen(version) + 1);
|
||||||
|
if (vname == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
vname[0] = '_';
|
vname[0] = '_';
|
||||||
strcpy(vname + 1, name);
|
strcpy(vname + 1, name);
|
||||||
strcat(vname, version);
|
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);
|
SNDERR("unable to verify version for symbol %s", name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Resolve the symbol, with ALSA extension
|
* \brief Resolve the symbol, with ALSA extension
|
||||||
* \param handle handle, similar to dlsym
|
* \param handle handle, similar to dlsym
|
||||||
* \param name symbol name
|
* \param name symbol name
|
||||||
* \param version symbol version
|
* \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)
|
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
|
#ifndef PIC
|
||||||
if (handle == &snd_dlsym_start) {
|
if (handle == &snd_dlsym_start) {
|
||||||
/* it's the funny part, we are looking for a symbol */
|
/* it's the funny part: */
|
||||||
/* in a static library */
|
/* we are looking for a symbol in a static library */
|
||||||
struct snd_dlsym_link *link = snd_dlsym_start;
|
struct snd_dlsym_link *link = snd_dlsym_start;
|
||||||
while (link) {
|
while (link) {
|
||||||
if (!strcmp(name, link->dlsym_name))
|
if (!strcmp(name, link->dlsym_name))
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
|
||||||
int err;
|
int err;
|
||||||
snd_config_t *conf, *type_conf = NULL;
|
snd_config_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_hwdep_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
int (*open_func)(snd_hwdep_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||||
#ifndef PIC
|
#ifndef PIC
|
||||||
|
|
@ -64,9 +65,14 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
|
||||||
SNDERR("type is not defined");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(hwdep_root, "hwdep_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,9 @@ int _snd_hwdep_hw_open(snd_hwdep_t **hwdep, char *name,
|
||||||
int err;
|
int err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
464
src/pcm/pcm.c
464
src/pcm/pcm.c
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* \file pcm/pcm.c
|
* \file pcm/pcm.c
|
||||||
|
* \ingroup PCM
|
||||||
* \brief PCM Interface
|
* \brief PCM Interface
|
||||||
* \author Jaroslav Kysela <perex@suse.cz>
|
* \author Jaroslav Kysela <perex@suse.cz>
|
||||||
* \author Abramo Bagnara <abramo@alsa-project.org>
|
* \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
|
* 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
|
* frame may be obtained from bits needed to store a sample and
|
||||||
* channels count.
|
* channels count.
|
||||||
|
*
|
||||||
|
* See the \ref pcm page for more details.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* PCM Interface - main file
|
* 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 <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.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);
|
was_empty = list_empty(&pcm->async_handlers);
|
||||||
list_add_tail(&h->hlist, &pcm->async_handlers);
|
list_add_tail(&h->hlist, &pcm->async_handlers);
|
||||||
if (was_empty) {
|
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) {
|
if (err < 0) {
|
||||||
snd_async_del_handler(h);
|
snd_async_del_handler(h);
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -1025,6 +1471,7 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
|
||||||
int err;
|
int err;
|
||||||
snd_config_t *conf, *type_conf = NULL;
|
snd_config_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_pcm_t **, const char *,
|
int (*open_func)(snd_pcm_t **, const char *,
|
||||||
snd_config_t *, snd_config_t *,
|
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");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
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);
|
va_end(args);
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "pcm") == 0) {
|
if (strcmp(id, "pcm") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -557,7 +557,9 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_pcm_format_t sformat;
|
snd_pcm_format_t sformat;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,9 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_pcm_format_t sformat;
|
snd_pcm_format_t sformat;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,9 @@ int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_config_t *slave = NULL, *sconf;
|
snd_config_t *slave = NULL, *sconf;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,9 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
||||||
long fd = -1;
|
long fd = -1;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
const char *str;
|
const char *str, *id;
|
||||||
const char *lib = NULL, *install = NULL;
|
const char *lib = NULL, *install = NULL;
|
||||||
snd_config_t *type = NULL, *args = NULL;
|
snd_config_t *type = NULL, *args = NULL;
|
||||||
snd_config_iterator_t i, next;
|
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_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0) {
|
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");
|
SNDERR("type is not defined");
|
||||||
return -EINVAL;
|
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);
|
err = snd_config_get_string(type, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(type));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
|
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_for_each(i, next, type) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
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_t *hooks = NULL;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
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];
|
char ctl_name[16];
|
||||||
snd_ctl_t *ctl;
|
snd_ctl_t *ctl;
|
||||||
snd_sctl_t *sctl;
|
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;
|
snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
|
||||||
assert(conf);
|
assert(conf);
|
||||||
assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
|
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);
|
SNDERR("Cannot open CTL %s", ctl_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_sctl_build(&sctl, ctl, conf, pcm, 0);
|
err = snd_config_make_pointer(&pcm_conf, "pcm_handle");
|
||||||
if (err < 0)
|
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,
|
err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
|
||||||
snd_pcm_hook_ctl_elems_hw_params, sctl);
|
snd_pcm_hook_ctl_elems_hw_params, sctl);
|
||||||
if (err < 0)
|
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)
|
if (h_close)
|
||||||
snd_pcm_hook_remove(h_close);
|
snd_pcm_hook_remove(h_close);
|
||||||
snd_sctl_free(sctl);
|
snd_sctl_free(sctl);
|
||||||
|
if (pcm_conf)
|
||||||
|
snd_config_delete(pcm_conf);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,9 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int err;
|
int err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "card") == 0) {
|
if (strcmp(id, "card") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,9 @@ int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_pcm_format_t sformat;
|
snd_pcm_format_t sformat;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,7 @@ static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[256];
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL, *str = NULL;
|
const char *lib = NULL, *open_name = NULL, *str = NULL;
|
||||||
snd_config_t *c, *type_conf;
|
snd_config_t *c, *type_conf;
|
||||||
int (*open_func)(snd_pcm_t *, const char *,
|
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");
|
SNDERR("type is not defined");
|
||||||
goto _err;
|
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);
|
err = snd_config_get_string(c, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(c));
|
SNDERR("Invalid type for %s", id);
|
||||||
goto _err;
|
goto _err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf);
|
err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf);
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
snd_config_for_each(i, next, type_conf) {
|
snd_config_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
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_t *scopes = NULL;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
@ -791,8 +801,9 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
|
||||||
return 0;
|
return 0;
|
||||||
snd_config_for_each(i, next, scopes) {
|
snd_config_for_each(i, next, scopes) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id, *str;
|
||||||
const char *str;
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
|
continue;
|
||||||
if (snd_config_get_string(n, &str) >= 0) {
|
if (snd_config_get_string(n, &str) >= 0) {
|
||||||
err = snd_config_search_definition(root, "pcm_scope", str, &n);
|
err = snd_config_search_definition(root, "pcm_scope", str, &n);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,9 @@ int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_pcm_format_t sformat;
|
snd_pcm_format_t sformat;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -684,7 +684,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
||||||
unsigned int channels_count = 0;
|
unsigned int channels_count = 0;
|
||||||
snd_config_for_each(i, inext, conf) {
|
snd_config_for_each(i, inext, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slaves") == 0) {
|
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) {
|
snd_config_for_each(i, inext, bindings) {
|
||||||
long cchannel;
|
long cchannel;
|
||||||
snd_config_t *m = snd_config_iterator_entry(i);
|
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);
|
err = safe_strtol(id, &cchannel);
|
||||||
if (err < 0 || cchannel < 0) {
|
if (err < 0 || cchannel < 0) {
|
||||||
SNDERR("Invalid channel number: %s", id);
|
SNDERR("Invalid channel number: %s", id);
|
||||||
|
|
@ -756,8 +760,11 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
||||||
idx = 0;
|
idx = 0;
|
||||||
snd_config_for_each(i, inext, slaves) {
|
snd_config_for_each(i, inext, slaves) {
|
||||||
snd_config_t *m = snd_config_iterator_entry(i);
|
snd_config_t *m = snd_config_iterator_entry(i);
|
||||||
|
const char *id;
|
||||||
int channels;
|
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,
|
err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
|
||||||
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
|
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -773,7 +780,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int slave = -1;
|
int slave = -1;
|
||||||
long val;
|
long val;
|
||||||
const char *str;
|
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);
|
err = safe_strtol(id, &cchannel);
|
||||||
if (err < 0 || cchannel < 0) {
|
if (err < 0 || cchannel < 0) {
|
||||||
SNDERR("Invalid channel number: %s", id);
|
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_for_each(j, jnext, m) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(j);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
@ -817,13 +828,13 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
||||||
goto _free;
|
goto _free;
|
||||||
}
|
}
|
||||||
if (slave < 0 || (unsigned int)slave >= slaves_count) {
|
if (slave < 0 || (unsigned int)slave >= slaves_count) {
|
||||||
SNDERR("Invalid or missing sidx");
|
SNDERR("Invalid or missing sidx for channel %s", id);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto _free;
|
goto _free;
|
||||||
}
|
}
|
||||||
if (schannel < 0 ||
|
if (schannel < 0 ||
|
||||||
(unsigned int) schannel >= slaves_channels[slave]) {
|
(unsigned int) schannel >= slaves_channels[slave]) {
|
||||||
SNDERR("Invalid or missing schannel");
|
SNDERR("Invalid or missing schannel for channel %s", id);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto _free;
|
goto _free;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,9 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
SNDERR("Unknown field %s", id);
|
SNDERR("Unknown field %s", id);
|
||||||
|
|
|
||||||
|
|
@ -828,7 +828,9 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int schannels = -1, srate = -1;
|
int schannels = -1, srate = -1;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -545,7 +545,9 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int srate = -1;
|
int srate = -1;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_t *in = snd_config_iterator_entry(i);
|
||||||
snd_config_iterator_t j, jnext;
|
snd_config_iterator_t j, jnext;
|
||||||
long cchannel;
|
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 ||
|
if (err < 0 ||
|
||||||
cchannel < 0 || (unsigned int) cchannel > tt_csize) {
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
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);
|
snd_config_t *jnode = snd_config_iterator_entry(j);
|
||||||
double value;
|
double value;
|
||||||
long schannel;
|
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);
|
err = safe_strtol(id, &schannel);
|
||||||
if (err < 0 ||
|
if (err < 0 ||
|
||||||
schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
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;
|
unsigned int cused, sused;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
if (strcmp(id, "slave") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "slave") == 0) {
|
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) {
|
snd_config_for_each(i, next, bindings) {
|
||||||
long cchannel = -1;
|
long cchannel = -1;
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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);
|
err = safe_strtol(id, &cchannel);
|
||||||
if (err < 0 || cchannel < 0) {
|
if (err < 0 || cchannel < 0) {
|
||||||
SNDERR("Invalid client channel in binding: %s", id);
|
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_for_each(i, next, bindings) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id = snd_config_get_id(n);
|
const char *id;
|
||||||
long cchannel;
|
long cchannel;
|
||||||
long schannel = -1;
|
long schannel = -1;
|
||||||
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
|
continue;
|
||||||
cchannel = atoi(id);
|
cchannel = atoi(id);
|
||||||
err = snd_config_get_integer(n, &schannel);
|
err = snd_config_get_integer(n, &schannel);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
|
||||||
|
|
@ -740,7 +740,9 @@ int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
|
||||||
struct hostent *h;
|
struct hostent *h;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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))
|
if (snd_pcm_conf_generic_id(id))
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "server") == 0) {
|
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_for_each(i, next, sconfig) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "host") == 0) {
|
if (strcmp(id, "host") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
snd_rawmidi_params_t params;
|
snd_rawmidi_params_t params;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_rawmidi_t **, snd_rawmidi_t **,
|
int (*open_func)(snd_rawmidi_t **, snd_rawmidi_t **,
|
||||||
const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
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");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(rawmidi_root, "rawmidi_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,9 @@ int _snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
|
||||||
int err;
|
int err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
|
||||||
int err;
|
int err;
|
||||||
snd_config_t *conf, *type_conf = NULL;
|
snd_config_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_seq_t **, const char *,
|
int (*open_func)(snd_seq_t **, const char *,
|
||||||
snd_config_t *, snd_config_t *,
|
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");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(seq_root, "seq_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -515,7 +515,9 @@ int _snd_seq_hw_open(snd_seq_t **handlep, char *name,
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ static int snd_timer_open_conf(snd_timer_t **timer,
|
||||||
int err;
|
int err;
|
||||||
snd_config_t *conf, *type_conf = NULL;
|
snd_config_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_timer_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
int (*open_func)(snd_timer_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||||
#ifndef PIC
|
#ifndef PIC
|
||||||
|
|
@ -63,9 +64,14 @@ static int snd_timer_open_conf(snd_timer_t **timer,
|
||||||
SNDERR("type is not defined");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(timer_root, "timer_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,9 @@ int _snd_timer_hw_open(snd_timer_t **timer, char *name,
|
||||||
int err;
|
int err;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
|
||||||
int err;
|
int err;
|
||||||
snd_config_t *conf, *type_conf = NULL;
|
snd_config_t *conf, *type_conf = NULL;
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
|
const char *id;
|
||||||
const char *lib = NULL, *open_name = NULL;
|
const char *lib = NULL, *open_name = NULL;
|
||||||
int (*open_func)(snd_timer_query_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
int (*open_func)(snd_timer_query_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||||
#ifndef PIC
|
#ifndef PIC
|
||||||
|
|
@ -62,9 +63,14 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
|
||||||
SNDERR("type is not defined");
|
SNDERR("type is not defined");
|
||||||
return err;
|
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);
|
err = snd_config_get_string(conf, &str);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
SNDERR("Invalid type for %s", id);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_config_search_definition(timer_root, "timer_query_type", str, &type_conf);
|
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_for_each(i, next, type_conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "lib") == 0) {
|
if (strcmp(id, "lib") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -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_iterator_t i, next;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
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)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "type") == 0)
|
if (strcmp(id, "type") == 0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue