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_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "host") == 0) {
|
||||
|
|
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
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
INPUT = index.doxygen conf.doxygen confarg.doxygen \
|
||||
conffunc.doxygen pcm.doxygen \
|
||||
INPUT = index.doxygen \
|
||||
../include/asoundlib.h \
|
||||
../include/version.h \
|
||||
../include/global.h \
|
||||
|
|
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,
|
||||
/** Character string */
|
||||
SND_CONFIG_TYPE_STRING,
|
||||
/** Pointer - runtime only - cannot be saved */
|
||||
SND_CONFIG_TYPE_POINTER,
|
||||
/** Compound */
|
||||
SND_CONFIG_TYPE_COMPOUND,
|
||||
SND_CONFIG_TYPE_COMPOUND = 1024,
|
||||
} snd_config_type_t;
|
||||
|
||||
/** Config node handle */
|
||||
|
@ -60,10 +62,13 @@ typedef struct _snd_config snd_config_t;
|
|||
/** Config compound iterator */
|
||||
typedef struct _snd_config_iterator *snd_config_iterator_t;
|
||||
|
||||
extern snd_config_t *snd_config;
|
||||
|
||||
int snd_config_top(snd_config_t **config);
|
||||
|
||||
int snd_config_load(snd_config_t *config, snd_input_t *in);
|
||||
int snd_config_save(snd_config_t *config, snd_output_t *out);
|
||||
int snd_config_update(void);
|
||||
|
||||
int snd_config_search(snd_config_t *config, const char *key,
|
||||
snd_config_t **result);
|
||||
|
@ -74,10 +79,10 @@ int snd_config_search_definition(snd_config_t *config,
|
|||
snd_config_t **result);
|
||||
|
||||
int snd_config_expand(snd_config_t *config, snd_config_t *root,
|
||||
const char *args, void *private_data,
|
||||
const char *args, snd_config_t *private_data,
|
||||
snd_config_t **result);
|
||||
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
||||
void *private_data, snd_config_t **result);
|
||||
snd_config_t *private_data, snd_config_t **result);
|
||||
|
||||
int snd_config_add(snd_config_t *config, snd_config_t *leaf);
|
||||
int snd_config_delete(snd_config_t *config);
|
||||
|
@ -88,17 +93,24 @@ int snd_config_make(snd_config_t **config, const char *key,
|
|||
int snd_config_make_integer(snd_config_t **config, const char *key);
|
||||
int snd_config_make_real(snd_config_t **config, const char *key);
|
||||
int snd_config_make_string(snd_config_t **config, const char *key);
|
||||
int snd_config_make_pointer(snd_config_t **config, const char *key);
|
||||
int snd_config_make_compound(snd_config_t **config, const char *key, int join);
|
||||
|
||||
snd_config_type_t snd_config_get_type(snd_config_t *config);
|
||||
|
||||
int snd_config_set_id(snd_config_t *config, const char *id);
|
||||
int snd_config_set_integer(snd_config_t *config, long value);
|
||||
int snd_config_set_real(snd_config_t *config, double value);
|
||||
int snd_config_set_string(snd_config_t *config, const char *value);
|
||||
int snd_config_set_ascii(snd_config_t *config, const char *ascii);
|
||||
int snd_config_set_pointer(snd_config_t *config, const void *ptr);
|
||||
int snd_config_get_id(snd_config_t *config, const char **value);
|
||||
int snd_config_get_integer(snd_config_t *config, long *value);
|
||||
int snd_config_get_real(snd_config_t *config, double *value);
|
||||
int snd_config_get_string(snd_config_t *config, const char **value);
|
||||
int snd_config_get_ascii(snd_config_t *config, char **value);
|
||||
int snd_config_get_pointer(snd_config_t *config, const void **value);
|
||||
int snd_config_test_id(snd_config_t *config, const char *id);
|
||||
|
||||
snd_config_iterator_t snd_config_iterator_first(snd_config_t *node);
|
||||
snd_config_iterator_t snd_config_iterator_next(snd_config_iterator_t iterator);
|
||||
|
@ -115,12 +127,6 @@ snd_config_t *snd_config_iterator_entry(snd_config_iterator_t iterator);
|
|||
#define snd_config_for_each(pos, next, node) \
|
||||
for (pos = snd_config_iterator_first(node), next = snd_config_iterator_next(pos); pos != snd_config_iterator_end(node); pos = next, next = snd_config_iterator_next(pos))
|
||||
|
||||
snd_config_type_t snd_config_get_type(snd_config_t *config);
|
||||
const char *snd_config_get_id(snd_config_t *config);
|
||||
|
||||
extern snd_config_t *snd_config;
|
||||
int snd_config_update(void);
|
||||
|
||||
/* Misc functions */
|
||||
|
||||
int snd_config_get_bool_ascii(const char *ascii);
|
||||
|
|
|
@ -192,12 +192,6 @@ int snd_card_get_index(const char *name);
|
|||
int snd_card_get_name(int card, char **name);
|
||||
int snd_card_get_longname(int card, char **name);
|
||||
|
||||
int snd_sctl_build(snd_sctl_t **ctl, snd_ctl_t *handle, snd_config_t *config,
|
||||
void *private_data, int mode);
|
||||
int snd_sctl_free(snd_sctl_t *handle);
|
||||
int snd_sctl_install(snd_sctl_t *handle);
|
||||
int snd_sctl_remove(snd_sctl_t *handle);
|
||||
|
||||
int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode);
|
||||
int snd_ctl_close(snd_ctl_t *ctl);
|
||||
int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock);
|
||||
|
@ -482,9 +476,23 @@ void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val);
|
|||
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* \defgroup SControl Setup Control Interface
|
||||
* \ingroup Control
|
||||
* The setup control interface - set or modify control elements from a configuration file.
|
||||
* \{
|
||||
*/
|
||||
|
||||
int snd_sctl_build(snd_sctl_t **ctl, snd_ctl_t *handle, snd_config_t *config,
|
||||
snd_config_t *private_data, int mode);
|
||||
int snd_sctl_free(snd_sctl_t *handle);
|
||||
int snd_sctl_install(snd_sctl_t *handle);
|
||||
int snd_sctl_remove(snd_sctl_t *handle);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ALSA_CONTROL_H */
|
||||
|
||||
|
|
|
@ -29,13 +29,14 @@
|
|||
#define __ALSA_GLOBAL_H_
|
||||
|
||||
/**
|
||||
* \defgroup Global Global defines
|
||||
* Global defines
|
||||
* \defgroup Global Global defines and functions
|
||||
* Global defines and functions.
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef ATTRIBUTE_UNUSED
|
||||
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) /**< don't print warning when attribute is not used */
|
||||
/** do not print warning (gcc) when function parameter is not used */
|
||||
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
#ifdef PIC /* dynamic build */
|
||||
|
@ -88,9 +89,9 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd,
|
|||
snd_async_callback_t callback, void *private_data);
|
||||
int snd_async_del_handler(snd_async_handler_t *handler);
|
||||
int snd_async_handler_get_fd(snd_async_handler_t *handler);
|
||||
int snd_async_handler_get_signo(snd_async_handler_t *handler);
|
||||
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler);
|
||||
|
||||
/** \} */
|
||||
|
||||
#endif /* __ALSA_GLOBAL_H */
|
||||
|
||||
|
|
|
@ -113,8 +113,6 @@ typedef struct sndrv_seq_event snd_seq_event_t;
|
|||
#define SND_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
extern int snd_async_signo;
|
||||
|
||||
struct _snd_async_handler {
|
||||
enum {
|
||||
SND_ASYNC_HANDLER_GENERIC,
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* \author Takashi Iwai <tiwai@suse.de>
|
||||
* \date 1998-2001
|
||||
*
|
||||
* Application interface library for the ALSA driver
|
||||
* Application interface library for the ALSA driver.
|
||||
* See the \ref pcm page for more details.
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
|
@ -34,7 +35,7 @@ extern "C" {
|
|||
|
||||
/**
|
||||
* \defgroup PCM PCM Interface
|
||||
* The PCM Interface.
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -59,6 +60,7 @@ typedef struct _snd_pcm_subformat_mask snd_pcm_subformat_mask_t;
|
|||
/** PCM class */
|
||||
typedef enum _snd_pcm_class {
|
||||
/** standard device */
|
||||
|
||||
SND_PCM_CLASS_GENERIC = 0,
|
||||
/** multichannel device */
|
||||
SND_PCM_CLASS_MULTI,
|
||||
|
@ -388,8 +390,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Info Stream Information
|
||||
* PCM Stream Information
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -422,8 +424,8 @@ void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_HW_Params Hardware Parameters
|
||||
* PCM Hardware Parameters
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -596,8 +598,8 @@ unsigned int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_par
|
|||
|
||||
/**
|
||||
* \defgroup PCM_SW_Params Software Parameters
|
||||
* PCM Software Parameters
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -637,8 +639,8 @@ snd_pcm_uframes_t snd_pcm_sw_params_get_silence_size(const snd_pcm_sw_params_t *
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Access Access Mask Functions
|
||||
* PCM Access Mask Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -661,8 +663,8 @@ void snd_pcm_access_mask_reset(snd_pcm_access_mask_t *mask, snd_pcm_access_t val
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Format Format Mask Functions
|
||||
* PCM Format Mask Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -685,8 +687,8 @@ void snd_pcm_format_mask_reset(snd_pcm_format_mask_t *mask, snd_pcm_format_t val
|
|||
|
||||
/**
|
||||
* \defgroup PCM_SubFormat Subformat Mask Functions
|
||||
* PCM Subformat Mask Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -709,8 +711,8 @@ void snd_pcm_subformat_mask_reset(snd_pcm_subformat_mask_t *mask, snd_pcm_subfor
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Status Status Functions
|
||||
* PCM Status Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -735,8 +737,8 @@ snd_pcm_uframes_t snd_pcm_status_get_overrange(const snd_pcm_status_t *obj);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Description Description Functions
|
||||
* PCM Description Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -754,8 +756,8 @@ const char *snd_pcm_state_name(const snd_pcm_state_t state);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Dump Debug Functions
|
||||
* PCM Debug Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -771,8 +773,8 @@ int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Direct Direct Access (MMAP) Functions
|
||||
* PCM Direct Access (MMAP) Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -791,8 +793,8 @@ snd_pcm_sframes_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframe
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Helpers Helper Functions
|
||||
* PCM Helper Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -832,8 +834,8 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_channels, snd_pcm_ufram
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Hook Hook Extension
|
||||
* PCM Hook Extension
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -861,8 +863,8 @@ int snd_pcm_hook_remove(snd_pcm_hook_t *hook);
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Scope Scope Plugin Extension
|
||||
* PCM Scope Plugin Extension
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -920,8 +922,8 @@ int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope,
|
|||
|
||||
/**
|
||||
* \defgroup PCM_Deprecated Deprecated Functions
|
||||
* PCM Deprecated Functions
|
||||
* \ingroup PCM
|
||||
* See the \ref pcm page for more details.
|
||||
* \{
|
||||
*/
|
||||
|
||||
|
@ -940,4 +942,3 @@ snd_pcm_xrun_t snd_pcm_sw_params_get_xrun_mode(const snd_pcm_sw_params_t *params
|
|||
#endif
|
||||
|
||||
#endif /* __ALSA_PCM_H */
|
||||
|
||||
|
|
25
src/async.c
25
src/async.c
|
@ -30,7 +30,8 @@
|
|||
|
||||
#ifdef SND_ASYNC_RT_SIGNAL
|
||||
/** async signal number */
|
||||
int snd_async_signo;
|
||||
static int snd_async_signo;
|
||||
|
||||
void snd_async_init(void) __attribute__ ((constructor));
|
||||
|
||||
void snd_async_init(void)
|
||||
|
@ -43,7 +44,7 @@ void snd_async_init(void)
|
|||
}
|
||||
#else
|
||||
/** async signal number */
|
||||
int snd_async_signo = SIGIO;
|
||||
static int snd_async_signo = SIGIO;
|
||||
#endif
|
||||
|
||||
static LIST_HEAD(snd_async_handlers);
|
||||
|
@ -68,12 +69,18 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo
|
|||
* \param callback - Async callback
|
||||
* \param private_data - Private data for async callback
|
||||
* \result zero if success, otherwise a negative error code
|
||||
*
|
||||
* The function create the async handler. The ALSA extension
|
||||
* for the standard SIGIO signal contains the multiplexer
|
||||
* for multiple asynchronous notifiers using one sigaction
|
||||
* callback.
|
||||
*/
|
||||
int snd_async_add_handler(snd_async_handler_t **handler, int fd,
|
||||
snd_async_callback_t callback, void *private_data)
|
||||
{
|
||||
snd_async_handler_t *h;
|
||||
int was_empty;
|
||||
assert(handler);
|
||||
h = malloc(sizeof(*h));
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
@ -107,6 +114,7 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd,
|
|||
int snd_async_del_handler(snd_async_handler_t *handler)
|
||||
{
|
||||
int err = 0;
|
||||
assert(handler);
|
||||
list_del(&handler->glist);
|
||||
if (list_empty(&snd_async_handlers)) {
|
||||
struct sigaction act;
|
||||
|
@ -139,6 +147,17 @@ int snd_async_del_handler(snd_async_handler_t *handler)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get signal number assigned to async handler
|
||||
* \param handler Async handler
|
||||
* \result signal number if success, otherwise a negative error code
|
||||
*/
|
||||
int snd_async_handler_get_signo(snd_async_handler_t *handler)
|
||||
{
|
||||
assert(handler);
|
||||
return snd_async_signo;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get file descriptor assigned to async handler
|
||||
* \param handler Async handler
|
||||
|
@ -146,6 +165,7 @@ int snd_async_del_handler(snd_async_handler_t *handler)
|
|||
*/
|
||||
int snd_async_handler_get_fd(snd_async_handler_t *handler)
|
||||
{
|
||||
assert(handler);
|
||||
return handler->fd;
|
||||
}
|
||||
|
||||
|
@ -156,6 +176,7 @@ int snd_async_handler_get_fd(snd_async_handler_t *handler)
|
|||
*/
|
||||
void *snd_async_handler_get_callback_private(snd_async_handler_t *handler)
|
||||
{
|
||||
assert(handler);
|
||||
return handler->private_data;
|
||||
}
|
||||
|
||||
|
|
431
src/conf.c
431
src/conf.c
|
@ -5,7 +5,7 @@
|
|||
* \author Jaroslav Kysela <perex@suse.cz>
|
||||
* \date 2000-2001
|
||||
*
|
||||
* Generic stdio-like input interface
|
||||
* Tree based, full nesting configuration functions.
|
||||
*/
|
||||
/*
|
||||
* Configuration helper functions
|
||||
|
@ -29,6 +29,306 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*! \page conf Configuration files
|
||||
|
||||
<P>Configuration files are using a simple format allowing the modern
|
||||
data description like nesting and array assignments.</P>
|
||||
|
||||
\section conf_whitespace Whitespace
|
||||
|
||||
Whitespace is the collective name given to spaces (blanks), horizontal and
|
||||
vertical tabs, newline characters, and comments. Whitespace can serve to
|
||||
indicate where configuration tokens start and end, but beyond this function,
|
||||
any surplus whitespace is discarded. For example, the two sequences
|
||||
|
||||
\code
|
||||
a 1 b 2
|
||||
\endcode
|
||||
|
||||
and
|
||||
|
||||
\code
|
||||
a 1
|
||||
b 2
|
||||
\endcode
|
||||
|
||||
are lexically equivalent and parse identically to give the four tokens:
|
||||
|
||||
\code
|
||||
a
|
||||
1
|
||||
b
|
||||
2
|
||||
\endcode
|
||||
|
||||
The ASCII characters representing whitespace can occur within literal
|
||||
strings, int which case they are protected from the normal parsing process
|
||||
(tey remain as part of the string). For example:
|
||||
|
||||
\code
|
||||
name "John Smith"
|
||||
\endcode
|
||||
|
||||
parses to two tokens, including the single literal-string token "John
|
||||
Smith".
|
||||
|
||||
\section conf_linesplicing Line splicing with \
|
||||
|
||||
A special case occurs, if the final newline character encountered is
|
||||
preceded by a backslash (\) in the string value definition. The backslash
|
||||
and new line are both discarded, allowing two physical lines of text to be
|
||||
treated as one unit.
|
||||
|
||||
\code
|
||||
"John \
|
||||
Smith"
|
||||
\endcode
|
||||
|
||||
is parsed as "John Smith".
|
||||
|
||||
\section conf_comments Comments
|
||||
|
||||
A single-line comments are defined using character #. The comment can start
|
||||
in any position, and extends until the next new line.
|
||||
|
||||
\code
|
||||
a 1 # this is a comment
|
||||
\endcode
|
||||
|
||||
\section conf_include Include another configuration file
|
||||
|
||||
A new configuration file can be included using <filename> syntax. The global
|
||||
configuration directory can be referenced using <confdir:filename> syntax.
|
||||
|
||||
\code
|
||||
</etc/alsa1.conf>
|
||||
<confdir:pcm/surround.conf>
|
||||
\endcode
|
||||
|
||||
\section conf_punctuators Punctuators
|
||||
|
||||
The configuration punctuators (also known as separators) are:
|
||||
|
||||
\code
|
||||
{} [] , ; = . ' " new-line form-feed carriage-return whitespace
|
||||
\endcode
|
||||
|
||||
\subsection conf_braces Braces
|
||||
|
||||
Open and close braces { } indicate the start and end of a compound
|
||||
statement:
|
||||
|
||||
\code
|
||||
a {
|
||||
b 1
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection conf_brackets Brackets
|
||||
|
||||
Open and close brackets indicate single array definition. The identificators
|
||||
are automatically generated starting with zero.
|
||||
|
||||
\code
|
||||
a [
|
||||
"first"
|
||||
"second"
|
||||
]
|
||||
\endcode
|
||||
|
||||
Above code is equal to
|
||||
|
||||
\code
|
||||
a.0 "first"
|
||||
a.1 "second"
|
||||
\endcode
|
||||
|
||||
\subsection conf_comma_semicolon Comma and semicolon
|
||||
|
||||
The comma (,) or semicolon (;) can separate the value assignments. It is not
|
||||
strictly required to use these separators, because any whitespace supplies
|
||||
them.
|
||||
|
||||
\code
|
||||
a 1;
|
||||
b 1,
|
||||
\endcode
|
||||
|
||||
\subsection conf_equal Equal sign
|
||||
|
||||
The equal sign (=) separates can separate variable declarations from
|
||||
initialization lists:
|
||||
|
||||
\code
|
||||
a=1
|
||||
b=2
|
||||
\endcode
|
||||
|
||||
Using the equal signs is not required, because any whitespace supplies
|
||||
them.
|
||||
|
||||
\section conf_assigns Assigns
|
||||
|
||||
The configuration file defines id (key) and value pairs. The id (key) can be
|
||||
composed from any ASCII digits or chars from a to z or A to Z, including
|
||||
char _. The value can be either a string, integer or real number.
|
||||
|
||||
\subsection conf_single Single assign
|
||||
|
||||
\code
|
||||
a 1 # is equal to
|
||||
a=1 # is equal to
|
||||
a=1; # is equal to
|
||||
a 1,
|
||||
\endcode
|
||||
|
||||
\subsection conf_compound Compound assign (definition using braces)
|
||||
|
||||
\code
|
||||
a {
|
||||
b = 1
|
||||
}
|
||||
a={
|
||||
b 1,
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section conf_compound1 Compound assign (one key definition)
|
||||
|
||||
\code
|
||||
a.b 1
|
||||
a.b=1
|
||||
\endcode
|
||||
|
||||
\subsection conf_array Array assign (definition using brackets)
|
||||
|
||||
\code
|
||||
a [
|
||||
"first"
|
||||
"second"
|
||||
]
|
||||
\endcode
|
||||
|
||||
\subsection conf_array1 Array assign (one key definition)
|
||||
|
||||
\code
|
||||
a.0 "first"
|
||||
a.1 "second"
|
||||
\endcode
|
||||
|
||||
\section conf_summary Summary
|
||||
|
||||
\code
|
||||
# Configuration file syntax
|
||||
|
||||
# Include a new configuration file
|
||||
<filename>
|
||||
|
||||
# Simple assign
|
||||
name [=] value [,|;]
|
||||
|
||||
# Compound assign (first style)
|
||||
name [=] {
|
||||
name1 [=] value [,|;]
|
||||
...
|
||||
}
|
||||
|
||||
# Compound assign (second style)
|
||||
name.name1 [=] value [,|;]
|
||||
|
||||
# Array assign (first style)
|
||||
name [
|
||||
value0 [,|;]
|
||||
value1 [,|;]
|
||||
...
|
||||
]
|
||||
|
||||
# Array assign (second style)
|
||||
name.0 [=] value0 [,|;]
|
||||
name.1 [=] value1 [,|;]
|
||||
\endcode
|
||||
|
||||
*/
|
||||
|
||||
/*! \page confarg Configuration - runtime arguments
|
||||
|
||||
<P>The ALSA library can accept runtime arguments for some configuration
|
||||
blocks. This extension is on top of the basic syntax of the configuration
|
||||
files.<P>
|
||||
|
||||
\section confarg_define Defining arguments
|
||||
|
||||
Arguments are specified by id (key) @args and array values containing
|
||||
the string names of arguments:
|
||||
|
||||
\code
|
||||
@args [ CARD ] # or
|
||||
@args.0 CARD
|
||||
\endcode
|
||||
|
||||
\section confarg_type Defining argument type and default value
|
||||
|
||||
Arguments type is specified by id (key) @args and argument name. The type
|
||||
and default value is specified in the compound:
|
||||
|
||||
\code
|
||||
@args.CARD {
|
||||
type string
|
||||
default "abcd"
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section confarg_refer Refering argument
|
||||
|
||||
Arguments are refered by dollar-sign ($) and name of argument:
|
||||
|
||||
\code
|
||||
card $CARD
|
||||
\endcode
|
||||
|
||||
\section confarg_example Example
|
||||
|
||||
\code
|
||||
pcm.demo {
|
||||
@args [ CARD DEVICE ]
|
||||
@args.CARD {
|
||||
type string
|
||||
default "supersonic"
|
||||
}
|
||||
@args.DEVICE {
|
||||
type integer
|
||||
default 0
|
||||
}
|
||||
type hw
|
||||
card $CARD
|
||||
device $DEVICE
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
|
||||
/*! \page conffunc Configuration - runtime functions
|
||||
|
||||
<P>The ALSA library accepts the runtime modification of configuration.
|
||||
The several build-in functions are available.</P>
|
||||
|
||||
<P>The function is refered using id @func and function name. All other
|
||||
values in the current compound are used as configuration for the function.
|
||||
If compound func.<function_name> is defined in the root leafs, then library
|
||||
and function from this compound configuration is used, otherwise the prefix
|
||||
'snd_func_' is added to string and the code from the ALSA library is used.
|
||||
The definition of function looks like:</P>
|
||||
|
||||
\code
|
||||
func.remove_first_char {
|
||||
lib "/usr/lib/libasoundextend.so"
|
||||
func "extend_remove_first_char"
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <wordexp.h>
|
||||
#include <dlfcn.h>
|
||||
|
@ -44,6 +344,7 @@ struct _snd_config {
|
|||
long integer;
|
||||
char *string;
|
||||
double real;
|
||||
const void *ptr;
|
||||
struct {
|
||||
struct list_head fields;
|
||||
int join;
|
||||
|
@ -895,6 +1196,9 @@ static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
|
|||
case SND_CONFIG_TYPE_STRING:
|
||||
string_print(n->u.string, 0, out);
|
||||
break;
|
||||
case SND_CONFIG_TYPE_POINTER:
|
||||
SNDERR("cannot save runtime pointer type");
|
||||
return -EINVAL;
|
||||
case SND_CONFIG_TYPE_COMPOUND:
|
||||
snd_output_putc(out, '{');
|
||||
snd_output_putc(out, '\n');
|
||||
|
@ -1024,11 +1328,14 @@ snd_config_type_t snd_config_get_type(snd_config_t *config)
|
|||
/**
|
||||
* \brief Return id of a config node
|
||||
* \param config Config node handle
|
||||
* \return node id
|
||||
* \param value The result id
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
const char *snd_config_get_id(snd_config_t *config)
|
||||
int snd_config_get_id(snd_config_t *config, const char **id)
|
||||
{
|
||||
return config->id;
|
||||
assert(config && id);
|
||||
*id = config->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1245,6 +1552,17 @@ int snd_config_make_string(snd_config_t **config, const char *id)
|
|||
return snd_config_make(config, id, SND_CONFIG_TYPE_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Build a pointer config node
|
||||
* \param config Returned config node handle pointer
|
||||
* \param id Node id
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_config_make_pointer(snd_config_t **config, const char *id)
|
||||
{
|
||||
return snd_config_make(config, id, SND_CONFIG_TYPE_POINTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Build an empty compound config node
|
||||
* \param config Returned config node handle pointer
|
||||
|
@ -1312,6 +1630,21 @@ int snd_config_set_string(snd_config_t *config, const char *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Change the value of a pointer config node
|
||||
* \param config Config node handle
|
||||
* \param ptr Value
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_config_set_pointer(snd_config_t *config, const void *value)
|
||||
{
|
||||
assert(config);
|
||||
if (config->type != SND_CONFIG_TYPE_POINTER)
|
||||
return -EINVAL;
|
||||
config->u.ptr = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Change the value of a config node
|
||||
* \param config Config node handle
|
||||
|
@ -1399,6 +1732,21 @@ int snd_config_get_string(snd_config_t *config, const char **ptr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the value of a pointer config node
|
||||
* \param config Config node handle
|
||||
* \param ptr Returned value pointer
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_config_get_pointer(snd_config_t *config, const void **ptr)
|
||||
{
|
||||
assert(config && ptr);
|
||||
if (config->type != SND_CONFIG_TYPE_POINTER)
|
||||
return -EINVAL;
|
||||
*ptr = config->u.ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the value in ASCII form
|
||||
* \param config Config node handle
|
||||
|
@ -1453,6 +1801,18 @@ int snd_config_get_ascii(snd_config_t *config, char **ascii)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compare the config node id and given ASCII id
|
||||
* \param config Config node handle
|
||||
* \param id ASCII id
|
||||
* \return the same value as result of the strcmp function
|
||||
*/
|
||||
int snd_config_test_id(snd_config_t *config, const char *id)
|
||||
{
|
||||
assert(config && id);
|
||||
return strcmp(config->id, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dump a config tree contents
|
||||
* \param config Config node handle
|
||||
|
@ -1666,7 +2026,7 @@ int snd_config_search_alias(snd_config_t *config,
|
|||
*/
|
||||
int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t **result)
|
||||
{
|
||||
static int snd_config_hooks(snd_config_t *config, void *private_data);
|
||||
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
|
||||
SND_CONFIG_SEARCH(config, key, result, \
|
||||
err = snd_config_hooks(config, NULL); \
|
||||
if (err < 0) \
|
||||
|
@ -1683,7 +2043,7 @@ int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t
|
|||
*/
|
||||
int snd_config_searcha_hooks(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
|
||||
{
|
||||
static int snd_config_hooks(snd_config_t *config, void *private_data);
|
||||
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
|
||||
SND_CONFIG_SEARCHA(root, config, key, result,
|
||||
snd_config_searcha_hooks,
|
||||
err = snd_config_hooks(config, NULL); \
|
||||
|
@ -1745,14 +2105,14 @@ static struct finfo {
|
|||
|
||||
static unsigned int files_info_count = 0;
|
||||
|
||||
static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void *private_data)
|
||||
static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_config_t *private_data)
|
||||
{
|
||||
void *h = NULL;
|
||||
snd_config_t *c, *func_conf = NULL;
|
||||
char *buf = NULL;
|
||||
const char *lib = NULL, *func_name = NULL;
|
||||
const char *str;
|
||||
int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data) = NULL;
|
||||
int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data) = NULL;
|
||||
int err;
|
||||
|
||||
err = snd_config_search(config, "func", &c);
|
||||
|
@ -1774,7 +2134,7 @@ static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void
|
|||
}
|
||||
snd_config_for_each(i, next, func_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id = n->id;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
@ -1833,7 +2193,7 @@ static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, void
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_config_hooks(snd_config_t *config, void *private_data)
|
||||
static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -1846,7 +2206,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
|
|||
hit = 0;
|
||||
snd_config_for_each(i, next, n) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id = n->id;
|
||||
long i;
|
||||
err = safe_strtol(id, &i);
|
||||
if (err < 0) {
|
||||
|
@ -1877,7 +2237,7 @@ static int snd_config_hooks(snd_config_t *config, void *private_data)
|
|||
* \param private_data Private data
|
||||
* \return zero if success, otherwise a negative error code
|
||||
*/
|
||||
int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data)
|
||||
int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n, *res = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -1913,7 +2273,7 @@ int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t
|
|||
snd_config_t *c = snd_config_iterator_entry(i);
|
||||
const char *str;
|
||||
if ((err = snd_config_get_string(c, &str)) < 0) {
|
||||
SNDERR("Field %s is not a string", snd_config_get_id(c));
|
||||
SNDERR("Field %s is not a string", c->id);
|
||||
goto _err;
|
||||
}
|
||||
fi_count++;
|
||||
|
@ -1927,7 +2287,7 @@ int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t
|
|||
hit = 0;
|
||||
snd_config_for_each(i, next, n) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id = n->id;
|
||||
long i;
|
||||
err = safe_strtol(id, &i);
|
||||
if (err < 0) {
|
||||
|
@ -2014,7 +2374,7 @@ int snd_determine_driver(int card, char **driver);
|
|||
* \param private_data Private data
|
||||
* \return zero if success, otherwise a negative error code
|
||||
*/
|
||||
int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, void *private_data ATTRIBUTE_UNUSED)
|
||||
int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int card = -1, err;
|
||||
|
||||
|
@ -2023,12 +2383,16 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
|
|||
if (err < 0)
|
||||
return err;
|
||||
if (card >= 0) {
|
||||
snd_config_t *n;
|
||||
snd_config_t *n, *private_data = NULL;
|
||||
const char *driver;
|
||||
char *fdriver = NULL;
|
||||
err = snd_determine_driver(card, &fdriver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_make_string(&private_data, "string");
|
||||
if (err < 0)
|
||||
goto __err;
|
||||
snd_config_set_string(private_data, fdriver);
|
||||
if (snd_config_search(root, fdriver, &n) >= 0) {
|
||||
if (snd_config_get_string(n, &driver) < 0)
|
||||
continue;
|
||||
|
@ -2043,7 +2407,10 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
|
|||
} else {
|
||||
driver = fdriver;
|
||||
}
|
||||
err = snd_config_hook_load(root, config, &n, (void *)driver);
|
||||
err = snd_config_hook_load(root, config, &n, private_data);
|
||||
__err:
|
||||
if (private_data)
|
||||
snd_config_delete(private_data);
|
||||
if (fdriver)
|
||||
free(fdriver);
|
||||
if (err < 0)
|
||||
|
@ -2255,14 +2622,14 @@ typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
|
|||
snd_config_t *root,
|
||||
snd_config_t **dst,
|
||||
snd_config_walk_pass_t pass,
|
||||
void *private_data);
|
||||
snd_config_t *private_data);
|
||||
#endif
|
||||
|
||||
static int snd_config_walk(snd_config_t *src,
|
||||
snd_config_t *root,
|
||||
snd_config_t **dst,
|
||||
snd_config_walk_callback_t callback,
|
||||
void *private_data)
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
int err;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -2303,10 +2670,10 @@ static int _snd_config_copy(snd_config_t *src,
|
|||
snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t **dst,
|
||||
snd_config_walk_pass_t pass,
|
||||
void *private_data ATTRIBUTE_UNUSED)
|
||||
snd_config_t *private_data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int err;
|
||||
const char *id = snd_config_get_id(src);
|
||||
const char *id = src->id;
|
||||
snd_config_type_t type = snd_config_get_type(src);
|
||||
switch (pass) {
|
||||
case SND_CONFIG_WALK_PASS_PRE:
|
||||
|
@ -2370,10 +2737,10 @@ static int _snd_config_expand(snd_config_t *src,
|
|||
snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t **dst,
|
||||
snd_config_walk_pass_t pass,
|
||||
void *private_data)
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
int err;
|
||||
const char *id = snd_config_get_id(src);
|
||||
const char *id = src->id;
|
||||
snd_config_type_t type = snd_config_get_type(src);
|
||||
switch (pass) {
|
||||
case SND_CONFIG_WALK_PASS_PRE:
|
||||
|
@ -2453,7 +2820,7 @@ static int _snd_config_evaluate(snd_config_t *src,
|
|||
snd_config_t *root,
|
||||
snd_config_t **dst ATTRIBUTE_UNUSED,
|
||||
snd_config_walk_pass_t pass,
|
||||
void *private_data)
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
int err;
|
||||
if (pass == SND_CONFIG_WALK_PASS_PRE) {
|
||||
|
@ -2461,7 +2828,7 @@ static int _snd_config_evaluate(snd_config_t *src,
|
|||
const char *lib = NULL, *func_name = NULL;
|
||||
const char *str;
|
||||
int (*func)(snd_config_t **dst, snd_config_t *root,
|
||||
snd_config_t *src, void *private_data) = NULL;
|
||||
snd_config_t *src, snd_config_t *private_data) = NULL;
|
||||
void *h = NULL;
|
||||
snd_config_t *c, *func_conf = NULL;
|
||||
err = snd_config_search(src, "@func", &c);
|
||||
|
@ -2481,7 +2848,7 @@ static int _snd_config_evaluate(snd_config_t *src,
|
|||
}
|
||||
snd_config_for_each(i, next, func_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id = n->id;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
@ -2552,7 +2919,7 @@ static int _snd_config_evaluate(snd_config_t *src,
|
|||
* \return zero if success, otherwise a negative error code
|
||||
*/
|
||||
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
|
||||
void *private_data, snd_config_t **result)
|
||||
snd_config_t *private_data, snd_config_t **result)
|
||||
{
|
||||
/* FIXME: Only in place evaluation is currently implemented */
|
||||
assert(result == NULL);
|
||||
|
@ -2569,7 +2936,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
|||
continue;
|
||||
snd_config_for_each(f, fnext, def) {
|
||||
snd_config_t *fld = snd_config_iterator_entry(f);
|
||||
const char *id = snd_config_get_id(fld);
|
||||
const char *id = fld->id;
|
||||
if (strcmp(id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "default") == 0) {
|
||||
|
@ -2578,7 +2945,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs)
|
|||
err = snd_config_copy(&deflt, fld);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_set_id(deflt, snd_config_get_id(def));
|
||||
err = snd_config_set_id(deflt, def->id);
|
||||
if (err < 0) {
|
||||
snd_config_delete(deflt);
|
||||
return err;
|
||||
|
@ -2818,7 +3185,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
|
|||
snd_config_for_each(i, next, subs) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
snd_config_t *d;
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id = n->id;
|
||||
err = snd_config_search(defs, id, &d);
|
||||
if (err < 0) {
|
||||
SNDERR("Unknown parameter %s", id);
|
||||
|
@ -2856,7 +3223,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
|
|||
err = -EINVAL;
|
||||
goto _err;
|
||||
}
|
||||
var = snd_config_get_id(def);
|
||||
var = def->id;
|
||||
err = snd_config_search(subs, var, &sub);
|
||||
if (err >= 0)
|
||||
snd_config_delete(sub);
|
||||
|
@ -2936,7 +3303,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
|
|||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,
|
||||
void *private_data, snd_config_t **result)
|
||||
snd_config_t *private_data, snd_config_t **result)
|
||||
{
|
||||
int err;
|
||||
snd_config_t *defs, *subs = NULL, *res;
|
||||
|
|
|
@ -38,7 +38,7 @@ pcm.!center_lfe {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.center_lfe." $DEV ":CARD=" $CARD
|
||||
|
|
|
@ -38,7 +38,7 @@ pcm.!front {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.front." $DEV ":CARD=" $CARD
|
||||
|
|
|
@ -58,7 +58,7 @@ pcm.!iec958 {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.iec958." $DEV ":"
|
||||
|
|
|
@ -38,7 +38,7 @@ pcm.!rear {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.rear." $DEV ":CARD=" $CARD
|
||||
|
|
|
@ -43,7 +43,7 @@ pcm.!surround40 {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.surround40." $DEV ":CARD=" $CARD
|
||||
|
|
|
@ -45,7 +45,7 @@ pcm.!surround51 {
|
|||
strings [
|
||||
"cards."
|
||||
{
|
||||
@func card_strtype
|
||||
@func card_driver
|
||||
card $CARD
|
||||
}
|
||||
".pcm.surround51." $DEV ":CARD=" $CARD
|
||||
|
|
446
src/confmisc.c
446
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
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>,
|
||||
|
@ -20,6 +28,40 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*! \page conffunc
|
||||
|
||||
\section Function reference
|
||||
|
||||
<UL>
|
||||
<LI>The getenv function - snd_func_getenv() - allows to obtain
|
||||
an environment value. The result is string.
|
||||
<LI>The igetenv function - snd_func_igetenv() - allows to obtain
|
||||
an environment value. The result is integer.
|
||||
<LI>The concat function - snd_func_concat() - merges all specified
|
||||
strings. The result is string.
|
||||
<LI>The datadir function - snd_func_datadir() - returns the
|
||||
data directory. The result is string.
|
||||
<LI>The refer function - snd_func_refer() - copies the refered
|
||||
configuration. The result is same as the refered node.
|
||||
<LI>The card_driver function - snd_func_card_driver() - returns
|
||||
the driver identification. The result is string.
|
||||
<LI>The card_id function - snd_func_card_id() - returns
|
||||
the card identification. The result is string.
|
||||
<LI>The pcm_id function - snd_func_pcm_id() - returns
|
||||
the pcm identification. The result is string.
|
||||
<LI>The private_string - snd_func_private_string() - returns
|
||||
string using private_data node.
|
||||
<LI>The private_card_driver - snd_func_private_card_driver() -
|
||||
returns the driver identification using private_data node.
|
||||
The result is string.
|
||||
<LI>The private_pcm_subdevice - snd_func_private_pcm_subdevice() -
|
||||
returns the PCM subdevice number using the private_data node.
|
||||
The result is string.
|
||||
</UL>
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -62,21 +104,24 @@ int snd_config_get_bool_ascii(const char *ascii)
|
|||
int snd_config_get_bool(snd_config_t *conf)
|
||||
{
|
||||
long v;
|
||||
const char *str;
|
||||
const char *str, *id;
|
||||
int err;
|
||||
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_get_integer(conf, &v);
|
||||
if (err >= 0) {
|
||||
if (v < 0 || v > 1) {
|
||||
_invalid_value:
|
||||
SNDERR("Invalid value for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid value for %s", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_bool_ascii(str);
|
||||
|
@ -116,20 +161,24 @@ int snd_config_get_ctl_iface_ascii(const char *ascii)
|
|||
int snd_config_get_ctl_iface(snd_config_t *conf)
|
||||
{
|
||||
long v;
|
||||
const char *str;
|
||||
const char *str, *id;
|
||||
int err;
|
||||
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_get_integer(conf, &v);
|
||||
if (err >= 0) {
|
||||
if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) {
|
||||
_invalid_value:
|
||||
SNDERR("Invalid value for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid value for %s", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_ctl_iface_ascii(str);
|
||||
|
@ -142,7 +191,25 @@ int snd_config_get_ctl_iface(snd_config_t *conf)
|
|||
* Helper functions for the configuration file
|
||||
*/
|
||||
|
||||
int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get environment value
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node, with vars and default definition
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func getenv
|
||||
vars [ MY_CARD CARD C ]
|
||||
default 0
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n, *d;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -178,9 +245,10 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
hit = 0;
|
||||
snd_config_for_each(i, next, n) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *ptr, *env;
|
||||
const char *id, *ptr, *env;
|
||||
long i;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
|
||||
SNDERR("field %s is not a string", id);
|
||||
err = -EINVAL;
|
||||
|
@ -209,9 +277,13 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
__ok:
|
||||
err = res == NULL ? -ENOMEM : 0;
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
const char *id;
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
snd_config_set_string(*dst, res);
|
||||
}
|
||||
free(res);
|
||||
}
|
||||
__error:
|
||||
|
@ -219,38 +291,80 @@ int snd_func_getenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
free(def);
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_getenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_igetenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get integer environment value
|
||||
* \param dst The destination node (result type is integer)
|
||||
* \param root The root source node
|
||||
* \param src The source node, with vars and default definition
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func getenv
|
||||
vars [ MY_DEVICE DEVICE D ]
|
||||
default 0
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_igetenv(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *d;
|
||||
const char *str;
|
||||
const char *str, *id;
|
||||
int err;
|
||||
long v;
|
||||
|
||||
err = snd_func_getenv(&d, root, src, private_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_get_string(d, &str);
|
||||
if (err < 0)
|
||||
goto _end;
|
||||
return err;
|
||||
err = safe_strtol(str, &v);
|
||||
if (err < 0)
|
||||
goto _end;
|
||||
err = snd_config_make_integer(dst, snd_config_get_id(src));
|
||||
return err;;
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err < 0)
|
||||
goto _end;
|
||||
snd_config_set_integer(*dst, v);
|
||||
err = 0;
|
||||
|
||||
_end:
|
||||
return err;
|
||||
err = snd_config_make_integer(dst, id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_config_set_integer(*dst, v);
|
||||
return 0;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_igetenv, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Merge given strings
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node, with strings definition
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example (result is string "a1b2c3" ]:
|
||||
\code
|
||||
{
|
||||
@func concat
|
||||
strings [ "a1" "b2" "c3" ]
|
||||
default 0
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
char *res = NULL, *tmp;
|
||||
int idx = 0, len = 0, len1, err, hit;
|
||||
|
||||
|
@ -269,8 +383,10 @@ int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
snd_config_for_each(i, next, n) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
char *ptr;
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
long i;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &i);
|
||||
if (err < 0) {
|
||||
SNDERR("id of field %s is not an integer", id);
|
||||
|
@ -303,24 +419,52 @@ int snd_func_concat(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
err = -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
snd_config_set_string(*dst, res);
|
||||
}
|
||||
free(res);
|
||||
__error:
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_concat, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t *src, void *private_data ATTRIBUTE_UNUSED)
|
||||
/**
|
||||
* \brief Get data directory
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node (unused)
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example (result is "/usr/share/alsa" using default paths):
|
||||
\code
|
||||
{
|
||||
int err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
@func datadir
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t *src, snd_config_t *private_data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int err;
|
||||
const char *id;
|
||||
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, DATADIR "/alsa");
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_datadir, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
static int open_ctl(long card, snd_ctl_t **ctl)
|
||||
{
|
||||
|
@ -344,19 +488,53 @@ static int string_from_integer(char **dst, long v)
|
|||
}
|
||||
#endif
|
||||
|
||||
int snd_func_private_string(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get string from private_data
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node (type string, id == "string")
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func private_string
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_private_string(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t *src, snd_config_t *private_data)
|
||||
{
|
||||
int err;
|
||||
snd_config_t *n;
|
||||
const char *str, *id;
|
||||
|
||||
if (private_data == NULL)
|
||||
return snd_config_copy(dst, src);
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, (char *)private_data);
|
||||
err = snd_config_test_id(private_data, "string");
|
||||
if (err) {
|
||||
SNDERR("field string not found");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_string(private_data, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("field string is not a string");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, str);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_private_string, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
#ifndef DOC_HIDDEN
|
||||
int snd_determine_driver(int card, char **driver)
|
||||
{
|
||||
snd_ctl_t *ctl = NULL;
|
||||
|
@ -388,25 +566,77 @@ int snd_determine_driver(int card, char **driver)
|
|||
snd_ctl_close(ctl);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
int snd_func_private_card_strtype(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get driver identification using private_data
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node (type = integer, id = "card")
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func private_card_driver
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_private_card_driver(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
char *driver;
|
||||
snd_config_t *n;
|
||||
const char *id;
|
||||
int err;
|
||||
long card;
|
||||
|
||||
if ((err = snd_determine_driver((long)private_data, &driver)) < 0)
|
||||
err = snd_config_test_id(private_data, "card");
|
||||
if (err) {
|
||||
SNDERR("field card not found");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_integer(n, &card);
|
||||
if (err < 0) {
|
||||
SNDERR("field card is not an integer");
|
||||
return err;
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
}
|
||||
if ((err = snd_determine_driver(card, &driver)) < 0)
|
||||
return err;
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, driver);
|
||||
}
|
||||
free(driver);
|
||||
return err;
|
||||
}
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_private_card_strtype, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_private_card_driver, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_card_strtype(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get driver identification
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
snd_config_t *n;
|
||||
@func card_driver
|
||||
card 0
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_card_driver(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n, *val;
|
||||
char *str;
|
||||
long v;
|
||||
int err;
|
||||
|
@ -433,16 +663,42 @@ int snd_func_card_strtype(snd_config_t **dst, snd_config_t *root, snd_config_t *
|
|||
return v;
|
||||
}
|
||||
free(str);
|
||||
return snd_func_private_card_strtype(dst, root, src, (void *)v);
|
||||
err = snd_config_make_integer(&val, "card");
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_config_set_integer(val, v);
|
||||
err = snd_func_private_card_driver(dst, root, src, val);
|
||||
snd_config_delete(val);
|
||||
return err;
|
||||
}
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_card_strtype, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_card_driver, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get card identification
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func card_id
|
||||
card 0
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n;
|
||||
char *res = NULL;
|
||||
snd_ctl_t *ctl = NULL;
|
||||
snd_ctl_card_info_t *info;
|
||||
const char *id;
|
||||
long v;
|
||||
int err;
|
||||
|
||||
|
@ -477,22 +733,46 @@ int snd_func_card_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
|||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, res);
|
||||
}
|
||||
free(res);
|
||||
__error:
|
||||
if (ctl)
|
||||
snd_ctl_close(ctl);
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_card_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Get pcm identification
|
||||
* \param dst The destination node (result type is string)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func pcm_id
|
||||
card 0
|
||||
device 0
|
||||
subdevice 0 # optional
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_pcm_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
{
|
||||
snd_config_t *n;
|
||||
snd_ctl_t *ctl = NULL;
|
||||
snd_pcm_info_t *info;
|
||||
const char *id;
|
||||
long card, device, subdevice = 0;
|
||||
int err;
|
||||
|
||||
|
@ -551,42 +831,94 @@ int snd_func_pcm_id(snd_config_t **dst, snd_config_t *root, snd_config_t *src, v
|
|||
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
|
||||
goto __error;
|
||||
}
|
||||
err = snd_config_make_string(dst, snd_config_get_id(src));
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_string(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_string(*dst, snd_pcm_info_get_id(info));
|
||||
}
|
||||
__error:
|
||||
if (ctl)
|
||||
snd_ctl_close(ctl);
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_pcm_id, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_private_pcm_subdevice(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Get pcm subdevice using private_data
|
||||
* \param dst The destination node (result type is integer)
|
||||
* \param root The root source node
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node (type = pointer, id = "pcm_handle")
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func private_pcm_subdevice
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_private_pcm_subdevice(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED,
|
||||
snd_config_t *src, snd_config_t *private_data)
|
||||
{
|
||||
char *res = NULL;
|
||||
snd_pcm_info_t *info;
|
||||
snd_config_t *n;
|
||||
const char *id;
|
||||
snd_pcm_t *pcm;
|
||||
int err;
|
||||
|
||||
if (private_data == NULL)
|
||||
return snd_config_copy(dst, src);
|
||||
err = snd_config_test_id(private_data, "pcm_handle");
|
||||
if (err) {
|
||||
SNDERR("field pcm_handle not found");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_pointer(private_data, (const void **)&pcm);
|
||||
if (err < 0) {
|
||||
SNDERR("field pcm_handle is not a pointer");
|
||||
return err;
|
||||
}
|
||||
snd_pcm_info_alloca(&info);
|
||||
err = snd_pcm_info((snd_pcm_t *)private_data, info);
|
||||
err = snd_pcm_info(pcm, info);
|
||||
if (err < 0) {
|
||||
SNDERR("snd_ctl_pcm_info error: %s", snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
res = strdup(snd_pcm_info_get_id(info));
|
||||
if (res == NULL)
|
||||
return -ENOMEM;
|
||||
err = snd_config_make_integer(dst, snd_config_get_id(src));
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0) {
|
||||
err = snd_config_make_integer(dst, id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_integer(*dst, snd_pcm_info_get_subdevice(info));
|
||||
free(res);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_private_pcm_subdevice, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
||||
int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src, void *private_data)
|
||||
/**
|
||||
* \brief Copy the refered configuration node
|
||||
* \param dst The destination node (result type is same as refered node)
|
||||
* \param root The root source node (can be modified!!!)
|
||||
* \param src The source node
|
||||
* \param private_data The private_data node
|
||||
* \return a positive value when success otherwise a negative error number
|
||||
*
|
||||
* Example:
|
||||
\code
|
||||
{
|
||||
@func refer
|
||||
file "/etc/myconf.conf" # optional
|
||||
name "id1.id2.id3"
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src,
|
||||
snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *n;
|
||||
const char *file = NULL, *name = NULL;
|
||||
|
@ -631,17 +963,21 @@ int snd_func_refer(snd_config_t **dst, snd_config_t *root, snd_config_t *src, vo
|
|||
goto _end;
|
||||
}
|
||||
err = snd_config_load(root, input);
|
||||
if (err < 0) {
|
||||
snd_input_close(input);
|
||||
if (err < 0)
|
||||
goto _end;
|
||||
}
|
||||
}
|
||||
err = snd_config_search_definition(root, NULL, name, dst);
|
||||
if (err >= 0) {
|
||||
const char *id;
|
||||
err = snd_config_get_id(src, &id);
|
||||
if (err >= 0)
|
||||
err = snd_config_set_id(*dst, snd_config_get_id(src));
|
||||
else
|
||||
err = snd_config_set_id(*dst, id);
|
||||
} else
|
||||
SNDERR("Unable to find definition '%s'", name);
|
||||
_end:
|
||||
return err;
|
||||
}
|
||||
#ifndef DOC_HIDDEN
|
||||
SND_DLSYM_BUILD_VERSION(snd_func_refer, SND_CONFIG_DLSYM_VERSION_EVALUATE);
|
||||
#endif
|
||||
|
|
|
@ -444,7 +444,7 @@ int snd_async_add_ctl_handler(snd_async_handler_t **handler, snd_ctl_t *ctl,
|
|||
was_empty = list_empty(&ctl->async_handlers);
|
||||
list_add_tail(&h->hlist, &ctl->async_handlers);
|
||||
if (was_empty) {
|
||||
err = snd_ctl_async(ctl, getpid(), snd_async_signo);
|
||||
err = snd_ctl_async(ctl, snd_async_get_signo(h), getpid());
|
||||
if (err < 0) {
|
||||
snd_async_del_handler(h);
|
||||
return err;
|
||||
|
@ -474,6 +474,7 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
|
|||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
const char *id;
|
||||
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||
#ifndef PIC
|
||||
extern void *snd_control_open_symbols(void);
|
||||
|
@ -491,9 +492,14 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
|
||||
|
@ -504,7 +510,9 @@ static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -349,7 +349,9 @@ int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBU
|
|||
int err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
|
@ -572,7 +572,9 @@ int _snd_ctl_shm_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_c
|
|||
struct hostent *h;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
@ -617,7 +619,9 @@ int _snd_ctl_shm_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_c
|
|||
}
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "host") == 0) {
|
||||
|
|
|
@ -335,7 +335,10 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf,
|
|||
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
err = safe_strtol(snd_config_get_id(n), &idx);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &idx);
|
||||
if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
|
||||
SNDERR("bad value index");
|
||||
return -EINVAL;
|
||||
|
@ -381,7 +384,7 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
|
||||
static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data)
|
||||
{
|
||||
snd_config_t *conf;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -401,7 +404,9 @@ static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
|
|||
return err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
|
||||
|
@ -576,7 +581,7 @@ static int add_elem(snd_sctl_t *h, snd_config_t *_conf, void *private_data)
|
|||
* \param mode Build mode - SND_SCTL_xxxx
|
||||
* \result zero if success, otherwise a negative error code
|
||||
*/
|
||||
int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, void *private_data, int mode)
|
||||
int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
|
||||
{
|
||||
snd_sctl_t *h;
|
||||
snd_config_iterator_t i, next;
|
||||
|
|
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 mode mode, similar to dlopen
|
||||
* \return pointer to handle
|
||||
*
|
||||
* The extension is a special code for the static build of
|
||||
* the alsa-lib library.
|
||||
*/
|
||||
void *snd_dlopen(const char *name, int mode)
|
||||
{
|
||||
|
@ -55,6 +58,9 @@ void *snd_dlopen(const char *name, int mode)
|
|||
* \brief Close the dynamic library, with ALSA extension
|
||||
* \param handle handle, similar to dlclose
|
||||
* \return zero if success, otherwise an error code
|
||||
*
|
||||
* The extension is a special code for the static build of
|
||||
* the alsa-lib library.
|
||||
*/
|
||||
int snd_dlclose(void *handle)
|
||||
{
|
||||
|
@ -80,6 +86,8 @@ static int snd_dlsym_verify(void *handle, const char *name, const char *version)
|
|||
if (handle == NULL)
|
||||
return -EINVAL;
|
||||
vname = alloca(1 + strlen(name) + strlen(version) + 1);
|
||||
if (vname == NULL)
|
||||
return -ENOMEM;
|
||||
vname[0] = '_';
|
||||
strcpy(vname + 1, name);
|
||||
strcat(vname, version);
|
||||
|
@ -89,11 +97,16 @@ static int snd_dlsym_verify(void *handle, const char *name, const char *version)
|
|||
SNDERR("unable to verify version for symbol %s", name);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Resolve the symbol, with ALSA extension
|
||||
* \param handle handle, similar to dlsym
|
||||
* \param name symbol name
|
||||
* \param version symbol version
|
||||
*
|
||||
* This special version of dlsym function checks also
|
||||
* the version of symbol. The version of a symbol should
|
||||
* be defined using #SND_DLSYM_BUILD_VERSION macro.
|
||||
*/
|
||||
void *snd_dlsym(void *handle, const char *name, const char *version)
|
||||
{
|
||||
|
@ -101,8 +114,8 @@ void *snd_dlsym(void *handle, const char *name, const char *version)
|
|||
|
||||
#ifndef PIC
|
||||
if (handle == &snd_dlsym_start) {
|
||||
/* it's the funny part, we are looking for a symbol */
|
||||
/* in a static library */
|
||||
/* it's the funny part: */
|
||||
/* we are looking for a symbol in a static library */
|
||||
struct snd_dlsym_link *link = snd_dlsym_start;
|
||||
while (link) {
|
||||
if (!strcmp(name, link->dlsym_name))
|
||||
|
|
|
@ -46,6 +46,7 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
|
|||
int err;
|
||||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_hwdep_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||
#ifndef PIC
|
||||
|
@ -64,9 +65,14 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(hwdep_root, "hwdep_type", str, &type_conf);
|
||||
|
@ -77,7 +83,9 @@ static int snd_hwdep_open_conf(snd_hwdep_t **hwdep,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -152,7 +152,9 @@ int _snd_hwdep_hw_open(snd_hwdep_t **hwdep, char *name,
|
|||
int err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
464
src/pcm/pcm.c
464
src/pcm/pcm.c
|
@ -1,5 +1,6 @@
|
|||
/**
|
||||
* \file pcm/pcm.c
|
||||
* \ingroup PCM
|
||||
* \brief PCM Interface
|
||||
* \author Jaroslav Kysela <perex@suse.cz>
|
||||
* \author Abramo Bagnara <abramo@alsa-project.org>
|
||||
|
@ -11,6 +12,8 @@
|
|||
* 44100 you'll hear 44100 frames per second. The size in bytes of a
|
||||
* frame may be obtained from bits needed to store a sample and
|
||||
* channels count.
|
||||
*
|
||||
* See the \ref pcm page for more details.
|
||||
*/
|
||||
/*
|
||||
* PCM Interface - main file
|
||||
|
@ -33,6 +36,449 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*! \page pcm PCM (digital audio) interface
|
||||
|
||||
<P>Although abbreviation PCM stands for Pulse Code Modulation, we are
|
||||
understanding it as general digital audio processing with volume samples
|
||||
generated in continuous time periods.</P>
|
||||
|
||||
<P>Digital audio is the most commonly used method of representing
|
||||
sound inside a computer. In this method sound is stored as a sequence of
|
||||
samples taken from the audio signal using constant time intervals.
|
||||
A sample represents volume of the signal at the moment when it
|
||||
was measured. In uncompressed digital audio each sample require one
|
||||
or more bytes of storage. The number of bytes required depends on number
|
||||
of channels (mono, stereo) and sample format (8 or 16 bits, mu-Law, etc.).
|
||||
The length of this interval determines the sampling rate. Commonly used
|
||||
sampling rates are between 8kHz (telephone quality) and
|
||||
48kHz (DAT tapes).</P>
|
||||
|
||||
<P>The physical devices used in digital audio are called the
|
||||
ADC (Analog to Digital Converter) and DAC (Digital to Analog Converter).
|
||||
A device containing both ADC and DAC is commonly known as a codec.
|
||||
The codec device used in a Sound Blaster cards is called a DSP which
|
||||
is somewhat misleading since DSP also stands for Digital Signal Processor
|
||||
(the SB DSP chip is very limited when compared to "true" DSP chips).</P>
|
||||
|
||||
<P>Sampling parameters affect the quality of sound which can be
|
||||
reproduced from the recorded signal. The most fundamental parameter
|
||||
is sampling rate which limits the highest frequency that can be stored.
|
||||
It is well known (Nyquist's Sampling Theorem) that the highest frequency
|
||||
that can be stored in a sampled signal is at most 1/2 of the sampling
|
||||
frequency. For example, an 8 kHz sampling rate permits the recording of
|
||||
a signal in which the highest frequency is less than 4 kHz. Higher frequency
|
||||
signals must be filtered out before feeding them to ADC.</P>
|
||||
|
||||
<P>Sample encoding limits the dynamic range of a recorded signal
|
||||
(difference between the faintest and the loudest signal that can be
|
||||
recorded). In theory the maximum dynamic range of signal is number_of_bits *
|
||||
6dB. This means that 8 bits sampling resolution gives dynamic range of
|
||||
48dB and 16 bit resolution gives 96dB.</P>
|
||||
|
||||
<P>Quality has price. The number of bytes required to store an audio
|
||||
sequence depends on sampling rate, number of channels and sampling
|
||||
resolution. For example just 8000 bytes of memory is required to store
|
||||
one second of sound using 8kHz/8 bits/mono but 48kHz/16bit/stereo takes
|
||||
192 kilobytes. A 64 kbps ISDN channel is required to transfer a
|
||||
8kHz/8bit/mono audio stream in real time, and about 1.5Mbps is required
|
||||
for DAT quality (48kHz/16bit/stereo). On the other hand it is possible
|
||||
to store just 5.46 seconds of sound in a megabyte of memory when using
|
||||
48kHz/16bit/stereo sampling. With 8kHz/8bits/mono it is possible to store
|
||||
131 seconds of sound using the same amount of memory. It is possible
|
||||
to reduce memory and communication costs by compressing the recorded
|
||||
signal but this is beyond the scope of this document. </P>
|
||||
|
||||
\section pcm_general_overview General overview
|
||||
|
||||
ALSA uses the ring buffer to store outgoing (playback) and incoming (capture,
|
||||
record) samples. There are two pointers being mantained to allow
|
||||
a precise communication between application and device pointing to current
|
||||
processed sample by hardware and last processed sample by application.
|
||||
The modern audio chips allow to program the transfer time periods.
|
||||
It means that the stream of samples is divided to small chunks. Device
|
||||
acknowledges to application when the transfer of a chunk is complete.
|
||||
|
||||
\section pcm_transfer Transfer methods in unix environments
|
||||
|
||||
In the unix environment, data chunk acknowledges are received via standard I/O
|
||||
calls or event waiting routines (poll or select function). To accomplish
|
||||
this list, the asynchronous notification of acknowledges should be listed
|
||||
here. The ALSA implementation for these methods is described in
|
||||
the \ref alsa_transfers section.
|
||||
|
||||
\subsection pcm_transfer_io Standard I/O transfers
|
||||
|
||||
The standard I/O transfers are using the read (see 'man 2 read') and write
|
||||
(see 'man 2 write') C functions. There are two basic behaviours of these
|
||||
functions - blocked and non-blocked (see the O_NONBLOCK flag for the
|
||||
standard C open function - see 'man 2 open'). In non-blocked behaviour,
|
||||
these I/O functions never stops, they return -EAGAIN error code, when no
|
||||
data can be transferred (the ring buffer is full in our case). In blocked
|
||||
behaviour, these I/O functions stop and wait until there is a room in the
|
||||
ring buffer (playback) or until there are a new samples (capture). The ALSA
|
||||
implementation can be found in the \ref alsa_pcm_rw section.
|
||||
|
||||
\subsection pcm_transfer_event Event waiting routines
|
||||
|
||||
The poll or select functions (see 'man 2 poll' or 'man 2 select' for further
|
||||
details) allows to receive requests/events from the device while
|
||||
an application is waiting on events from other sources (like keyboard, screen,
|
||||
network etc.), too. The select function is old and deprecated in modern
|
||||
applications, so the ALSA library does not support it. The implemented
|
||||
transfer routines can be found in the \ref alsa_transfers section.
|
||||
|
||||
\subsection pcm_transfer_async Asynchronous notification
|
||||
|
||||
ALSA driver and library knows to handle the asynchronous notifications over
|
||||
the SIGIO signal. This signal allows to interrupt application and transfer
|
||||
data in the signal handler. For further details see the sigaction function
|
||||
('man 2 sigaction'). The section \ref pcm_async describes the ALSA API for
|
||||
this extension. The implemented transfer routines can be found in the
|
||||
\ref alsa_transfers section.
|
||||
|
||||
\section pcm_open_behaviour Blocked and non-blocked open
|
||||
|
||||
The ALSA PCM API uses a different behaviour when the device is opened
|
||||
with blocked or non-blocked mode. The mode can be specified with
|
||||
\a mode argument in \link ::snd_pcm_open() \endlink function.
|
||||
The blocked mode is the default (without \link ::SND_PCM_NONBLOCK \endlink mode).
|
||||
In this mode, the behaviour is that if the resources have already used
|
||||
with another application, then it blocks the caller, until resources are
|
||||
free. The non-blocked behaviour (with \link ::SND_PCM_NONBLOCK \endlink)
|
||||
doesn't block the caller in any way and returns -EBUSY error when the
|
||||
resources are not available. Note that the mode also determines the
|
||||
behaviour of standard I/O calls, returning -EAGAIN when non-blocked mode is
|
||||
used and the ring buffer is full (playback) or empty (capture).
|
||||
The operation mode for I/O calls can be changed later with
|
||||
the \link snd_pcm_nonblock() \endlink function.
|
||||
|
||||
\section pcm_async Asynchronous mode
|
||||
|
||||
There is also possibility to receive asynchronous notification after
|
||||
specified time periods. You may see the \link ::SND_PCM_ASYNC \endlink
|
||||
mode for \link ::snd_pcm_open() \endlink function and
|
||||
\link ::snd_async_add_pcm_handler() \endlink function for further details.
|
||||
|
||||
\section pcm_handshake Handshake between application and library
|
||||
|
||||
The ALSA PCM API design uses the states to determine the communication
|
||||
phase between application and library. The actual state can be determined
|
||||
using \link ::snd_pcm_state() \endlink call. There are these states:
|
||||
|
||||
\par SND_PCM_STATE_OPEN
|
||||
The PCM device is in the open state. After the \link ::snd_pcm_open() \endlink open call,
|
||||
the device is in this state. Also, when \link ::snd_pcm_hw_params() \endlink call fails,
|
||||
then this state is entered to force application calling
|
||||
\link ::snd_pcm_hw_params() \endlink function to set right communication
|
||||
parameters.
|
||||
|
||||
\par SND_PCM_STATE_SETUP
|
||||
The PCM device has accepted communication parameters and it is waiting
|
||||
for \link ::snd_pcm_prepare() \endlink call to prepare the hardware for
|
||||
selected operation (playback or capture).
|
||||
|
||||
\par SND_PCM_STATE_PREPARE
|
||||
The PCM device is prepared for operation. Application can use
|
||||
\link ::snd_pcm_start() \endlink call, write or read data to start
|
||||
the operation.
|
||||
|
||||
\par SND_PCM_STATE_RUNNING
|
||||
The PCM device is running. It processes the samples. The stream can
|
||||
be stopped using the \link ::snd_pcm_drop() \endlink or
|
||||
\link ::snd_pcm_drain \endlink calls.
|
||||
|
||||
\par SND_PCM_STATE_XRUN
|
||||
The PCM device reached overrun (capture) or underrun (playback).
|
||||
You can use the -EPIPE return code from I/O functions
|
||||
(\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_writen() \endlink,
|
||||
\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readi() \endlink)
|
||||
to determine this state without checking
|
||||
the actual state via \link ::snd_pcm_state() \endlink call. You can recover from
|
||||
this state with \link ::snd_pcm_prepare() \endlink,
|
||||
\link ::snd_pcm_drop() \endlink or \link ::snd_pcm_drain() \endlink calls.
|
||||
|
||||
\par SND_PCM_STATE_DRAINING
|
||||
The device is in this state when application using the capture mode
|
||||
called \link ::snd_pcm_drain() \endlink function. Until all data are
|
||||
read from the internal ring buffer using I/O routines
|
||||
(\link ::snd_pcm_readi() \endlink, \link ::snd_pcm_readn() \endlink),
|
||||
then the device stays in this state.
|
||||
|
||||
\par SND_PCM_STATE_PAUSED
|
||||
The device is in this state when application called
|
||||
the \link ::snd_pcm_pause() \endlink function until the pause is released.
|
||||
Not all hardware supports this feature. Application should check the
|
||||
capability with the \link ::snd_pcm_hw_params_can_pause() \endlink.
|
||||
|
||||
\par SND_PCM_STATE_SUSPENDED
|
||||
The device is in the suspend state provoked with the power management
|
||||
system. The stream can be resumed using \link ::snd_pcm_resume() \endlink
|
||||
call, but not all hardware supports this feature. Application should check
|
||||
the capability with the \link ::snd_pcm_hw_params_can_resume() \endlink.
|
||||
In other case, the calls \link ::snd_pcm_prepare() \endlink,
|
||||
\link ::snd_pcm_drop() \endlink, \link ::snd_pcm_drain() \endlink can be used
|
||||
to leave this state.
|
||||
|
||||
\section pcm_formats PCM formats
|
||||
|
||||
The full list of formats present the \link ::snd_pcm_format_t \endlink type.
|
||||
The 24-bit linear samples uses 32-bit physical space, but the sample is
|
||||
stored in low three bits. Some hardware does not support processing of full
|
||||
range, thus you may get the significative bits for linear samples via
|
||||
\link ::snd_pcm_hw_params_get_sbits \endlink function. The example: ICE1712
|
||||
chips support 32-bit sample processing, but low byte is ignored (playback)
|
||||
or zero (capture). The function \link ::snd_pcm_hw_params_get_sbits() \endlink
|
||||
returns 24 in the case.
|
||||
|
||||
\section alsa_transfers ALSA transfers
|
||||
|
||||
There are two methods to transfer samples in application. The first method
|
||||
is the standard read / write one. The second method, uses the direct audio
|
||||
buffer to communicate with the device while ALSA library manages this space
|
||||
itself. You can find examples of all communication schemes for playback
|
||||
in \ref example_test_pcm "Sine-wave generator example". To complete the
|
||||
list, we should note that \link ::snd_pcm_wait \endlink function contains
|
||||
embedded poll waiting implementation.
|
||||
|
||||
\subsection alsa_pcm_rw Read / Write transfer
|
||||
|
||||
There are two versions of read / write routines. The first expects the
|
||||
interleaved samples at input, and the second one expects non-interleaved
|
||||
(samples in separated buffers) at input. There are these functions for
|
||||
interleaved transfers: \link ::snd_pcm_writei \endlink,
|
||||
\link ::snd_pcm_readi \endlink. For non-interleaved transfers, there are
|
||||
these functions: \link ::snd_pcm_writen \endlink and \link ::snd_pcm_readn
|
||||
\endlink.
|
||||
|
||||
\subsection alsa_mmap_rw Direct Read / Write transfer (via mmaped areas)
|
||||
|
||||
There are two functions for this kind of transfer. Application can get an
|
||||
access to memory areas via \link ::snd_pcm_mmap_begin \endlink function.
|
||||
This functions returns the areas (single area is equal to a channel)
|
||||
containing the direct pointers to memory and sample position description
|
||||
in \link ::snd_pcm_channel_area_t \endlink structure. After application
|
||||
transfers the data in the memory areas, then it must be acknowledged
|
||||
the end of transfer via \link ::snd_pcm_mmap_commit() \endlink function
|
||||
to allow the ALSA library update the pointers to ring buffer. This sort of
|
||||
communication is also called "zero-copy", because the device does not require
|
||||
to copy the samples from application to another place in system memory.
|
||||
|
||||
\par
|
||||
|
||||
If you like to use the compatibility functions in mmap mode, there are
|
||||
read / write routines equaling to standard read / write transfers. Using
|
||||
these functions discards the benefits of direct access to memory region.
|
||||
See the \link ::snd_pcm_mmap_readi() \endlink,
|
||||
\link ::snd_pcm_writei() \endlink, \link ::snd_pcm_readn() \endlink
|
||||
and \link ::snd_pcm_writen() \endlink functions.
|
||||
|
||||
\section pcm_params Managing parameters
|
||||
|
||||
The ALSA PCM device uses two groups of PCM related parameters. The hardware
|
||||
parameters contains the stream description like format, rate, count of
|
||||
channels, ring buffer size etc. The software parameters contains the
|
||||
software (driver) related parameters. The communicatino behaviour can be
|
||||
controlled via these parameters, like automatic start, automatic stop,
|
||||
interrupting (chunk acknowledge) etc. The software parameters can be
|
||||
modified at any time (when valid hardware parameters are set). It includes
|
||||
the running state as well.
|
||||
|
||||
\subsection pcm_hw_params Hardware related parameters
|
||||
|
||||
The ALSA PCM devices use the parameter refining system for hardware
|
||||
parameters - \link ::snd_pcm_hw_params_t \endlink. It means, that
|
||||
application choose the full-range of configurations at first and then
|
||||
application sets single parameters until all parameters are elementary
|
||||
(definite).
|
||||
|
||||
\par Access modes
|
||||
|
||||
ALSA knows about five access modes. The first three can be used for direct
|
||||
communication. The access mode \link ::SND_PCM_ACCESS_MMAP_INTERLEAVED \endlink
|
||||
determines the direct memory area and interleaved sample organization.
|
||||
Interleaved organization means, that samples from channels are mixed together.
|
||||
The access mode \link ::SND_PCM_ACCESS_MMAP_NONINTERLEAVED \endlink
|
||||
determines the direct memory area and non-interleaved sample organization.
|
||||
Each channel has a separate buffer in the case. The complex direct memory
|
||||
organization represents the \link ::SND_PCM_ACCESS_MMAP_COMPLEX \endlink
|
||||
access mode. The sample organization does not fit the interleaved or
|
||||
non-interleaved access modes in the case. The last two access modes
|
||||
describes the read / write access methods.
|
||||
The \link ::SND_PCM_ACCESS_RW_INTERLEAVED \endlink access represents the read /
|
||||
write interleaved access and the \link ::SND_PCM_ACCESS_RW_NONINTERLEAVED \endlink
|
||||
represents the non-interleaved access.
|
||||
|
||||
\par Formats
|
||||
|
||||
The full list of formats is available in \link ::snd_pcm_format_t \endlink
|
||||
enumeration.
|
||||
|
||||
\subsection pcm_sw_params Software related parameters
|
||||
|
||||
These parameters - \link ::snd_pcm_sw_params_t \endlink can be modified at
|
||||
any time including the running state.
|
||||
|
||||
\par Minimum available count of samples
|
||||
|
||||
This parameter controls the wakeup point. If the count of available samples
|
||||
is equal or greater than this value, then application will be activated.
|
||||
|
||||
\par Timestamp mode
|
||||
|
||||
The timestamp mode specifies, if timestamps are activated. Currently, only
|
||||
\link ::SND_PCM_TSTAMP_NONE \endlink and \link ::SND_PCM_TSTAMP_MMAP
|
||||
\endlink modes are known. The mmap mode means that timestamp is taken
|
||||
on every period time boundary.
|
||||
|
||||
\par Minimal sleep
|
||||
|
||||
This parameters means the minimum of ticks to sleep using a standalone
|
||||
timer (usually the system timer). The tick resolution can be obtained
|
||||
via the function \link ::snd_pcm_hw_params_get_tick_time \endlink. This
|
||||
function can be used to fine-tune the transfer acknowledge process. It could
|
||||
be useful especially when some hardware does not support small transfer
|
||||
periods.
|
||||
|
||||
\par Transfer align
|
||||
|
||||
The read / write transfers can be aligned to this sample count. The modulo
|
||||
is ignored by device. Usually, this value is set to one (no align).
|
||||
|
||||
\par Start threshold
|
||||
|
||||
The start threshold parameter is used to determine the start point in
|
||||
stream. For playback, if samples in ring buffer is equal or greater than
|
||||
the start threshold parameters and the stream is not running, the stream will
|
||||
be started automatically from the device. For capture, if the application wants
|
||||
to read count of samples equal or greater then the stream will be started.
|
||||
If you want to use explicit start (\link ::snd_pcm_start \endlink), you can
|
||||
set this value greater than ring buffer size (in samples), but use the
|
||||
constant MAXINT is not a bad idea.
|
||||
|
||||
\par Stop threshold
|
||||
|
||||
Similarly, the stop threshold parameter is used to automatically stop
|
||||
the running stream, when the available samples crosses this boundary.
|
||||
It means, for playback, the empty samples in ring buffer and for capture,
|
||||
the filled (used) samples in ring buffer.
|
||||
|
||||
\par Silence threshold
|
||||
|
||||
The silence threshold specifies count of samples filled with silence
|
||||
ahead of the current application pointer for playback. It is useable
|
||||
for applications when an overrun is possible (like tasks depending on
|
||||
network I/O etc.). If application wants to manage the ahead samples itself,
|
||||
the \link ::snd_pcm_rewind() \endlink function allows to forget the last
|
||||
samples in the stream.
|
||||
|
||||
\section pcm_status Obtaining device status
|
||||
|
||||
The device status is stored in \link ::snd_pcm_status_t \endlink structure.
|
||||
These parameters can be obtained: the current stream state -
|
||||
\link ::snd_pcm_status_get_state \endlink, timestamp of trigger -
|
||||
\link ::snd_pcm_status_get_trigger_tstamp \endlink, timestamp of last
|
||||
update \link ::snd_pcm_status_get_tstamp \endlink, delay in samples -
|
||||
\link ::snd_pcm_status_get_delay \endlink, available count in samples -
|
||||
\link ::snd_pcm_status_get_avail \endlink, maximum available samples -
|
||||
\link ::snd_pcm_status_get_avail_max \endlink, ADC overrange count in
|
||||
samples - \link ::snd_pcm_status_get_overrange \endlink. The last two
|
||||
parameters - avail_max and overrange are reset to zero after the status
|
||||
call.
|
||||
|
||||
\subsection pcm_status_fast Obtaining fast device status
|
||||
|
||||
The function \link ::snd_pcm_avail_update \endlink updates the current
|
||||
available count of samples for writting (playback) or filled samples for
|
||||
reading (capture).
|
||||
<p>
|
||||
The function \link ::snd_pcm_delay \endlink returns the delay in samples.
|
||||
For playback, it means count of samples in the ring buffer before
|
||||
the next sample will be sent to DAC. For capture, it means count of samples
|
||||
in the ring buffer before the next sample will be captured from ADC.
|
||||
|
||||
\section pcm_action Managing the stream state
|
||||
|
||||
These functions directly and indirectly affecting the stream state:
|
||||
|
||||
\par snd_pcm_hw_params
|
||||
The \link ::snd_pcm_hw_params \endlink function brings the stream state
|
||||
to \link ::SND_PCM_STATE_SETUP \endlink
|
||||
if successfully finishes, otherwise the state \link ::SND_PCM_STATE_OPEN
|
||||
\endlink is entered.
|
||||
|
||||
\par snd_pcm_prepare
|
||||
The \link ::snd_pcm_prepare \endlink function enters the
|
||||
\link ::SND_PCM_STATE_PREPARED \endlink after a successfull finish.
|
||||
|
||||
\par snd_pcm_start
|
||||
The \link ::snd_pcm_start \endlink function enters
|
||||
the \link ::SND_PCM_STATE_RUNNING \endlink after a successfull finish.
|
||||
|
||||
\par snd_pcm_drop
|
||||
The \link ::snd_pcm_drop \endlink function enters the
|
||||
\link ::SND_PCM_STATE_SETUP \endlink state.
|
||||
|
||||
\par snd_pcm_drain
|
||||
The \link ::snd_pcm_drain \endlink function enters the
|
||||
\link ::SND_PCM_STATE_DRAINING \endlink, if
|
||||
the capture device has some samples in the ring buffer otherwise
|
||||
\link ::SND_PCM_STATE_SETUP \endlink state is entered.
|
||||
|
||||
\par snd_pcm_pause
|
||||
The \link ::snd_pcm_pause \endlink function enters the
|
||||
\link ::SND_PCM_STATE_PAUSED \endlink or
|
||||
\link ::SND_PCM_STATE_RUNNING \endlink.
|
||||
|
||||
\par snd_pcm_writei, snd_pcm_writen
|
||||
The \link ::snd_pcm_writei \endlink and \link ::snd_pcm_writen \endlink
|
||||
functions can conditionally start the stream -
|
||||
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
|
||||
software parameter.
|
||||
|
||||
\par snd_pcm_readi, snd_pcm_readn
|
||||
The \link ::snd_pcm_readi \endlink and \link ::snd_pcm_readn \endlink
|
||||
functions can conditionally start the stream -
|
||||
\link ::SND_PCM_STATE_RUNNING \endlink. They depend on the start threshold
|
||||
software parameter.
|
||||
|
||||
\section pcm_sync Streams synchronization
|
||||
|
||||
There are two functions allowing link multiple streams together. In the
|
||||
case, the linking means that all operations are synchronized. Because the
|
||||
drivers cannot guarantee the synchronization (sample resolution) on hardware
|
||||
lacking this feature, the \link ::snd_pcm_info_get_sync \endlink function
|
||||
returns synchronization ID - \link ::snd_pcm_sync_id_t \endlink, which is equal
|
||||
for hardware synchronizated streams. When the \link ::snd_pcm_link \endlink
|
||||
function is called, all operations managing the stream state for these two
|
||||
streams are joined. The oposite function is \link ::snd_pcm_unlink \endlink.
|
||||
|
||||
\section pcm_examples Examples
|
||||
|
||||
The full featured examples with cross-links:
|
||||
|
||||
\par Sine-wave generator
|
||||
\ref example_test_pcm "example code"
|
||||
\par
|
||||
This example shows various transfer methods for the playback direction.
|
||||
|
||||
\par Latency measuring tool
|
||||
\ref example_test_latency "example code"
|
||||
\par
|
||||
This example shows the measuring of minimal latency between capture and
|
||||
playback devices.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* \example ../test/pcm.c
|
||||
* \anchor example_test_pcm
|
||||
*/
|
||||
/**
|
||||
* \example ../test/latency.c
|
||||
* \anchor example_test_latency
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
@ -995,7 +1441,7 @@ int snd_async_add_pcm_handler(snd_async_handler_t **handler, snd_pcm_t *pcm,
|
|||
was_empty = list_empty(&pcm->async_handlers);
|
||||
list_add_tail(&h->hlist, &pcm->async_handlers);
|
||||
if (was_empty) {
|
||||
err = snd_pcm_async(pcm, snd_async_signo, getpid());
|
||||
err = snd_pcm_async(pcm, snd_async_signo(h), getpid());
|
||||
if (err < 0) {
|
||||
snd_async_del_handler(h);
|
||||
return err;
|
||||
|
@ -1025,6 +1471,7 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
|
|||
int err;
|
||||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_pcm_t **, const char *,
|
||||
snd_config_t *, snd_config_t *,
|
||||
|
@ -1045,9 +1492,14 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);
|
||||
|
@ -1058,7 +1510,9 @@ static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
@ -4645,7 +5099,9 @@ int snd_pcm_slave_conf(snd_config_t *root, snd_config_t *conf,
|
|||
va_end(args);
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "pcm") == 0) {
|
||||
|
|
|
@ -557,7 +557,9 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_pcm_format_t sformat;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -430,7 +430,9 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_pcm_format_t sformat;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -199,7 +199,9 @@ int _snd_pcm_copy_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_t *slave = NULL, *sconf;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -474,7 +474,9 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
|||
long fd = -1;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -335,7 +335,7 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
|
|||
{
|
||||
int err;
|
||||
char buf[256];
|
||||
const char *str;
|
||||
const char *str, *id;
|
||||
const char *lib = NULL, *install = NULL;
|
||||
snd_config_t *type = NULL, *args = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
|
@ -347,7 +347,9 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
|
|||
}
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0) {
|
||||
|
@ -365,9 +367,14 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
|
|||
SNDERR("type is not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_config_get_id(type, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(type, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(type));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(root, "pcm_hook_type", str, &type);
|
||||
|
@ -378,7 +385,9 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
|
|||
}
|
||||
snd_config_for_each(i, next, type) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
@ -446,7 +455,9 @@ int _snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_t *hooks = NULL;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
@ -620,6 +631,7 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
|
|||
char ctl_name[16];
|
||||
snd_ctl_t *ctl;
|
||||
snd_sctl_t *sctl;
|
||||
snd_config_t *pcm_conf = NULL;
|
||||
snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL;
|
||||
assert(conf);
|
||||
assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND);
|
||||
|
@ -638,9 +650,13 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
|
|||
SNDERR("Cannot open CTL %s", ctl_name);
|
||||
return err;
|
||||
}
|
||||
err = snd_sctl_build(&sctl, ctl, conf, pcm, 0);
|
||||
err = snd_config_make_pointer(&pcm_conf, "pcm_handle");
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
goto _err;
|
||||
snd_config_set_pointer(pcm_conf, pcm);
|
||||
err = snd_sctl_build(&sctl, ctl, conf, pcm_conf, 0);
|
||||
if (err < 0)
|
||||
goto _err;
|
||||
err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS,
|
||||
snd_pcm_hook_ctl_elems_hw_params, sctl);
|
||||
if (err < 0)
|
||||
|
@ -662,5 +678,7 @@ int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf)
|
|||
if (h_close)
|
||||
snd_pcm_hook_remove(h_close);
|
||||
snd_sctl_free(sctl);
|
||||
if (pcm_conf)
|
||||
snd_config_delete(pcm_conf);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -669,7 +669,9 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
|||
int err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "card") == 0) {
|
||||
|
|
|
@ -335,7 +335,9 @@ int _snd_pcm_linear_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_pcm_format_t sformat;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -663,6 +663,7 @@ static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
|
|||
{
|
||||
char buf[256];
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL, *str = NULL;
|
||||
snd_config_t *c, *type_conf;
|
||||
int (*open_func)(snd_pcm_t *, const char *,
|
||||
|
@ -679,16 +680,23 @@ static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
|
|||
SNDERR("type is not defined");
|
||||
goto _err;
|
||||
}
|
||||
err = snd_config_get_id(c, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
goto _err;
|
||||
}
|
||||
err = snd_config_get_string(c, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(c));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
goto _err;
|
||||
}
|
||||
err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf);
|
||||
if (err >= 0) {
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
@ -745,7 +753,9 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_t *scopes = NULL;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
@ -791,8 +801,9 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
|
|||
return 0;
|
||||
snd_config_for_each(i, next, scopes) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *str;
|
||||
const char *id, *str;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_config_get_string(n, &str) >= 0) {
|
||||
err = snd_config_search_definition(root, "pcm_scope", str, &n);
|
||||
if (err < 0) {
|
||||
|
|
|
@ -445,7 +445,9 @@ int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_pcm_format_t sformat;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -684,7 +684,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
unsigned int channels_count = 0;
|
||||
snd_config_for_each(i, inext, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slaves") == 0) {
|
||||
|
@ -731,7 +733,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_for_each(i, inext, bindings) {
|
||||
long cchannel;
|
||||
snd_config_t *m = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(m);
|
||||
const char *id;
|
||||
if (snd_config_get_id(m, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &cchannel);
|
||||
if (err < 0 || cchannel < 0) {
|
||||
SNDERR("Invalid channel number: %s", id);
|
||||
|
@ -756,8 +760,11 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
idx = 0;
|
||||
snd_config_for_each(i, inext, slaves) {
|
||||
snd_config_t *m = snd_config_iterator_entry(i);
|
||||
const char *id;
|
||||
int channels;
|
||||
slaves_id[idx] = snd_config_get_id(m);
|
||||
if (snd_config_get_id(m, &id) < 0)
|
||||
continue;
|
||||
slaves_id[idx] = id;
|
||||
err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
|
||||
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
|
||||
if (err < 0)
|
||||
|
@ -773,7 +780,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
int slave = -1;
|
||||
long val;
|
||||
const char *str;
|
||||
const char *id = snd_config_get_id(m);
|
||||
const char *id;
|
||||
if (snd_config_get_id(m, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &cchannel);
|
||||
if (err < 0 || cchannel < 0) {
|
||||
SNDERR("Invalid channel number: %s", id);
|
||||
|
@ -782,7 +791,9 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
}
|
||||
snd_config_for_each(j, jnext, m) {
|
||||
snd_config_t *n = snd_config_iterator_entry(j);
|
||||
id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
@ -817,13 +828,13 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|||
goto _free;
|
||||
}
|
||||
if (slave < 0 || (unsigned int)slave >= slaves_count) {
|
||||
SNDERR("Invalid or missing sidx");
|
||||
SNDERR("Invalid or missing sidx for channel %s", id);
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
if (schannel < 0 ||
|
||||
(unsigned int) schannel >= slaves_channels[slave]) {
|
||||
SNDERR("Invalid or missing schannel");
|
||||
SNDERR("Invalid or missing schannel for channel %s", id);
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
|
|
|
@ -376,7 +376,9 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_iterator_t i, next;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
SNDERR("Unknown field %s", id);
|
||||
|
|
|
@ -828,7 +828,9 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
|
|||
int schannels = -1, srate = -1;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -545,7 +545,9 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
|
|||
int srate = -1;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -796,10 +796,13 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|||
snd_config_t *in = snd_config_iterator_entry(i);
|
||||
snd_config_iterator_t j, jnext;
|
||||
long cchannel;
|
||||
err = safe_strtol(snd_config_get_id(in), &cchannel);
|
||||
const char *id;
|
||||
if (!snd_config_get_id(in, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &cchannel);
|
||||
if (err < 0 ||
|
||||
cchannel < 0 || (unsigned int) cchannel > tt_csize) {
|
||||
SNDERR("Invalid client channel: %s", snd_config_get_id(in));
|
||||
SNDERR("Invalid client channel: %s", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
||||
|
@ -808,7 +811,9 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|||
snd_config_t *jnode = snd_config_iterator_entry(j);
|
||||
double value;
|
||||
long schannel;
|
||||
const char *id = snd_config_get_id(jnode);
|
||||
const char *id;
|
||||
if (snd_config_get_id(jnode, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &schannel);
|
||||
if (err < 0 ||
|
||||
schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
||||
|
@ -855,7 +860,9 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
|||
unsigned int cused, sused;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
|
|
@ -1388,7 +1388,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
|
|||
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "slave") == 0) {
|
||||
|
@ -1436,7 +1438,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
|
|||
snd_config_for_each(i, next, bindings) {
|
||||
long cchannel = -1;
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
err = safe_strtol(id, &cchannel);
|
||||
if (err < 0 || cchannel < 0) {
|
||||
SNDERR("Invalid client channel in binding: %s", id);
|
||||
|
@ -1455,9 +1459,11 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
|
|||
|
||||
snd_config_for_each(i, next, bindings) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
long cchannel;
|
||||
long schannel = -1;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
cchannel = atoi(id);
|
||||
err = snd_config_get_integer(n, &schannel);
|
||||
if (err < 0) {
|
||||
|
|
|
@ -740,7 +740,9 @@ int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
|
|||
struct hostent *h;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (snd_pcm_conf_generic_id(id))
|
||||
continue;
|
||||
if (strcmp(id, "server") == 0) {
|
||||
|
@ -781,7 +783,9 @@ int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
|
|||
}
|
||||
snd_config_for_each(i, next, sconfig) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "host") == 0) {
|
||||
|
|
|
@ -71,6 +71,7 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
|
|||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
snd_rawmidi_params_t params;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_rawmidi_t **, snd_rawmidi_t **,
|
||||
const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||
|
@ -90,9 +91,14 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(rawmidi_root, "rawmidi_type", str, &type_conf);
|
||||
|
@ -103,7 +109,9 @@ static int snd_rawmidi_open_conf(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -317,7 +317,9 @@ int _snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
|
|||
int err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
|
@ -73,6 +73,7 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
|
|||
int err;
|
||||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_seq_t **, const char *,
|
||||
snd_config_t *, snd_config_t *,
|
||||
|
@ -93,9 +94,14 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(seq_root, "seq_type", str, &type_conf);
|
||||
|
@ -106,7 +112,9 @@ static int snd_seq_open_conf(snd_seq_t **seqp, const char *name,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -515,7 +515,9 @@ int _snd_seq_hw_open(snd_seq_t **handlep, char *name,
|
|||
snd_config_iterator_t i, next;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
|
@ -45,6 +45,7 @@ static int snd_timer_open_conf(snd_timer_t **timer,
|
|||
int err;
|
||||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_timer_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||
#ifndef PIC
|
||||
|
@ -63,9 +64,14 @@ static int snd_timer_open_conf(snd_timer_t **timer,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(timer_root, "timer_type", str, &type_conf);
|
||||
|
@ -76,7 +82,9 @@ static int snd_timer_open_conf(snd_timer_t **timer,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -216,7 +216,9 @@ int _snd_timer_hw_open(snd_timer_t **timer, char *name,
|
|||
int err;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
|
@ -44,6 +44,7 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
|
|||
int err;
|
||||
snd_config_t *conf, *type_conf = NULL;
|
||||
snd_config_iterator_t i, next;
|
||||
const char *id;
|
||||
const char *lib = NULL, *open_name = NULL;
|
||||
int (*open_func)(snd_timer_query_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
|
||||
#ifndef PIC
|
||||
|
@ -62,9 +63,14 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
|
|||
SNDERR("type is not defined");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_id(conf, &id);
|
||||
if (err < 0) {
|
||||
SNDERR("unable to get id");
|
||||
return err;
|
||||
}
|
||||
err = snd_config_get_string(conf, &str);
|
||||
if (err < 0) {
|
||||
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
||||
SNDERR("Invalid type for %s", id);
|
||||
return err;
|
||||
}
|
||||
err = snd_config_search_definition(timer_root, "timer_query_type", str, &type_conf);
|
||||
|
@ -75,7 +81,9 @@ static int snd_timer_query_open_conf(snd_timer_query_t **timer,
|
|||
}
|
||||
snd_config_for_each(i, next, type_conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "lib") == 0) {
|
||||
|
|
|
@ -100,7 +100,9 @@ int _snd_timer_query_hw_open(snd_timer_query_t **timer, char *name,
|
|||
snd_config_iterator_t i, next;
|
||||
snd_config_for_each(i, next, conf) {
|
||||
snd_config_t *n = snd_config_iterator_entry(i);
|
||||
const char *id = snd_config_get_id(n);
|
||||
const char *id;
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
if (strcmp(id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(id, "type") == 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue