From c39882f6021faa3b60bc36f73e0df71b741da967 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 19 Nov 2001 08:14:21 +0000 Subject: [PATCH] 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 --- aserver/aserver.c | 4 +- doc/conf.doxygen | 220 ---------------- doc/confarg.doxygen | 56 ----- doc/conffunc.doxygen | 106 -------- doc/doxygen.cfg | 3 +- doc/pcm.doxygen | 441 -------------------------------- include/conf.h | 24 +- include/control.h | 22 +- include/global.h | 9 +- include/local.h | 2 - include/pcm.h | 35 +-- src/async.c | 25 +- src/conf.c | 431 +++++++++++++++++++++++++++++--- src/conf/pcm/center_lfe.conf | 2 +- src/conf/pcm/front.conf | 2 +- src/conf/pcm/iec958.conf | 2 +- src/conf/pcm/rear.conf | 2 +- src/conf/pcm/surround40.conf | 2 +- src/conf/pcm/surround51.conf | 2 +- src/confmisc.c | 472 ++++++++++++++++++++++++++++++----- src/control/control.c | 14 +- src/control/control_hw.c | 4 +- src/control/control_shm.c | 8 +- src/control/setup.c | 13 +- src/dlmisc.c | 17 +- src/hwdep/hwdep.c | 12 +- src/hwdep/hwdep_hw.c | 4 +- src/pcm/pcm.c | 464 +++++++++++++++++++++++++++++++++- src/pcm/pcm_adpcm.c | 4 +- src/pcm/pcm_alaw.c | 4 +- src/pcm/pcm_copy.c | 4 +- src/pcm/pcm_file.c | 4 +- src/pcm/pcm_hooks.c | 32 ++- src/pcm/pcm_hw.c | 4 +- src/pcm/pcm_linear.c | 4 +- src/pcm/pcm_meter.c | 21 +- src/pcm/pcm_mulaw.c | 4 +- src/pcm/pcm_multi.c | 25 +- src/pcm/pcm_null.c | 4 +- src/pcm/pcm_plug.c | 4 +- src/pcm/pcm_rate.c | 4 +- src/pcm/pcm_route.c | 15 +- src/pcm/pcm_share.c | 12 +- src/pcm/pcm_shm.c | 8 +- src/rawmidi/rawmidi.c | 12 +- src/rawmidi/rawmidi_hw.c | 4 +- src/seq/seq.c | 12 +- src/seq/seq_hw.c | 4 +- src/timer/timer.c | 12 +- src/timer/timer_hw.c | 4 +- src/timer/timer_query.c | 12 +- src/timer/timer_query_hw.c | 4 +- 52 files changed, 1573 insertions(+), 1042 deletions(-) delete mode 100644 doc/conf.doxygen delete mode 100644 doc/confarg.doxygen delete mode 100644 doc/conffunc.doxygen delete mode 100644 doc/pcm.doxygen diff --git a/aserver/aserver.c b/aserver/aserver.c index 6fccb8a2..bc516afa 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -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) { diff --git a/doc/conf.doxygen b/doc/conf.doxygen deleted file mode 100644 index 8c835c25..00000000 --- a/doc/conf.doxygen +++ /dev/null @@ -1,220 +0,0 @@ -/*! \page conf Configuration files - -

Configuration files are using a simple format allowing the modern -data description like nesting and array assignments.

- -\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 syntax. The global -configuration directory can be referenced using syntax. - -\code - - -\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 - - -# 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 - -*/ diff --git a/doc/confarg.doxygen b/doc/confarg.doxygen deleted file mode 100644 index baccd0db..00000000 --- a/doc/confarg.doxygen +++ /dev/null @@ -1,56 +0,0 @@ -/*! \page confarg Configuration - runtime arguments - -

The ALSA library can accept runtime arguments for some configuration -blocks. This extension is on top of the basic syntax of the configuration -files.

- -\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 - -*/ diff --git a/doc/conffunc.doxygen b/doc/conffunc.doxygen deleted file mode 100644 index 7d5dfc16..00000000 --- a/doc/conffunc.doxygen +++ /dev/null @@ -1,106 +0,0 @@ -/*! \page conffunc Configuration - runtime functions - -

The ALSA library accepts the runtime modification of configuration. -The several build-in functions are available.

- -

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. 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:

- -\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. - -*/ diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 0f79e71c..99c3cb0f 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -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 \ diff --git a/doc/pcm.doxygen b/doc/pcm.doxygen deleted file mode 100644 index 568408f7..00000000 --- a/doc/pcm.doxygen +++ /dev/null @@ -1,441 +0,0 @@ -/*! \page pcm PCM (digital audio) interface - -

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.

- -

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).

- -

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).

- -

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.

- -

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.

- -

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.

- -\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). -

-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 - */ diff --git a/include/conf.h b/include/conf.h index 74cfe113..0fc0636b 100644 --- a/include/conf.h +++ b/include/conf.h @@ -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); diff --git a/include/control.h b/include/control.h index b13ed254..0c6ccc21 100644 --- a/include/control.h +++ b/include/control.h @@ -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 */ - diff --git a/include/global.h b/include/global.h index cd72e552..f4cb2700 100644 --- a/include/global.h +++ b/include/global.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 */ - diff --git a/include/local.h b/include/local.h index ecff86ea..74f0ddc0 100644 --- a/include/local.h +++ b/include/local.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, diff --git a/include/pcm.h b/include/pcm.h index 526a0ca8..6ab6cef3 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -6,7 +6,8 @@ * \author Takashi Iwai * \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 */ - diff --git a/src/async.c b/src/async.c index aea46597..a89bbfd3 100644 --- a/src/async.c +++ b/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; } diff --git a/src/conf.c b/src/conf.c index 872001c2..4568c775 100644 --- a/src/conf.c +++ b/src/conf.c @@ -5,7 +5,7 @@ * \author Jaroslav Kysela * \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 + +

Configuration files are using a simple format allowing the modern +data description like nesting and array assignments.

+ +\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 syntax. The global +configuration directory can be referenced using syntax. + +\code + + +\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 + + +# 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 + +

The ALSA library can accept runtime arguments for some configuration +blocks. This extension is on top of the basic syntax of the configuration +files.

+ +\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 + +

The ALSA library accepts the runtime modification of configuration. +The several build-in functions are available.

+ +

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. 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:

+ +\code +func.remove_first_char { + lib "/usr/lib/libasoundextend.so" + func "extend_remove_first_char" +} +\endcode + +*/ + + #include #include #include @@ -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; diff --git a/src/conf/pcm/center_lfe.conf b/src/conf/pcm/center_lfe.conf index 7ab08f71..423f6cb4 100644 --- a/src/conf/pcm/center_lfe.conf +++ b/src/conf/pcm/center_lfe.conf @@ -38,7 +38,7 @@ pcm.!center_lfe { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.center_lfe." $DEV ":CARD=" $CARD diff --git a/src/conf/pcm/front.conf b/src/conf/pcm/front.conf index 73cad08a..74ba7f2e 100644 --- a/src/conf/pcm/front.conf +++ b/src/conf/pcm/front.conf @@ -38,7 +38,7 @@ pcm.!front { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.front." $DEV ":CARD=" $CARD diff --git a/src/conf/pcm/iec958.conf b/src/conf/pcm/iec958.conf index 85c5bc11..555c58d5 100644 --- a/src/conf/pcm/iec958.conf +++ b/src/conf/pcm/iec958.conf @@ -58,7 +58,7 @@ pcm.!iec958 { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.iec958." $DEV ":" diff --git a/src/conf/pcm/rear.conf b/src/conf/pcm/rear.conf index 85ad8ec4..33b4b90f 100644 --- a/src/conf/pcm/rear.conf +++ b/src/conf/pcm/rear.conf @@ -38,7 +38,7 @@ pcm.!rear { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.rear." $DEV ":CARD=" $CARD diff --git a/src/conf/pcm/surround40.conf b/src/conf/pcm/surround40.conf index 3368af77..a3b577aa 100644 --- a/src/conf/pcm/surround40.conf +++ b/src/conf/pcm/surround40.conf @@ -43,7 +43,7 @@ pcm.!surround40 { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.surround40." $DEV ":CARD=" $CARD diff --git a/src/conf/pcm/surround51.conf b/src/conf/pcm/surround51.conf index fc66f7a3..1347033f 100644 --- a/src/conf/pcm/surround51.conf +++ b/src/conf/pcm/surround51.conf @@ -45,7 +45,7 @@ pcm.!surround51 { strings [ "cards." { - @func card_strtype + @func card_driver card $CARD } ".pcm.surround51." $DEV ":CARD=" $CARD diff --git a/src/confmisc.c b/src/confmisc.c index efdb0713..31c96156 100644 --- a/src/confmisc.c +++ b/src/confmisc.c @@ -1,3 +1,11 @@ +/** + * \file confmisc.c + * \brief Configuration helper functions + * \author Abramo Bagnara + * \author Jaroslav Kysela + * \date 2000-2001 + * + * Configuration helper functions. /* * Miscellaneous configuration helper functions * Copyright (c) 2000 by Abramo Bagnara , @@ -20,6 +28,40 @@ * */ +/*! \page conffunc + +\section Function reference + +
    +
  • The getenv function - snd_func_getenv() - allows to obtain + an environment value. The result is string. +
  • The igetenv function - snd_func_igetenv() - allows to obtain + an environment value. The result is integer. +
  • The concat function - snd_func_concat() - merges all specified + strings. The result is string. +
  • The datadir function - snd_func_datadir() - returns the + data directory. The result is string. +
  • The refer function - snd_func_refer() - copies the refered + configuration. The result is same as the refered node. +
  • The card_driver function - snd_func_card_driver() - returns + the driver identification. The result is string. +
  • The card_id function - snd_func_card_id() - returns + the card identification. The result is string. +
  • The pcm_id function - snd_func_pcm_id() - returns + the pcm identification. The result is string. +
  • The private_string - snd_func_private_string() - returns + string using private_data node. +
  • The private_card_driver - snd_func_private_card_driver() - + returns the driver identification using private_data node. + The result is string. +
  • The private_pcm_subdevice - snd_func_private_pcm_subdevice() - + returns the PCM subdevice number using the private_data node. + The result is string. +
+ +*/ + + #include #include #include @@ -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)); - if (err >= 0) - snd_config_set_string(*dst, res); + 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; + return err; + err = snd_config_make_integer(dst, id); + if (err < 0) + return err; snd_config_set_integer(*dst, v); - err = 0; - - _end: - return err; + 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)); - if (err >= 0) - snd_config_set_string(*dst, res); + 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 +/** + * \brief Get data directory + * \param dst The destination node (result type is string) + * \param root The root source node + * \param src The source node + * \param private_data The private_data node (unused) + * \return a positive value when success otherwise a negative error number + * + * Example (result is "/usr/share/alsa" using default paths): +\code + { + @func datadir + } +\endcode + */ int snd_func_datadir(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, - snd_config_t *src, void *private_data ATTRIBUTE_UNUSED) + snd_config_t *src, snd_config_t *private_data ATTRIBUTE_UNUSED) { - int err = snd_config_make_string(dst, snd_config_get_id(src)); + int err; + const char *id; + + err = snd_config_get_id(src, &id); + if (err < 0) + return err; + err = snd_config_make_string(dst, id); if (err >= 0) 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 >= 0) - err = snd_config_set_string(*dst, driver); + } + 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 + { + @func card_driver + card 0 + } +\endcode + */ +int snd_func_card_driver(snd_config_t **dst, snd_config_t *root, snd_config_t *src, + snd_config_t *private_data) { - snd_config_t *n; + snd_config_t *n, *val; char *str; 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)); - if (err >= 0) - err = snd_config_set_string(*dst, res); + 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)); - if (err >= 0) - err = snd_config_set_string(*dst, snd_pcm_info_get_id(info)); + 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)); - if (err >= 0) - err = snd_config_set_integer(*dst, snd_pcm_info_get_subdevice(info)); - free(res); + 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)); + } 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); + snd_input_close(input); + if (err < 0) goto _end; - } } err = snd_config_search_definition(root, NULL, name, dst); - if (err >= 0) - err = snd_config_set_id(*dst, snd_config_get_id(src)); - else + if (err >= 0) { + const char *id; + err = snd_config_get_id(src, &id); + if (err >= 0) + err = snd_config_set_id(*dst, id); + } else SNDERR("Unable to find definition '%s'", name); _end: return err; } +#ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(snd_func_refer, SND_CONFIG_DLSYM_VERSION_EVALUATE); +#endif diff --git a/src/control/control.c b/src/control/control.c index 029a860f..5a04d1d0 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -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) { diff --git a/src/control/control_hw.c b/src/control/control_hw.c index e9b147c5..7cf62a2c 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -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) diff --git a/src/control/control_shm.c b/src/control/control_shm.c index 24b416e6..99df087b 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -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) { diff --git a/src/control/setup.c b/src/control/setup.c index f2d0d778..88b46f60 100644 --- a/src/control/setup.c +++ b/src/control/setup.c @@ -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; diff --git a/src/dlmisc.c b/src/dlmisc.c index 4fd497b0..138ccc01 100644 --- a/src/dlmisc.c +++ b/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)) diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index 5180de95..23dbf6a9 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -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) { diff --git a/src/hwdep/hwdep_hw.c b/src/hwdep/hwdep_hw.c index 63ea4d6d..b9f4625d 100644 --- a/src/hwdep/hwdep_hw.c +++ b/src/hwdep/hwdep_hw.c @@ -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) diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 615660ea..4e397e1a 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1,5 +1,6 @@ /** * \file pcm/pcm.c + * \ingroup PCM * \brief PCM Interface * \author Jaroslav Kysela * \author Abramo Bagnara @@ -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 + +

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.

+ +

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).

+ +

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).

+ +

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.

+ +

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.

+ +

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.

+ +\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). +

+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 #include #include @@ -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) { diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index d21b263d..cc7d9973 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -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) { diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index d11df70d..aa61e76e 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -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) { diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c index 3e02f03c..1e9287ed 100644 --- a/src/pcm/pcm_copy.c +++ b/src/pcm/pcm_copy.c @@ -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) { diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c index 525f35de..b65b7f1d 100644 --- a/src/pcm/pcm_file.c +++ b/src/pcm/pcm_file.c @@ -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) { diff --git a/src/pcm/pcm_hooks.c b/src/pcm/pcm_hooks.c index 59a625bd..65cc3e96 100644 --- a/src/pcm/pcm_hooks.c +++ b/src/pcm/pcm_hooks.c @@ -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; } diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 9bdaf5ab..023b6c1d 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -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) { diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index ca33a48c..10147aab 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -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) { diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c index bde9bb63..363b8451 100644 --- a/src/pcm/pcm_meter.c +++ b/src/pcm/pcm_meter.c @@ -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) { diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index 461a64a3..d951c5cf 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -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) { diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 5c6fe74b..0a9de427 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -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; } diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c index cac6f189..4e30cab4 100644 --- a/src/pcm/pcm_null.c +++ b/src/pcm/pcm_null.c @@ -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); diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index d00e91db..db002222 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -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) { diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index ed65dccb..b7cd5012 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -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) { diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index debcc8ca..769079d4 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -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) { diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 9b1d28ba..3ea7dde9 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -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) { diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index 1851a02a..cb6b0cfb 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -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) { diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 4cf1efa8..1d136d20 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -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) { diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index 07011d43..97eb51f5 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -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) diff --git a/src/seq/seq.c b/src/seq/seq.c index da51aa10..da04532e 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -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) { diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index 38a30e69..f7d128ec 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -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) diff --git a/src/timer/timer.c b/src/timer/timer.c index 297b3cf0..24806899 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -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) { diff --git a/src/timer/timer_hw.c b/src/timer/timer_hw.c index 7921c9e7..9400e989 100644 --- a/src/timer/timer_hw.c +++ b/src/timer/timer_hw.c @@ -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) diff --git a/src/timer/timer_query.c b/src/timer/timer_query.c index fe24fa66..84559a7f 100644 --- a/src/timer/timer_query.c +++ b/src/timer/timer_query.c @@ -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) { diff --git a/src/timer/timer_query_hw.c b/src/timer/timer_query_hw.c index a7b84505..5b80c678 100644 --- a/src/timer/timer_query_hw.c +++ b/src/timer/timer_query_hw.c @@ -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) -- 2.47.1