alsa/alsa-lib-git-fixes.diff

4714 lines
158 KiB
Diff

diff --git a/configure.in b/configure.in
index 4e5e757..cb2877b 100644
--- a/configure.in
+++ b/configure.in
@@ -594,8 +594,9 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \
src/conf/cards/Makefile \
src/conf/pcm/Makefile \
modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \
- alsalisp/Makefile aserver/Makefile test/Makefile utils/Makefile \
- utils/alsa-lib.spec utils/alsa.pc)
+ alsalisp/Makefile aserver/Makefile \
+ test/Makefile test/lsb/Makefile \
+ utils/Makefile utils/alsa-lib.spec utils/alsa.pc)
dnl Create asoundlib.h dynamically according to configure options
echo "Creating asoundlib.h..."
diff --git a/doc/Makefile.am b/doc/Makefile.am
index a5896a7..2cc250b 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -5,7 +5,7 @@ EXTRA_DIST=README.1st asoundrc.txt doxygen.cfg index.doxygen
INCLUDES=-I$(top_srcdir)/include
doc:
- test -e doxygen.cfg || sed s:@top_srcdir@:..:g doxygen.cfg.in > doxygen.cfg
+ test -e doxygen.cfg || sed s:[@]top_srcdir[@]:..:g doxygen.cfg.in > doxygen.cfg
doxygen doxygen.cfg
doc-pack: doc
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 4f682cf..8606c48 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -114,6 +114,7 @@ PREDEFINED = DOXYGEN PIC "DOC_HIDDEN" \
"link_warning(x,y)="
OPTIMIZE_OUTPUT_FOR_C = YES # doxygen 1.2.6 option
+TYPEDEF_HIDES_STRUCT = YES # needed in doxygen >= 1.5.4
#INPUT_FILTER = inputfilter
#FILTER_SOURCE_FILES = YES
diff --git a/include/conf.h b/include/conf.h
index 7c0cab0..ff270f6 100644
--- a/include/conf.h
+++ b/include/conf.h
@@ -44,11 +44,11 @@ extern "C" {
/** \brief \c dlsym version for the config hook callback. */
#define SND_CONFIG_DLSYM_VERSION_HOOK _dlsym_config_hook_001
-/** Configuration node type. */
+/** \brief Configuration node type. */
typedef enum _snd_config_type {
/** Integer number. */
SND_CONFIG_TYPE_INTEGER,
- /** 64 bit Integer number. */
+ /** 64-bit integer number. */
SND_CONFIG_TYPE_INTEGER64,
/** Real number. */
SND_CONFIG_TYPE_REAL,
@@ -154,11 +154,20 @@ snd_config_t *snd_config_iterator_entry(const snd_config_iterator_t iterator);
/**
* \brief Helper macro to iterate over the children of a compound node.
- * \param pos Iterator variable for the current node.
- * \param next Iterator variable for the next node.
- * \param node Handle to the compound configuration node to iterate over.
+ * \param[in,out] pos Iterator variable for the current node.
+ * \param[in,out] next Temporary iterator variable for the next node.
+ * \param[in] node Handle to the compound configuration node to iterate over.
*
- * This macro is designed to permit the removal of the current node.
+ * Use this macro like a \c for statement, e.g.:
+ * \code
+ * snd_config_iterator_t pos, next;
+ * snd_config_for_each(pos, next, node) {
+ * snd_config_t *entry = snd_config_iterator_entry(pos);
+ * ...
+ * }
+ * \endcode
+ *
+ * This macro allows deleting or removing the current node.
*/
#define snd_config_for_each(pos, next, node) \
for (pos = snd_config_iterator_first(node), next = snd_config_iterator_next(pos); pos != snd_config_iterator_end(node); pos = next, next = snd_config_iterator_next(pos))
diff --git a/include/control.h b/include/control.h
index 2361dc3..29ea397 100644
--- a/include/control.h
+++ b/include/control.h
@@ -174,6 +174,10 @@ typedef enum _snd_ctl_event_type {
#define SND_CTL_TLVT_DB_LINEAR 0x0002
/** TLV type - dB range container */
#define SND_CTL_TLVT_DB_RANGE 0x0003
+/** TLV type - dB scale specified by min/max values */
+#define SND_CTL_TLVT_DB_MINMAX 0x0004
+/** TLV type - dB scale specified by min/max values (with mute) */
+#define SND_CTL_TLVT_DB_MINMAX_MUTE 0x0005
/** Mute state */
#define SND_CTL_TLV_DB_GAIN_MUTE -9999999
diff --git a/include/mixer.h b/include/mixer.h
index df92164..58256a6 100644
--- a/include/mixer.h
+++ b/include/mixer.h
@@ -124,8 +124,6 @@ void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *obj, void * val);
snd_mixer_elem_type_t snd_mixer_elem_get_type(const snd_mixer_elem_t *obj);
int snd_mixer_class_register(snd_mixer_class_t *class_, snd_mixer_t *mixer);
-int snd_mixer_add_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem);
-int snd_mixer_remove_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem);
int snd_mixer_elem_new(snd_mixer_elem_t **elem,
snd_mixer_elem_type_t type,
int compare_weight,
diff --git a/include/pcm.h b/include/pcm.h
index 15e9cb2..f3618c3 100644
--- a/include/pcm.h
+++ b/include/pcm.h
@@ -526,8 +526,6 @@ int snd_pcm_hw_params_is_batch(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_is_block_transfer(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_is_monotonic(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_can_overrange(const snd_pcm_hw_params_t *params);
-int snd_pcm_hw_params_can_forward(const snd_pcm_hw_params_t *params);
-int snd_pcm_hw_params_can_rewind(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_can_pause(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_can_resume(const snd_pcm_hw_params_t *params);
int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params);
@@ -1022,7 +1020,8 @@ snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm);
int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope);
snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name);
int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr);
-void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val);
+void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope,
+ const snd_pcm_scope_ops_t *val);
void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val);
const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope);
void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope);
diff --git a/include/pcm_ioplug.h b/include/pcm_ioplug.h
index b5968f1..6331cf0 100644
--- a/include/pcm_ioplug.h
+++ b/include/pcm_ioplug.h
@@ -55,11 +55,11 @@ typedef struct snd_pcm_ioplug snd_pcm_ioplug_t;
/** Callback table of ioplug */
typedef struct snd_pcm_ioplug_callback snd_pcm_ioplug_callback_t;
-/**
+/*
* bit flags for additional conditions
*/
-#define SND_PCM_IOPLUG_FLAG_LISTED (1<<0) /* list up this PCM */
-#define SND_PCM_IOPLUG_FLAG_MONOTONIC (1<<1) /* monotonic timestamps */
+#define SND_PCM_IOPLUG_FLAG_LISTED (1<<0) /**< list up this PCM */
+#define SND_PCM_IOPLUG_FLAG_MONOTONIC (1<<1) /**< monotonic timestamps */
/*
* Protocol version
diff --git a/include/pcm_rate.h b/include/pcm_rate.h
index d211e09..4d70df2 100644
--- a/include/pcm_rate.h
+++ b/include/pcm_rate.h
@@ -38,7 +38,7 @@ extern "C" {
/**
* Protocol version
*/
-#define SND_PCM_RATE_PLUGIN_VERSION 0x010001
+#define SND_PCM_RATE_PLUGIN_VERSION 0x010002
/** hw_params information for a single side */
typedef struct snd_pcm_rate_side_info {
@@ -98,6 +98,22 @@ typedef struct snd_pcm_rate_ops {
* compute the frame size for output
*/
snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+ /**
+ * the protocol version the plugin supports;
+ * new field since version 0x010002
+ */
+ unsigned int version;
+ /**
+ * return the supported min / max sample rates;
+ * new ops since version 0x010002
+ */
+ int (*get_supported_rates)(void *obj, unsigned int *rate_min,
+ unsigned int *rate_max);
+ /**
+ * show some status messages for verbose mode;
+ * new ops since version 0x010002
+ */
+ void (*dump)(void *obj, snd_output_t *out);
} snd_pcm_rate_ops_t;
/** open function type */
@@ -110,6 +126,26 @@ typedef int (*snd_pcm_rate_open_func_t)(unsigned int version, void **objp,
#define SND_PCM_RATE_PLUGIN_ENTRY(name) _snd_pcm_rate_##name##_open
+#ifndef DOC_HIDDEN
+/* old rate_ops for protocol version 0x010001 */
+typedef struct snd_pcm_rate_old_ops {
+ void (*close)(void *obj);
+ int (*init)(void *obj, snd_pcm_rate_info_t *info);
+ void (*free)(void *obj);
+ void (*reset)(void *obj);
+ int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info);
+ void (*convert)(void *obj,
+ const snd_pcm_channel_area_t *dst_areas,
+ snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
+ const snd_pcm_channel_area_t *src_areas,
+ snd_pcm_uframes_t src_offset, unsigned int src_frames);
+ void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames,
+ const int16_t *src, unsigned int src_frames);
+ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames);
+ snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+} snd_pcm_rate_old_ops_t;
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/conf.c b/src/conf.c
index c86f819..3f0dfe1 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -440,7 +440,7 @@ struct _snd_config {
} compound;
} u;
struct list_head list;
- snd_config_t *father;
+ snd_config_t *parent;
int hop;
};
@@ -879,16 +879,16 @@ static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t
static int _snd_config_make_add(snd_config_t **config, char **id,
- snd_config_type_t type, snd_config_t *father)
+ snd_config_type_t type, snd_config_t *parent)
{
snd_config_t *n;
int err;
- assert(father->type == SND_CONFIG_TYPE_COMPOUND);
+ assert(parent->type == SND_CONFIG_TYPE_COMPOUND);
err = _snd_config_make(&n, id, type);
if (err < 0)
return err;
- n->father = father;
- list_add_tail(&n->list, &father->u.compound.fields);
+ n->parent = parent;
+ list_add_tail(&n->list, &parent->u.compound.fields);
*config = n;
return 0;
}
@@ -912,7 +912,7 @@ static int _snd_config_search(snd_config_t *config,
return -ENOENT;
}
-static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input, char **id, int skip)
+static int parse_value(snd_config_t **_n, snd_config_t *parent, input_t *input, char **id, int skip)
{
snd_config_t *n = *_n;
char *s;
@@ -940,7 +940,7 @@ static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input,
return -EINVAL;
}
} else {
- err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, father);
+ err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_REAL, parent);
if (err < 0)
return err;
}
@@ -957,9 +957,9 @@ static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input,
}
} else {
if (i <= INT_MAX)
- err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, father);
+ err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER, parent);
else
- err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER64, father);
+ err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_INTEGER64, parent);
if (err < 0)
return err;
}
@@ -978,7 +978,7 @@ static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input,
return -EINVAL;
}
} else {
- err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, father);
+ err = _snd_config_make_add(&n, id, SND_CONFIG_TYPE_STRING, parent);
if (err < 0)
return err;
}
@@ -988,10 +988,10 @@ static int parse_value(snd_config_t **_n, snd_config_t *father, input_t *input,
return 0;
}
-static int parse_defs(snd_config_t *father, input_t *input, int skip, int override);
+static int parse_defs(snd_config_t *parent, input_t *input, int skip, int override);
static int parse_array_defs(snd_config_t *farther, input_t *input, int skip, int override);
-static int parse_array_def(snd_config_t *father, input_t *input, int idx, int skip, int override)
+static int parse_array_def(snd_config_t *parent, input_t *input, int idx, int skip, int override)
{
char *id = NULL;
int c;
@@ -1023,7 +1023,7 @@ static int parse_array_def(snd_config_t *father, input_t *input, int idx, int sk
goto __end;
}
} else {
- err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
+ err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, parent);
if (err < 0)
goto __end;
}
@@ -1050,7 +1050,7 @@ static int parse_array_def(snd_config_t *father, input_t *input, int idx, int sk
}
default:
unget_char(c, input);
- err = parse_value(&n, father, input, &id, skip);
+ err = parse_value(&n, parent, input, &id, skip);
if (err < 0)
goto __end;
break;
@@ -1061,7 +1061,7 @@ static int parse_array_def(snd_config_t *father, input_t *input, int idx, int sk
return err;
}
-static int parse_array_defs(snd_config_t *father, input_t *input, int skip, int override)
+static int parse_array_defs(snd_config_t *parent, input_t *input, int skip, int override)
{
int idx = 0;
while (1) {
@@ -1071,14 +1071,14 @@ static int parse_array_defs(snd_config_t *father, input_t *input, int skip, int
unget_char(c, input);
if (c == ']')
return 0;
- err = parse_array_def(father, input, idx++, skip, override);
+ err = parse_array_def(parent, input, idx++, skip, override);
if (err < 0)
return err;
}
return 0;
}
-static int parse_def(snd_config_t *father, input_t *input, int skip, int override)
+static int parse_def(snd_config_t *parent, input_t *input, int skip, int override)
{
char *id = NULL;
int c;
@@ -1116,7 +1116,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
free(id);
continue;
}
- if (_snd_config_search(father, id, -1, &n) == 0) {
+ if (_snd_config_search(parent, id, -1, &n) == 0) {
if (mode == DONT_OVERRIDE) {
skip = 1;
free(id);
@@ -1128,7 +1128,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
return -EINVAL;
}
n->u.compound.join = 1;
- father = n;
+ parent = n;
free(id);
continue;
}
@@ -1139,11 +1139,11 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
err = -ENOENT;
goto __end;
}
- err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
+ err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, parent);
if (err < 0)
goto __end;
n->u.compound.join = 1;
- father = n;
+ parent = n;
}
if (c == '=') {
c = get_nonwhite(input);
@@ -1151,7 +1151,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
return c;
}
if (!skip) {
- if (_snd_config_search(father, id, -1, &n) == 0) {
+ if (_snd_config_search(parent, id, -1, &n) == 0) {
if (mode == DONT_OVERRIDE) {
skip = 1;
n = NULL;
@@ -1181,7 +1181,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
goto __end;
}
} else {
- err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, father);
+ err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, parent);
if (err < 0)
goto __end;
}
@@ -1204,7 +1204,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
}
default:
unget_char(c, input);
- err = parse_value(&n, father, input, &id, skip);
+ err = parse_value(&n, parent, input, &id, skip);
if (err < 0)
goto __end;
break;
@@ -1222,7 +1222,7 @@ static int parse_def(snd_config_t *father, input_t *input, int skip, int overrid
return err;
}
-static int parse_defs(snd_config_t *father, input_t *input, int skip, int override)
+static int parse_defs(snd_config_t *parent, input_t *input, int skip, int override)
{
int c, err;
while (1) {
@@ -1232,7 +1232,7 @@ static int parse_defs(snd_config_t *father, input_t *input, int skip, int overri
unget_char(c, input);
if (c == '}')
return 0;
- err = parse_def(father, input, skip, override);
+ err = parse_def(parent, input, skip, override);
if (err < 0)
return err;
}
@@ -1242,20 +1242,17 @@ static int parse_defs(snd_config_t *father, input_t *input, int skip, int overri
static void string_print(char *str, int id, snd_output_t *out)
{
unsigned char *p = (unsigned char *)str;
+ if (!p || !*p) {
+ snd_output_puts(out, "''");
+ return;
+ }
if (!id) {
switch (*p) {
- case 0:
- assert(0);
- break;
case '0' ... '9':
case '-':
goto quoted;
}
}
- if (!*p) {
- snd_output_puts(out, "''");
- return;
- }
loop:
switch (*p) {
case 0:
@@ -1327,10 +1324,11 @@ static void string_print(char *str, int id, snd_output_t *out)
snd_output_putc(out, '\'');
}
-static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins);
+static int _snd_config_save_children(snd_config_t *config, snd_output_t *out,
+ unsigned int level, unsigned int joins);
-static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
- unsigned int level)
+static int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out,
+ unsigned int level)
{
int err;
unsigned int k;
@@ -1353,7 +1351,7 @@ static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
case SND_CONFIG_TYPE_COMPOUND:
snd_output_putc(out, '{');
snd_output_putc(out, '\n');
- err = _snd_config_save_leaves(n, out, level + 1, 0);
+ err = _snd_config_save_children(n, out, level + 1, 0);
if (err < 0)
return err;
for (k = 0; k < level; ++k) {
@@ -1368,14 +1366,15 @@ static int _snd_config_save_leaf(snd_config_t *n, snd_output_t *out,
static void id_print(snd_config_t *n, snd_output_t *out, unsigned int joins)
{
if (joins > 0) {
- assert(n->father);
- id_print(n->father, out, joins - 1);
+ assert(n->parent);
+ id_print(n->parent, out, joins - 1);
snd_output_putc(out, '.');
}
string_print(n->id, 1, out);
}
-static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsigned int level, unsigned int joins)
+static int _snd_config_save_children(snd_config_t *config, snd_output_t *out,
+ unsigned int level, unsigned int joins)
{
unsigned int k;
int err;
@@ -1385,7 +1384,7 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
snd_config_t *n = snd_config_iterator_entry(i);
if (n->type == SND_CONFIG_TYPE_COMPOUND &&
n->u.compound.join) {
- err = _snd_config_save_leaves(n, out, level, joins + 1);
+ err = _snd_config_save_children(n, out, level, joins + 1);
if (err < 0)
return err;
continue;
@@ -1399,7 +1398,7 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
snd_output_putc(out, '=');
#endif
snd_output_putc(out, ' ');
- err = _snd_config_save_leaf(n, out, level);
+ err = _snd_config_save_node_value(n, out, level);
if (err < 0)
return err;
#if 0
@@ -1415,7 +1414,7 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
/**
* \brief Substitutes one configuration node to another.
* \param dst Handle to the destination node.
- * \param src Handle to the source node. Must not be the same as \p dst.
+ * \param src Handle to the source node. Must not be the same as \a dst.
* \return Zero if successful, otherwise a negative error code.
*
* If both nodes are compounds, the source compound node members are
@@ -1425,7 +1424,11 @@ static int _snd_config_save_leaves(snd_config_t *config, snd_output_t *out, unsi
* an ordinary type, the compound members are deleted (including
* their contents).
*
- * A successful call to this function invalidates the source node.
+ * Otherwise, the source node's value replaces the destination node's
+ * value.
+ *
+ * In any case, a successful call to this function frees the source
+ * node.
*/
int snd_config_substitute(snd_config_t *dst, snd_config_t *src)
{
@@ -1435,7 +1438,7 @@ int snd_config_substitute(snd_config_t *dst, snd_config_t *src)
snd_config_iterator_t i, next;
snd_config_for_each(i, next, src) {
snd_config_t *n = snd_config_iterator_entry(i);
- n->father = dst;
+ n->parent = dst;
}
src->u.compound.fields.next->prev = &dst->u.compound.fields;
src->u.compound.fields.prev->next = &dst->u.compound.fields;
@@ -1455,10 +1458,23 @@ int snd_config_substitute(snd_config_t *dst, snd_config_t *src)
/**
* \brief Converts an ASCII string to a configuration node type.
- * \param ascii A string containing a configuration node type.
- * \param type The function puts the node type at the address specified
- * by \p type.
- * \return Zero if successgul, otherwise a negative error code.
+ * \param[in] ascii A string containing a configuration node type.
+ * \param[out] type The node type corresponding to \a ascii.
+ * \return Zero if successful, otherwise a negative error code.
+ *
+ * This function recognizes at least the following node types:
+ * <dl>
+ * <dt>integer<dt>#SND_CONFIG_TYPE_INTEGER
+ * <dt>integer64<dt>#SND_CONFIG_TYPE_INTEGER64
+ * <dt>real<dt>#SND_CONFIG_TYPE_REAL
+ * <dt>string<dt>#SND_CONFIG_TYPE_STRING
+ * <dt>compound<dt>#SND_CONFIG_TYPE_COMPOUND
+ * </dl>
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>Unknown note type in \a type.
+ * </dl>
*/
int snd_config_get_type_ascii(const char *ascii, snd_config_type_t *type)
{
@@ -1490,6 +1506,9 @@ int snd_config_get_type_ascii(const char *ascii, snd_config_type_t *type)
* \brief Returns the type of a configuration node.
* \param config Handle to the configuration node.
* \return The node's type.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
snd_config_type_t snd_config_get_type(const snd_config_t *config)
{
@@ -1498,13 +1517,19 @@ snd_config_type_t snd_config_get_type(const snd_config_t *config)
/**
* \brief Returns the id of a configuration node.
- * \param config Handle to the configuration node.
- * \param id The function puts the pointer to the id string at the address
- * specified by \p id.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] id The function puts the pointer to the id string at the
+ * address specified by \a id.
* \return Zero if successful, otherwise a negative error code.
*
* The returned string is owned by the configuration node; the application
- * must not modify or delete it.
+ * must not modify or delete it, and the string becomes invalid when the
+ * node's id changes or when the node is freed.
+ *
+ * If the node does not have an id, \a *id is set to \c NULL.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_get_id(const snd_config_t *config, const char **id)
{
@@ -1516,16 +1541,39 @@ int snd_config_get_id(const snd_config_t *config, const char **id)
/**
* \brief Sets the id of a configuration node.
* \param config Handle to the configuration node.
- * \param id The new node id.
+ * \param id The new node id, must not be \c NULL.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function stores a copy of \a id in the node.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EEXIST<dd>One of \a config's siblings already has the id \a id.
+ * <dt>-EINVAL<dd>The id of a node with a parent cannot be set to \c NULL.
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
*/
int snd_config_set_id(snd_config_t *config, const char *id)
{
+ snd_config_iterator_t i, next;
char *new_id;
- assert(config && id);
- new_id = strdup(id);
- if (!new_id)
- return -ENOMEM;
+ assert(config);
+ if (id) {
+ if (config->parent) {
+ snd_config_for_each(i, next, config->parent) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ if (n != config && strcmp(id, n->id) == 0)
+ return -EEXIST;
+ }
+ }
+ new_id = strdup(id);
+ if (!new_id)
+ return -ENOMEM;
+ } else {
+ if (config->parent)
+ return -EINVAL;
+ new_id = NULL;
+ }
free(config->id);
config->id = new_id;
return 0;
@@ -1533,11 +1581,19 @@ int snd_config_set_id(snd_config_t *config, const char *id)
/**
* \brief Creates a top level configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
+ * \param[out] config Handle to the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The returned node is a compound node.
+ * The returned node is an empty compound node without a parent and
+ * without an id.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_top(snd_config_t **config)
{
@@ -1611,6 +1667,16 @@ static int snd_config_load1(snd_config_t *config, snd_input_t *in, int override)
* \param config Handle to a top level configuration node.
* \param in Input handle to read the configuration from.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * The definitions loaded from the input are added to \a config, which
+ * must be a compound node.
+ *
+ * \par Errors:
+ * Any errors encountered when parsing the input or returned by hooks or
+ * functions.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_load(snd_config_t *config, snd_input_t *in)
{
@@ -1622,6 +1688,10 @@ int snd_config_load(snd_config_t *config, snd_input_t *in)
* \param config Handle to a top level configuration node.
* \param in Input handle to read the configuration from.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function loads definitions from \a in into \a config like
+ * #snd_config_load, but the default mode for input nodes is 'override'
+ * (!) instead of 'merge+create' (+).
*/
int snd_config_load_override(snd_config_t *config, snd_input_t *in)
{
@@ -1630,21 +1700,41 @@ int snd_config_load_override(snd_config_t *config, snd_input_t *in)
/**
* \brief Adds a child to a compound configuration node.
- * \param father Handle to the compound configuration node.
- * \param leaf Handle to the configuration node to be added to \p father.
+ * \param parent Handle to a compound configuration node.
+ * \param child Handle to the configuration node to be added.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function makes the node \a child a child of the node \a parent.
+ *
+ * The parent node then owns the child node, i.e., the child node gets
+ * deleted together with its parent.
+ *
+ * \a child must have an id.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a child does not have an id.
+ * <dt>-EINVAL<dd>\a child already has a parent.
+ * <dt>-EEXIST<dd>\a parent already contains a child node with the same
+ * id as \a child.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
-int snd_config_add(snd_config_t *father, snd_config_t *leaf)
+int snd_config_add(snd_config_t *parent, snd_config_t *child)
{
snd_config_iterator_t i, next;
- assert(father && leaf);
- snd_config_for_each(i, next, father) {
+ assert(parent && child);
+ if (!child->id || child->parent)
+ return -EINVAL;
+ snd_config_for_each(i, next, parent) {
snd_config_t *n = snd_config_iterator_entry(i);
- if (strcmp(leaf->id, n->id) == 0)
+ if (strcmp(child->id, n->id) == 0)
return -EEXIST;
}
- leaf->father = father;
- list_add_tail(&leaf->list, &father->u.compound.fields);
+ child->parent = parent;
+ list_add_tail(&child->list, &parent->u.compound.fields);
return 0;
}
@@ -1653,25 +1743,40 @@ int snd_config_add(snd_config_t *father, snd_config_t *leaf)
* \param config Handle to the configuration node to be removed.
* \return Zero if successful, otherwise a negative error code.
*
- * This functions does \e not delete the removed node.
+ * This function makes \a config a top-level node, i.e., if \a config
+ * has a parent, then \a config is removed from the list of the parent's
+ * children.
+ *
+ * This functions does \e not free the removed node.
+ *
+ * \sa snd_config_delete
*/
int snd_config_remove(snd_config_t *config)
{
assert(config);
- if (config->father)
+ if (config->parent)
list_del(&config->list);
- config->father = NULL;
+ config->parent = NULL;
return 0;
}
/**
- * \brief Deletes a configuration node (freeing all its related resources).
+ * \brief Frees a configuration node.
* \param config Handle to the configuration node to be deleted.
* \return Zero if successful, otherwise a negative error code.
*
+ * This function frees a configuration node and all its resources.
+ *
* If the node is a child node, it is removed from the tree before being
- * deleted. If the node is a compound node, all children are deleted
- * recursively.
+ * deleted.
+ *
+ * If the node is a compound node, its descendants (the whole subtree)
+ * are deleted recursively.
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_config_remove
*/
int snd_config_delete(snd_config_t *config)
{
@@ -1684,8 +1789,8 @@ int snd_config_delete(snd_config_t *config)
i = config->u.compound.fields.next;
while (i != &config->u.compound.fields) {
struct list_head *nexti = i->next;
- snd_config_t *leaf = snd_config_iterator_entry(i);
- err = snd_config_delete(leaf);
+ snd_config_t *child = snd_config_iterator_entry(i);
+ err = snd_config_delete(child);
if (err < 0)
return err;
i = nexti;
@@ -1698,7 +1803,7 @@ int snd_config_delete(snd_config_t *config)
default:
break;
}
- if (config->father)
+ if (config->parent)
list_del(&config->list);
free(config->id);
free(config);
@@ -1706,11 +1811,22 @@ int snd_config_delete(snd_config_t *config)
}
/**
- * \brief Deletes the children of a compound configuration node (freeing all its related resources)
+ * \brief Deletes the children of a node.
* \param config Handle to the compound configuration node.
* \return Zero if successful, otherwise a negative error code.
*
- * Any compound nodes among the children of \p config are deleted recursively.
+ * This function removes and frees all children of a configuration node.
+ *
+ * Any compound nodes among the children of \a config are deleted
+ * recursively.
+ *
+ * After a successful call to this function, \a config is an empty
+ * compound node.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a compound node.
+ * </dl>
*/
int snd_config_delete_compound_members(const snd_config_t *config)
{
@@ -1723,8 +1839,8 @@ int snd_config_delete_compound_members(const snd_config_t *config)
i = config->u.compound.fields.next;
while (i != &config->u.compound.fields) {
struct list_head *nexti = i->next;
- snd_config_t *leaf = snd_config_iterator_entry(i);
- err = snd_config_delete(leaf);
+ snd_config_t *child = snd_config_iterator_entry(i);
+ err = snd_config_delete(child);
if (err < 0)
return err;
i = nexti;
@@ -1734,11 +1850,22 @@ int snd_config_delete_compound_members(const snd_config_t *config)
/**
* \brief Creates a configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param type The type of the new node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] type The type of the new node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This functions creates a new node of the specified type.
+ * The new node has id \a id, which may be \c NULL.
+ *
+ * The value of the new node is zero (for numbers), or \c NULL (for
+ * strings and pointers), or empty (for compound nodes).
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
*/
int snd_config_make(snd_config_t **config, const char *id,
snd_config_type_t type)
@@ -1756,12 +1883,23 @@ int snd_config_make(snd_config_t **config, const char *id,
/**
* \brief Creates an integer configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The value of the new node is 0.
+ * This function creates a new node of type #SND_CONFIG_TYPE_INTEGER and
+ * with value \c 0.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_config_imake_integer
*/
int snd_config_make_integer(snd_config_t **config, const char *id)
{
@@ -1769,13 +1907,24 @@ int snd_config_make_integer(snd_config_t **config, const char *id)
}
/**
- * \brief Creates an integer64 configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
+ * \brief Creates a 64-bit-integer configuration node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The value of the new node is 0.
+ * This function creates a new node of type #SND_CONFIG_TYPE_INTEGER64
+ * and with value \c 0.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_config_imake_integer64
*/
int snd_config_make_integer64(snd_config_t **config, const char *id)
{
@@ -1783,13 +1932,21 @@ int snd_config_make_integer64(snd_config_t **config, const char *id)
}
/**
- * \brief Creates a real configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
+ * \brief Creates a real number configuration node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The value of the new node is 0.0.
+ * This function creates a new node of type #SND_CONFIG_TYPE_REAL and
+ * with value \c 0.0.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \sa snd_config_imake_real
*/
int snd_config_make_real(snd_config_t **config, const char *id)
{
@@ -1798,12 +1955,23 @@ int snd_config_make_real(snd_config_t **config, const char *id)
/**
* \brief Creates a string configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The value of the new node is \c NULL.
+ * This function creates a new node of type #SND_CONFIG_TYPE_STRING and
+ * with value \c NULL.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_config_imake_string
*/
int snd_config_make_string(snd_config_t **config, const char *id)
{
@@ -1812,12 +1980,20 @@ int snd_config_make_string(snd_config_t **config, const char *id)
/**
* \brief Creates a pointer configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
* \return Zero if successful, otherwise a negative error code.
*
- * The value of the new node is \c NULL.
+ * This function creates a new node of type #SND_CONFIG_TYPE_POINTER and
+ * with value \c NULL.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \sa snd_config_imake_pointer
*/
int snd_config_make_pointer(snd_config_t **config, const char *id)
{
@@ -1826,12 +2002,41 @@ int snd_config_make_pointer(snd_config_t **config, const char *id)
/**
* \brief Creates an empty compound configuration node.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param join Join flag.
- * This is checked in #snd_config_save to change look. (Huh?)
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] join Join flag.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new empty node of type
+ * #SND_CONFIG_TYPE_COMPOUND.
+ *
+ * \a join determines how the compound node's id is printed when the
+ * configuration is saved to a text file. For example, if the join flag
+ * of compound node \c a is zero, the output will look as follows:
+ * \code
+ * a {
+ * b "hello"
+ * c 42
+ * }
+ * \endcode
+ * If, however, the join flag of \c a is nonzero, its id will be joined
+ * with its children's ids, like this:
+ * \code
+ * a.b "hello"
+ * a.c 42
+ * \endcode
+ * An \e empty compound node with its join flag set would result in no
+ * output, i.e., after saving and reloading the configuration file, that
+ * compound node would be lost.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_make_compound(snd_config_t **config, const char *id,
int join)
@@ -1846,11 +2051,22 @@ int snd_config_make_compound(snd_config_t **config, const char *id,
/**
* \brief Creates an integer configuration node with the given initial value.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param value The initial value of the new node.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new node of type #SND_CONFIG_TYPE_INTEGER and
+ * with value \a value.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_imake_integer(snd_config_t **config, const char *id, const long value)
{
@@ -1864,12 +2080,23 @@ int snd_config_imake_integer(snd_config_t **config, const char *id, const long v
}
/**
- * \brief Creates an integer configuration node with the given initial value.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param value The initial value of the new node.
+ * \brief Creates a 64-bit-integer configuration node with the given initial value.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new node of type #SND_CONFIG_TYPE_INTEGER64
+ * and with value \a value.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_imake_integer64(snd_config_t **config, const char *id, const long long value)
{
@@ -1883,12 +2110,20 @@ int snd_config_imake_integer64(snd_config_t **config, const char *id, const long
}
/**
- * \brief Creates a real configuration node with the given initial value.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param value The initial value of the new node.
+ * \brief Creates a real number configuration node with the given initial value.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new node of type #SND_CONFIG_TYPE_REAL and
+ * with value \a value.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
*/
int snd_config_imake_real(snd_config_t **config, const char *id, const double value)
{
@@ -1903,13 +2138,22 @@ int snd_config_imake_real(snd_config_t **config, const char *id, const double va
/**
* \brief Creates a string configuration node with the given initial value.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param value The initial value of the new node. May be \c NULL.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node. May be \c NULL.
* \return Zero if successful, otherwise a negative error code.
*
- * This function creates the new node with its own copy of the passed string.
+ * This function creates a new node of type #SND_CONFIG_TYPE_STRING and
+ * with a copy of the string \c value.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_imake_string(snd_config_t **config, const char *id, const char *value)
{
@@ -1934,11 +2178,19 @@ int snd_config_imake_string(snd_config_t **config, const char *id, const char *v
/**
* \brief Creates a pointer configuration node with the given initial value.
- * \param config The function puts the handle to the new node at the address
- * specified by \p config.
- * \param id The id of the new node.
- * \param value The initial value of the new node. May be \c NULL.
+ * \param[out] config The function puts the handle to the new node at
+ * the address specified by \a config.
+ * \param[in] id The id of the new node.
+ * \param[in] value The initial value of the new node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function creates a new node of type #SND_CONFIG_TYPE_POINTER and
+ * with value \c value.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
*/
int snd_config_imake_pointer(snd_config_t **config, const char *id, const void *value)
{
@@ -1956,6 +2208,14 @@ int snd_config_imake_pointer(snd_config_t **config, const char *id, const void *
* \param config Handle to the configuration node.
* \param value The new value for the node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not an integer node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_set_integer(snd_config_t *config, long value)
{
@@ -1967,10 +2227,18 @@ int snd_config_set_integer(snd_config_t *config, long value)
}
/**
- * \brief Changes the value of an integer64 configuration node.
+ * \brief Changes the value of a 64-bit-integer configuration node.
* \param config Handle to the configuration node.
* \param value The new value for the node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a 64-bit-integer node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_set_integer64(snd_config_t *config, long long value)
{
@@ -1982,10 +2250,15 @@ int snd_config_set_integer64(snd_config_t *config, long long value)
}
/**
- * \brief Changes the value of a real configuration node.
+ * \brief Changes the value of a real-number configuration node.
* \param config Handle to the configuration node.
* \param value The new value for the node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a real-number node.
+ * </dl>
*/
int snd_config_set_real(snd_config_t *config, double value)
{
@@ -1999,11 +2272,19 @@ int snd_config_set_real(snd_config_t *config, double value)
/**
* \brief Changes the value of a string configuration node.
* \param config Handle to the configuration node.
- * \param value The new value for the node. May be \c NULL.
+ * \param value The new value for the node. May be \c NULL.
* \return Zero if successful, otherwise a negative error code.
*
* This function deletes the old string in the node and stores a copy of
- * the passed string in the node.
+ * \a value string in the node.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a string node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_set_string(snd_config_t *config, const char *value)
{
@@ -2026,10 +2307,15 @@ int snd_config_set_string(snd_config_t *config, const char *value)
/**
* \brief Changes the value of a pointer configuration node.
* \param config Handle to the configuration node.
- * \param value The new value for the node. May be \c NULL.
+ * \param value The new value for the node. May be \c NULL.
* \return Zero if successful, otherwise a negative error code.
*
* This function does not free the old pointer in the node.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a pointer node.
+ * </dl>
*/
int snd_config_set_pointer(snd_config_t *config, const void *value)
{
@@ -2043,11 +2329,27 @@ int snd_config_set_pointer(snd_config_t *config, const void *value)
/**
* \brief Changes the value of a configuration node.
* \param config Handle to the configuration node.
- * \param ascii The new value for the node as an ASCII string. \p ascii must
- * not be \c NULL, not even for a string node.
+ * \param ascii The new value for the node, as an ASCII string.
* \return Zero if successful, otherwise a negative error code.
*
- * The node must have a simple type, and the new value must have the same type.
+ * This function changes the node's value to a new value that is parsed
+ * from the string \a ascii. \a ascii must not be \c NULL, not even for
+ * a string node.
+ *
+ * The node's type does not change, i.e., the string must contain a
+ * valid value with the same type as the node's type. For a string
+ * node, the node's new value is a copy of \a ascii.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a number or string node.
+ * <dt>-EINVAL<dd>The value in \a ascii cannot be parsed.
+ * <dt>-ERANGE<dd>The value in \a ascii is too big for the node's type.
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_set_ascii(snd_config_t *config, const char *ascii)
{
@@ -2097,10 +2399,17 @@ int snd_config_set_ascii(snd_config_t *config, const char *ascii)
/**
* \brief Returns the value of an integer configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The node's value.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not an integer node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_get_integer(const snd_config_t *config, long *ptr)
{
@@ -2112,11 +2421,18 @@ int snd_config_get_integer(const snd_config_t *config, long *ptr)
}
/**
- * \brief Returns the value of an integer64 configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \brief Returns the value of a 64-bit-integer configuration node.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The node's value.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a 64-bit-integer node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_get_integer64(const snd_config_t *config, long long *ptr)
{
@@ -2128,11 +2444,15 @@ int snd_config_get_integer64(const snd_config_t *config, long long *ptr)
}
/**
- * \brief Returns the value of a real configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \brief Returns the value of a real-number configuration node.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The node's value.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a real-number node.
+ * </dl>
*/
int snd_config_get_real(const snd_config_t *config, double *ptr)
{
@@ -2145,13 +2465,17 @@ int snd_config_get_real(const snd_config_t *config, double *ptr)
/**
* \brief Returns the value of a real or integer configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The node's value.
* \return Zero if successful, otherwise a negative error code.
*
* If the node's type is integer or integer64, the value is converted
* to the \c double type on the fly.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a number node.
+ * </dl>
*/
int snd_config_get_ireal(const snd_config_t *config, double *ptr)
{
@@ -2169,13 +2493,24 @@ int snd_config_get_ireal(const snd_config_t *config, double *ptr)
/**
* \brief Returns the value of a string configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The function puts the node's value at the address
+ * specified by \a ptr.
* \return Zero if successful, otherwise a negative error code.
*
- * The returned string is owned by the configuration node; the application
- * must not modify or delete it.
+ * The returned string is owned by the configuration node; the
+ * application must not modify or delete it, and the string becomes
+ * invalid when the node's value changes or when the node is freed.
+ *
+ * The string may be \c NULL.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a string node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_get_string(const snd_config_t *config, const char **ptr)
{
@@ -2188,10 +2523,15 @@ int snd_config_get_string(const snd_config_t *config, const char **ptr)
/**
* \brief Returns the value of a pointer configuration node.
- * \param config Handle to the configuration node.
- * \param ptr The function puts the node's value at the address specified
- * by \p ptr.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ptr The function puts the node's value at the address
+ * specified by \a ptr.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a string node.
+ * </dl>
*/
int snd_config_get_pointer(const snd_config_t *config, const void **ptr)
{
@@ -2204,13 +2544,30 @@ int snd_config_get_pointer(const snd_config_t *config, const void **ptr)
/**
* \brief Returns the value of a configuration node as a string.
- * \param config Handle to the configuration node.
- * \param ascii The function puts the pointer to the returned string at the
- * address specified by \p ascii.
+ * \param[in] config Handle to the configuration node.
+ * \param[out] ascii The function puts the pointer to the returned
+ * string at the address specified by \a ascii.
* \return Zero if successful, otherwise a negative error code.
*
- * This function dynamically allocates the returned string. The application
- * is responsible for deleting it with \c free() when it is no longer used.
+ * This function dynamically allocates the returned string. The
+ * application is responsible for deleting it with \c free() when it is
+ * no longer used.
+ *
+ * For a string node with \c NULL value, the returned string is \c NULL.
+ *
+ * Supported node types are #SND_CONFIG_TYPE_INTEGER,
+ * #SND_CONFIG_TYPE_INTEGER64, #SND_CONFIG_TYPE_REAL, and
+ * #SND_CONFIG_TYPE_STRING.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a config is not a (64-bit) integer or real number or
+ * string node.
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_get_ascii(const snd_config_t *config, char **ascii)
{
@@ -2281,12 +2638,18 @@ int snd_config_get_ascii(const snd_config_t *config, char **ascii)
* \brief Compares the id of a configuration node to a given string.
* \param config Handle to the configuration node.
* \param id ASCII id.
- * \return The same value as the result of the \c strcmp function.
+ * \return The same value as the result of the \c strcmp function, i.e.,
+ * less than zero if \a config's id is lexicographically less
+ * than \a id, zero if \a config's id is equal to id, greater
+ * than zero otherwise.
*/
int snd_config_test_id(const snd_config_t *config, const char *id)
{
assert(config && id);
- return strcmp(config->id, id);
+ if (config->id)
+ return strcmp(config->id, id);
+ else
+ return -1;
}
/**
@@ -2294,14 +2657,26 @@ int snd_config_test_id(const snd_config_t *config, const char *id)
* \param config Handle to the (root) configuration node.
* \param out Output handle.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function writes a textual representation of \a config's value to
+ * the output \a out.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>A node in the tree has a type that cannot be printed,
+ * i.e., #SND_CONFIG_TYPE_POINTER.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_save(snd_config_t *config, snd_output_t *out)
{
assert(config && out);
if (config->type == SND_CONFIG_TYPE_COMPOUND)
- return _snd_config_save_leaves(config, out, 0, 0);
+ return _snd_config_save_children(config, out, 0, 0);
else
- return _snd_config_save_leaf(config, out, 0);
+ return _snd_config_save_node_value(config, out, 0);
}
/*
@@ -2426,6 +2801,7 @@ int snd_config_save(snd_config_t *config, snd_output_t *out)
} \
if (snd_config_get_string(res, &key) < 0) \
break; \
+ assert(key); \
if (!first && (strcmp(key, old_key) == 0 || maxloop <= 0)) { \
if (maxloop == 0) \
SNDERR("maximum loop count reached (circular configuration?)"); \
@@ -2451,11 +2827,43 @@ int snd_config_save(snd_config_t *config, snd_output_t *out)
/**
* \brief Searches for a node in a configuration tree.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param key Search key: one or more node keys, separated with dots.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \param[in] config Handle to the root of the configuration (sub)tree to search.
+ * \param[in] key Search key: one or more node ids, separated with dots.
+ * \param[out] result When \a result != \c NULL, the function puts the
+ * handle to the node found at the address specified
+ * by \a result.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function searches for a child node of \a config that is
+ * identified by \a key, which contains either the id of a direct child
+ * node of \a config, or a series of ids, separated with dots, where
+ * each id specifies a node that is contained in the previous compound
+ * node.
+ *
+ * In the following example, the comment after each node shows the
+ * search key to find that node, assuming that \a config is a handle to
+ * the compound node with id \c config:
+ * \code
+ * config {
+ * a 42 # "a"
+ * b { # "b"
+ * c "cee" # "b.c"
+ * d { # "b.d"
+ * e 2.71828 # "b.d.e"
+ * }
+ * }
+ * }
+ * \endcode
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
{
@@ -2464,13 +2872,53 @@ int snd_config_search(snd_config_t *config, const char *key, snd_config_t **resu
/**
* \brief Searches for a node in a configuration tree, expanding aliases.
- * \param root Handle to the root configuration node containing alias
- * definitions.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param key Search key: one or more node keys, separated with dots.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \param[in] root Handle to the root configuration node containing
+ * alias definitions.
+ * \param[in] config Handle to the root of the configuration (sub)tree to search.
+ * \param[in] key Search key: one or more node keys, separated with dots.
+ * \param[out] result When \a result != \c NULL, the function puts the
+ * handle to the node found at the address specified
+ * by \a result.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This functions searches for a child node of \a config like
+ * #snd_config_search. However, any compound node can also be
+ * identified by an alias, which is a string node whose value is taken
+ * as the id of a compound node below \a root.
+ *
+ * \a root must be a compound node.
+ * \a root and \a config may be the same node.
+ *
+ * For example, with the following configuration, the call
+ * \code
+ * snd_config_searcha(root, config, "a.b.c.d", &result);
+ * \endcode
+ * would return the node with id \c d:
+ * \code
+ * config {
+ * a {
+ * b bb
+ * }
+ * }
+ * root {
+ * bb {
+ * c cc
+ * }
+ * cc ccc
+ * ccc {
+ * d {
+ * x "icks"
+ * }
+ * }
+ * }
+ * \endcode
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound or string node.
+ * </dl>
*/
int snd_config_searcha(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
{
@@ -2479,11 +2927,34 @@ int snd_config_searcha(snd_config_t *root, snd_config_t *config, const char *key
/**
* \brief Searches for a node in a configuration tree.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
- * \param ... One or more concatenated dot separated search keys, terminated with \c NULL.
+ * \param[in] config Handle to the root of the configuration (sub)tree to search.
+ * \param[out] result When \a result != \c NULL, the function puts the
+ * handle to the node found at the address specified
+ * by \a result.
+ * \param[in] ... One or more concatenated dot-separated search keys,
+ * terminated with \c NULL.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This functions searches for a child node of \a config like
+ * #snd_config_search, but the search key is the concatenation of all
+ * passed search key strings. For example, the call
+ * \code
+ * snd_config_searchv(cfg, &res, "a", "b.c", "d.e", NULL);
+ * \endcode
+ * is equivalent to the call
+ * \code
+ * snd_config_search(cfg, "a.b.c.d.e", &res);
+ * \endcode
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in a search key does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_searchv(snd_config_t *config, snd_config_t **result, ...)
{
@@ -2492,13 +2963,27 @@ int snd_config_searchv(snd_config_t *config, snd_config_t **result, ...)
/**
* \brief Searches for a node in a configuration tree, expanding aliases.
- * \param root Handle to the root configuration node containing alias
- * definitions.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
- * \param ... One or more concatenated dot separated search keys, terminated with \c NULL.
+ * \param[in] root Handle to the root configuration node containing
+ * alias definitions.
+ * \param[in] config Handle to the root of the configuration (sub)tree to search.
+ * \param[out] result When \a result != \c NULL, the function puts the
+ * handle to the node found at the address specified
+ * by \a result.
+ * \param[in] ... One or more concatenated dot separated search keys,
+ * terminated with \c NULL.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function searches for a child node of \a config, allowing
+ * aliases, like #snd_config_searcha, but the search key is the
+ * concatenation of all passed seach key strings, like with
+ * #snd_config_searchv.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in a search key does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound or string node.
+ * </dl>
*/
int snd_config_searchva(snd_config_t *root, snd_config_t *config, snd_config_t **result, ...)
{
@@ -2506,17 +2991,30 @@ int snd_config_searchva(snd_config_t *root, snd_config_t *config, snd_config_t *
}
/**
- * \brief Searches for a node in a configuration tree, using an alias.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param base Search key base, or \c NULL.
- * \param key Search key suffix.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \brief Searches for a node in a configuration tree, expanding aliases.
+ * \param[in] config Handle to the root of the configuration (sub)tree to search.
+ * \param[in] base Search key base, or \c NULL.
+ * \param[in] key Search key suffix.
+ * \param[out] result When \a result != \c NULL, the function puts the
+ * handle to the node found at the address specified
+ * by \a result.
* \return Zero if successful, otherwise a negative error code.
*
- * First \c key is tried, then, if nothing is found, \c base.key is tried.
- * If the value found is a string, this is recursively tried in the
- * same way.
+ * This functions searches for a child node of \a config, allowing
+ * aliases, like #snd_config_searcha. However, alias definitions are
+ * searched below \a config (there is no separate \a root parameter),
+ * and \a base specifies a seach key that identifies a compound node
+ * that is used to search for an alias definitions that is not found
+ * directly below \a config and that does not contain a period. In
+ * other words, when \c "id" is not found in \a config, this function
+ * also tries \c "base.id".
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound or string node.
+ * </dl>
*/
int snd_config_search_alias(snd_config_t *config,
const char *base, const char *key,
@@ -2530,11 +3028,25 @@ static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data);
/**
* \brief Searches for a node in a configuration tree and expands hooks.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param key Search key: one or more node keys, separated with dots.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \param[in,out] config Handle to the root of the configuration
+ * (sub)tree to search.
+ * \param[in] key Search key: one or more node keys, separated with dots.
+ * \param[out] result The function puts the handle to the node found at
+ * the address specified by \a result.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This functions searches for a child node of \a config like
+ * #snd_config_search, but any compound nodes to be searched that
+ * contain hooks are modified by the respective hook functions.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ * Additionally, any errors encountered when parsing the hook
+ * definitions or returned by the hook functions.
*/
int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t **result)
{
@@ -2547,13 +3059,27 @@ int snd_config_search_hooks(snd_config_t *config, const char *key, snd_config_t
/**
* \brief Searches for a node in a configuration tree, expanding aliases and hooks.
- * \param root Handle to the root configuration node containing alias
- * definitions.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param key Search key: one or more node keys, separated with dots.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \param[in] root Handle to the root configuration node containing
+ * alias definitions.
+ * \param[in,out] config Handle to the root of the configuration
+ * (sub)tree to search.
+ * \param[in] key Search key: one or more node keys, separated with dots.
+ * \param[out] result The function puts the handle to the node found at
+ * the address specified by \a result.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function searches for a child node of \a config, allowing
+ * aliases, like #snd_config_searcha, and expanding hooks, like
+ * #snd_config_search_hooks.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ * Additionally, any errors encountered when parsing the hook
+ * definitions or returned by the hook functions.
*/
int snd_config_searcha_hooks(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result)
{
@@ -2567,13 +3093,29 @@ int snd_config_searcha_hooks(snd_config_t *root, snd_config_t *config, const cha
/**
* \brief Searches for a node in a configuration tree, expanding aliases and hooks.
- * \param root Handle to the root configuration node containing alias
- * definitions.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
- * \param ... One or more concatenated dot separated search keys, terminated with \c NULL.
+ * \param[in] root Handle to the root configuration node containing
+ * alias definitions.
+ * \param[in,out] config Handle to the root of the configuration
+ * (sub)tree to search.
+ * \param[out] result The function puts the handle to the node found at
+ * the address specified by \a result.
+ * \param[in] ... One or more concatenated dot separated search keys,
+ * terminated with \c NULL.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function searches for a child node of \a config, allowing
+ * aliases and expanding hooks like #snd_config_searcha_hooks, but the
+ * search key is the concatenation of all passed seach key strings, like
+ * with #snd_config_searchv.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ * Additionally, any errors encountered when parsing the hook
+ * definitions or returned by the hook functions.
*/
int snd_config_searchva_hooks(snd_config_t *root, snd_config_t *config,
snd_config_t **result, ...)
@@ -2583,16 +3125,26 @@ int snd_config_searchva_hooks(snd_config_t *root, snd_config_t *config,
/**
* \brief Searches for a node in a configuration tree, using an alias and expanding hooks.
- * \param config Handle to the root of the configuration (sub)tree to search.
- * \param base Search key base, or \c NULL.
- * \param key Search key suffix.
- * \param result The function puts the handle to the node found at the address
- * specified by \p result.
+ * \param[in] config Handle to the root of the configuration (sub)tree
+ * to search.
+ * \param[in] base Search key base, or \c NULL.
+ * \param[in] key Search key suffix.
+ * \param[out] result The function puts the handle to the node found at
+ * the address specified by \a result.
* \return Zero if successful, otherwise a negative error code.
*
- * First \c key is tried, then, if nothing is found, \c base.key is tried.
- * If the value found is a string, this is recursively tried in the
- * same way.
+ * This functions searches for a child node of \a config, allowing
+ * aliases, like #snd_config_search_alias, and expanding hooks, like
+ * #snd_config_search_hooks.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ * Additionally, any errors encountered when parsing the hook
+ * definitions or returned by the hook functions.
*/
int snd_config_search_alias_hooks(snd_config_t *config,
const char *base, const char *key,
@@ -2611,7 +3163,26 @@ int snd_config_search_alias_hooks(snd_config_t *config,
/**
* \ingroup Config
- * Configuration top level node (the global configuration).
+ * \brief Configuration top-level node (the global configuration).
+ *
+ * This variable contains a handle to the top-level configuration node,
+ * as loaded from global configuration file.
+ *
+ * This variable is initialized or updated by #snd_config_update.
+ * Functions like #snd_pcm_open (that use a device name from the global
+ * configuration) automatically call #snd_config_update. Before the
+ * first call to #snd_config_update, this variable is \c NULL.
+ *
+ * The global configuration files are specified in the environment
+ * variable \c ALSA_CONFIG_PATH. If this is not set, the default value
+ * is "/usr/share/alsa/alsa.conf".
+ *
+ * \warning Whenever the configuration tree is updated, all string
+ * pointers and configuration node handles previously obtained from this
+ * variable may become invalid.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
snd_config_t *snd_config = NULL;
@@ -2651,6 +3222,7 @@ static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_c
SNDERR("Invalid type for field func");
return err;
}
+ assert(str);
err = snd_config_search_definition(root, "hook_func", str, &func_conf);
if (err >= 0) {
snd_config_iterator_t i, next;
@@ -2760,12 +3332,15 @@ static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data)
/**
* \brief Loads and parses the given configurations files.
- * \param root Handle to the root configuration node.
- * \param config Handle to the configuration node for this hook.
- * \param dst The function puts the handle to the configuration node loaded
- * from the file(s) at the address specified by \p dst.
- * \param private_data Handle to the private data configuration node.
+ * \param[in] root Handle to the root configuration node.
+ * \param[in] config Handle to the configuration node for this hook.
+ * \param[out] dst The function puts the handle to the configuration
+ * node loaded from the file(s) at the address specified
+ * by \a dst.
+ * \param[in] private_data Handle to the private data configuration node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * See \ref confhooks for an example.
*/
int snd_config_hook_load(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data)
{
@@ -2873,13 +3448,19 @@ int snd_determine_driver(int card, char **driver);
#endif
/**
- * \brief Loads and parses the given configurations files for each installed sound card.
- * \param root Handle to the root configuration node.
- * \param config Handle to the configuration node for this hook.
- * \param dst The function puts the handle to the configuration node loaded
- * from the file(s) at the address specified by \p dst.
- * \param private_data Handle to the private data configuration node.
+ * \brief Loads and parses the given configurations files for each
+ * installed sound card.
+ * \param[in] root Handle to the root configuration node.
+ * \param[in] config Handle to the configuration node for this hook.
+ * \param[out] dst The function puts the handle to the configuration
+ * node loaded from the file(s) at the address specified
+ * by \a dst.
+ * \param[in] private_data Handle to the private data configuration node.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This function works like #snd_config_hook_load, but the files are
+ * loaded once for each sound card. The driver name is available with
+ * the \c private_string function to customize the file name.
*/
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)
{
@@ -2896,9 +3477,9 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config,
err = snd_determine_driver(card, &fdriver);
if (err < 0)
return err;
- if (snd_config_search(root, fdriver, &n) >= 0) {
- if (snd_config_get_string(n, &driver) < 0)
- goto __err;
+ if (snd_config_search(root, fdriver, &n) >= 0 &&
+ snd_config_get_string(n, &driver) >= 0) {
+ assert(driver);
while (1) {
char *s = strchr(driver, '.');
if (s == NULL)
@@ -2931,20 +3512,30 @@ SND_DLSYM_BUILD_VERSION(snd_config_hook_load_for_all_cards, SND_CONFIG_DLSYM_VER
/**
* \brief Updates a configuration tree by rereading the configuration files (if needed).
- * \param _top Address of the handle to the top level node.
- * \param _update Address of a pointer to private update information.
- * \param cfgs A list of configuration file names, delimited with ':'.
- * If \p cfgs is set to \c NULL, the default global configuration
- * file is used ("/usr/share/alsa/alsa.conf").
- * \return A non-negative value if successful, otherwise a negative error code.
- * \retval 0 No action is needed.
- * \retval 1 The configuration tree has been rebuilt.
+ * \param[in,out] _top Address of the handle to the top-level node.
+ * \param[in,out] _update Address of a pointer to private update information.
+ * \param[in] cfgs A list of configuration file names, delimited with ':'.
+ * If \p cfgs is \c NULL, the default global
+ * configuration file is used.
+ * \return 0 if \a _top was up to date, 1 if the configuration files
+ * have been reread, otherwise a negative error code.
+ *
+ * The variables pointed to by \a _top and \a _update can be initialized
+ * to \c NULL before the first call to this function. The private
+ * update information holds information about all used configuration
+ * files that allows this function to detects changes to them; this data
+ * can be freed with #snd_config_update_free.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH.
*
* \warning If the configuration tree is reread, all string pointers and
- * configuration node handles previously obtained from this tree become invalid.
+ * configuration node handles previously obtained from this tree become
+ * invalid.
+ *
+ * \par Errors:
+ * Any errors encountered when parsing the input or returned by hooks or
+ * functions.
*/
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
{
@@ -3090,16 +3681,19 @@ static pthread_mutex_t snd_config_update_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* \brief Updates #snd_config by rereading the global configuration files (if needed).
- * \return A non-negative value if successful, otherwise a negative error code.
- * \retval 0 No action is needed.
- * \retval 1 The configuration tree has been rebuilt.
+ * \return 0 if #snd_config was up to date, 1 if #snd_config was
+ * updated, otherwise a negative error code.
*
- * The global configuration files are specified in the environment variable
- * \c ALSA_CONFIG_PATH. If this is not set, the default value is
- * "/usr/share/alsa/alsa.conf".
+ * \warning Whenever #snd_config is updated, all string pointers and
+ * configuration node handles previously obtained from it may become
+ * invalid.
*
- * \warning If the configuration tree is reread, all string pointers and
- * configuration node handles previously obtained from this tree become invalid.
+ * \par Errors:
+ * Any errors encountered when parsing the input or returned by hooks or
+ * functions.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_update(void)
{
@@ -3117,7 +3711,7 @@ int snd_config_update(void)
/**
* \brief Frees a private update structure.
- * \param update The private update structure to free.
+ * \param[in] update The private update structure to free.
* \return Zero if successful, otherwise a negative error code.
*/
int snd_config_update_free(snd_config_update_t *update)
@@ -3135,6 +3729,12 @@ int snd_config_update_free(snd_config_update_t *update)
/**
* \brief Frees the global configuration tree in #snd_config.
* \return Zero if successful, otherwise a negative error code.
+ *
+ * This functions releases all resources of the global configuration
+ * tree, and sets #snd_config to \c NULL.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_update_free_global(void)
{
@@ -3157,23 +3757,40 @@ int snd_config_update_free_global(void)
}
/**
- * \brief Returns an iterator pointing to the first child of a compound configuration node.
- * \param node Handle to the compound configuration node.
- * \return An iterator pointing to the first child.
+ * \brief Returns an iterator pointing to a node's first child.
+ * \param[in] config Handle to a configuration node.
+ * \return An iterator pointing to \a config's first child.
+ *
+ * \a config must be a compound node.
+ *
+ * The returned iterator is valid if it is not equal to the return value
+ * of #snd_config_iterator_end on \a config.
+ *
+ * Use #snd_config_iterator_entry to get the handle of the node pointed
+ * to.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
-snd_config_iterator_t snd_config_iterator_first(const snd_config_t *node)
+snd_config_iterator_t snd_config_iterator_first(const snd_config_t *config)
{
- assert(node->type == SND_CONFIG_TYPE_COMPOUND);
- return node->u.compound.fields.next;
+ assert(config->type == SND_CONFIG_TYPE_COMPOUND);
+ return config->u.compound.fields.next;
}
/**
* \brief Returns an iterator pointing to the next sibling.
- * \param iterator An iterator pointing to a child configuration node.
- * \return An iterator pointing to the next sibling of \p iterator.
- * If \p iterator is the last sibling, the returned value is the same
- * as the result of calling #snd_config_iterator_end on the father
- * of the nodes.
+ * \param[in] iterator An iterator pointing to a child configuration node.
+ * \return An iterator pointing to the next sibling of \a iterator.
+ *
+ * The returned iterator is valid if it is not equal to the return value
+ * of #snd_config_iterator_end on the node's parent.
+ *
+ * Use #snd_config_iterator_entry to get the handle of the node pointed
+ * to.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
snd_config_iterator_t snd_config_iterator_next(const snd_config_iterator_t iterator)
{
@@ -3181,20 +3798,31 @@ snd_config_iterator_t snd_config_iterator_next(const snd_config_iterator_t itera
}
/**
- * \brief Returns an iterator pointing past the last child of a compound configuration node.
- * \param node Handle to the compound configuration node.
- * \return An iterator pointing past the last child of \p node.
+ * \brief Returns an iterator that ends a node's children list.
+ * \param[in] config Handle to a configuration node.
+ * \return An iterator that indicates the end of \a config's children list.
+ *
+ * \a config must be a compound node.
+ *
+ * The return value can be understood as pointing past the last child of
+ * \a config.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
-snd_config_iterator_t snd_config_iterator_end(const snd_config_t *node)
+snd_config_iterator_t snd_config_iterator_end(const snd_config_t *config)
{
- assert(node->type == SND_CONFIG_TYPE_COMPOUND);
- return (const snd_config_iterator_t)&node->u.compound.fields;
+ assert(config->type == SND_CONFIG_TYPE_COMPOUND);
+ return (const snd_config_iterator_t)&config->u.compound.fields;
}
/**
* \brief Returns the configuration node handle pointed to by an iterator.
- * \param iterator A configuration node iterator.
- * \return The configuration node handle pointed to by \p iterator.
+ * \param[in] iterator A configuration node iterator.
+ * \return The configuration node handle pointed to by \a iterator.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
snd_config_t *snd_config_iterator_entry(const snd_config_iterator_t iterator)
{
@@ -3209,7 +3837,7 @@ typedef enum _snd_config_walk_pass {
} snd_config_walk_pass_t;
#endif
-/* Return 1 if node needs to be attached to father */
+/* Return 1 if node needs to be attached to parent */
/* Return 2 if compound is replaced with standard node */
#ifndef DOC_HIDDEN
typedef int (*snd_config_walk_callback_t)(snd_config_t *src,
@@ -3327,10 +3955,21 @@ static int _snd_config_copy(snd_config_t *src,
/**
* \brief Creates a copy of a configuration node.
- * \param dst The function puts the handle to the new configuration node
- * at the address specified by \p dst.
- * \param src Handle to the source configuration node.
+ * \param[out] dst The function puts the handle to the new configuration
+ * node at the address specified by \a dst.
+ * \param[in] src Handle to the source configuration node.
* \return A non-negative value if successful, otherwise a negative error code.
+ *
+ * This function creates a deep copy, i.e., if \a src is a compound
+ * node, all children are copied recursively.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ * </dl>
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_config_copy(snd_config_t **dst,
snd_config_t *src)
@@ -3350,7 +3989,7 @@ static int _snd_config_expand(snd_config_t *src,
switch (pass) {
case SND_CONFIG_WALK_PASS_PRE:
{
- if (strcmp(id, "@args") == 0)
+ if (id && strcmp(id, "@args") == 0)
return 0;
err = snd_config_make_compound(dst, id, src->u.compound.join);
if (err < 0)
@@ -3395,7 +4034,7 @@ static int _snd_config_expand(snd_config_t *src,
snd_config_t *val;
snd_config_t *vars = private_data;
snd_config_get_string(src, &s);
- if (*s == '$') {
+ if (s && *s == '$') {
s++;
if (snd_config_search(vars, s, &val) < 0)
return 0;
@@ -3447,6 +4086,7 @@ static int _snd_config_evaluate(snd_config_t *src,
SNDERR("Invalid type for @func");
return err;
}
+ assert(str);
err = snd_config_search_definition(root, "func", str, &func_conf);
if (err >= 0) {
snd_config_iterator_t i, next;
@@ -3531,14 +4171,14 @@ static int _snd_config_evaluate(snd_config_t *src,
/**
* \brief Evaluates a configuration node at runtime.
- * \param config Handle to the source configuration node.
- * \param root Handle to the root of the source configuration.
- * \param private_data Handle to the private data node for runtime evaluation.
- * \param result The function puts the handle to the result node at the
- * address specified by \p result. \p result is \c NULL for
- * in-place evaluation.
+ * \param[in,out] config Handle to the source configuration node.
+ * \param[in] root Handle to the root of the source configuration.
+ * \param[in] private_data Handle to the private data node for runtime evaluation.
+ * \param result Must be \c NULL.
* \return A non-negative value if successful, otherwise a negative error code.
- * \note Only in-place evaluation is currently implemented.
+ *
+ * This function evaluates any functions (\c \@func) in \a config and
+ * replaces those nodes with the respective function results.
*/
int snd_config_evaluate(snd_config_t *config, snd_config_t *root,
snd_config_t *private_data, snd_config_t **result)
@@ -3859,7 +4499,7 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
goto _err;
}
err = snd_config_get_string(typ, &tmp);
- if (err < 0)
+ if (err < 0 || !tmp)
goto _invalid_type;
if (strcmp(tmp, "integer") == 0) {
long v;
@@ -3932,14 +4572,21 @@ static int parse_args(snd_config_t *subs, const char *str, snd_config_t *defs)
}
/**
- * \brief Expands a configuration node applying arguments and functions.
- * \param config Handle to the configuration node.
- * \param root Handle to the root configuration node.
- * \param args Arguments string (optional).
- * \param private_data Handle to the private data node for functions.
- * \param result The function puts the handle to the result configuration node
- * at the address specified by \p result.
+ * \brief Expands a configuration node, applying arguments and functions.
+ * \param[in] config Handle to the configuration node.
+ * \param[in] root Handle to the root configuration node.
+ * \param[in] args Arguments string, can be \c NULL.
+ * \param[in] private_data Handle to the private data node for functions.
+ * \param[out] result The function puts the handle to the result
+ * configuration node at the address specified by
+ * \a result.
* \return A non-negative value if successful, otherwise a negative error code.
+ *
+ * If \a config has arguments (defined by a child with id \c \@args),
+ * this function replaces any string node beginning with $ with the
+ * respective argument value, or the default argument value, or nothing.
+ * Furthermore, any functions are evaluated (see #snd_config_evaluate).
+ * The resulting copy of \a config is returned in \a result.
*/
int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,
snd_config_t *private_data, snd_config_t **result)
@@ -3995,20 +4642,33 @@ int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args
}
/**
- * \brief Searches for a definition in a configuration tree, using aliases and expanding hooks and arguments.
- * \param config Handle to the configuration (sub)tree to search.
- * \param base Implicit key base, or \c NULL for none.
- * \param name Key suffix.
- * \param result The function puts the handle to the expanded found node at
- * the address specified by \p result.
+ * \brief Searches for a definition in a configuration tree, using
+ * aliases and expanding hooks and arguments.
+ * \param[in] config Handle to the configuration (sub)tree to search.
+ * \param[in] base Implicit key base, or \c NULL for none.
+ * \param[in] name Key suffix, optionally with arguments.
+ * \param[out] result The function puts the handle to the expanded found
+ * node at the address specified by \a result.
* \return A non-negative value if successful, otherwise a negative error code.
*
- * First the key is tried, then, if nothing is found, base.key is tried.
- * If the value found is a string, this is recursively tried in the
- * same way.
+ * This functions searches for a child node of \a config, allowing
+ * aliases and expanding hooks, like #snd_config_search_alias_hooks.
+ *
+ * If \a name contains a colon (:), the rest of the string after the
+ * colon contains arguments that are expanded as with
+ * #snd_config_expand.
+ *
+ * In any case, \a result is a new node that must be freed by the
+ * caller.
*
- * If \p key contains a dot (.), the implicit base is ignored and the key
- * starts from the root given by \p config.
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
+ * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
+ * not a compound node.
+ * </dl>
+ * Additionally, any errors encountered when parsing the hook
+ * definitions or arguments, or returned by (hook) functions.
*/
int snd_config_search_definition(snd_config_t *config,
const char *base, const char *name,
diff --git a/src/conf/cards/Makefile.am b/src/conf/cards/Makefile.am
index f4d6c17..9da78f0 100644
--- a/src/conf/cards/Makefile.am
+++ b/src/conf/cards/Makefile.am
@@ -41,6 +41,7 @@ cfg_files = aliases.conf \
RME9636.conf \
RME9652.conf \
SI7018.conf \
+ SB-XFi.conf \
TRID4DWAVENX.conf \
USB-Audio.conf \
YMF744.conf \
diff --git a/src/conf/cards/SB-XFi.conf b/src/conf/cards/SB-XFi.conf
new file mode 100644
index 0000000..38d0027
--- /dev/null
+++ b/src/conf/cards/SB-XFi.conf
@@ -0,0 +1,108 @@
+#
+# Configuration for the SB X-Fi driver
+#
+
+<confdir:pcm/front.conf>
+
+SB-XFi.pcm.front.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 0
+}
+
+<confdir:pcm/rear.conf>
+
+SB-XFi.pcm.rear.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 1
+ hint.device 1
+}
+
+<confdir:pcm/center_lfe.conf>
+
+SB-XFi.pcm.center_lfe.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 2
+ hint.device 2
+}
+
+<confdir:pcm/side.conf>
+
+SB-XFi.pcm.side.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 3
+ hint.device 3
+}
+
+<confdir:pcm/surround40.conf>
+<confdir:pcm/surround41.conf>
+<confdir:pcm/surround50.conf>
+<confdir:pcm/surround51.conf>
+<confdir:pcm/surround71.conf>
+
+SB-XFi.pcm.surround40.0 cards.SB-XFi.pcm.front.0
+SB-XFi.pcm.surround51.0 cards.SB-XFi.pcm.front.0
+SB-XFi.pcm.surround71.0 cards.SB-XFi.pcm.front.0
+
+<confdir:pcm/iec958.conf>
+
+SB-XFi.pcm.iec958.0 {
+ @args [ CARD AES0 AES1 AES2 AES3 ]
+ @args.CARD {
+ type string
+ }
+ @args.AES0 {
+ type integer
+ }
+ @args.AES1 {
+ type integer
+ }
+ @args.AES2 {
+ type integer
+ }
+ @args.AES3 {
+ type integer
+ }
+ type asym
+ playback.pcm {
+ type hooks
+ slave.pcm {
+ type hw
+ card $CARD
+ device 4
+ }
+ hooks.0 {
+ type ctl_elems
+ hook_args [
+ {
+ interface PCM
+ name "IEC958 Playback PCM Stream"
+ device 4
+ lock true
+ preserve true
+ value [ $AES0 $AES1 $AES2 $AES3 ]
+ }
+ ]
+ }
+ }
+ hint.device 4
+}
diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf
index d6fbec5..5849e3f 100644
--- a/src/conf/cards/USB-Audio.conf
+++ b/src/conf/cards/USB-Audio.conf
@@ -40,11 +40,48 @@ USB-Audio.pcm.iec958_device {
# If a device requires non-standard definitions for front, surround40,
# surround51, surround71 or iec958, they can be defined here.
-# USB-Audio."NoiseBlaster 3000".pcm.surround51 {
-# @args [ CARD ]
-# @args.CARD { type string }
-# ...
-# }
+# M-Audio AudioPhile USB:
+# device 0: analog output, digital input
+# device 1: digital output, analog input
+USB-Audio."AudioPhile".pcm.default {
+ @args [ CARD ]
+ @args.CARD { type string }
+ type asym
+ playback.pcm {
+ type plug
+ slave.pcm {
+ type hw
+ card $CARD
+ device 0
+ }
+ }
+ capture.pcm {
+ type plug
+ slave.pcm {
+ @func concat
+ strings [ "dsnoop:DEVICE=1,CARD=" $CARD ]
+ }
+ }
+}
+USB-Audio."AudioPhile".pcm.iec958 {
+ @args [ CARD AES0 AES1 AES2 AES3 ]
+ @args.CARD { type string }
+ @args.AES0 { type integer }
+ @args.AES1 { type integer }
+ @args.AES2 { type integer }
+ @args.AES3 { type integer }
+ type asym
+ playback.pcm {
+ type hw
+ card $CARD
+ device 1
+ }
+ capture.pcm {
+ type hw
+ card $CARD
+ device 0
+ }
+}
################################################################################
diff --git a/src/control/cards.c b/src/control/cards.c
index 4d2c739..0bb8f86 100644
--- a/src/control/cards.c
+++ b/src/control/cards.c
@@ -39,27 +39,40 @@
#define SND_FILE_LOAD ALOAD_DEVICE_DIRECTORY "aloadC%i"
#endif
-static int snd_card_load1(int card)
+static int snd_card_load2(const char *control)
{
int open_dev;
+ snd_ctl_card_info_t info;
+
+ open_dev = snd_open_device(control, O_RDONLY);
+ if (open_dev >= 0) {
+ if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {
+ int err = -errno;
+ close(open_dev);
+ return err;
+ }
+ close(open_dev);
+ return info.card;
+ } else {
+ return -errno;
+ }
+}
+
+static int snd_card_load1(int card)
+{
+ int res;
char control[sizeof(SND_FILE_CONTROL) + 10];
sprintf(control, SND_FILE_CONTROL, card);
-
- open_dev = snd_open_device(control, O_RDONLY);
+ res = snd_card_load2(control);
#ifdef SUPPORT_ALOAD
- if (open_dev < 0) {
+ if (res < 0) {
char aload[sizeof(SND_FILE_LOAD) + 10];
sprintf(aload, SND_FILE_LOAD, card);
- open_dev = snd_open_device(aload, O_RDONLY);
+ res = snd_card_load2(aload);
}
#endif
- if (open_dev >= 0) {
- close (open_dev);
- return 0;
- } else {
- return -errno;
- }
+ return res;
}
/**
@@ -69,7 +82,7 @@ static int snd_card_load1(int card)
*/
int snd_card_load(int card)
{
- return !!(snd_card_load1(card) == 0);
+ return !!(snd_card_load1(card) >= 0);
}
/**
@@ -107,6 +120,7 @@ int snd_card_next(int *rcard)
*
* The accepted format is an integer value in ASCII representation
* or the card identifier (the id parameter for sound-card drivers).
+ * The control device name like /dev/snd/controlC0 is accepted, too.
*/
int snd_card_get_index(const char *string)
{
@@ -127,6 +141,8 @@ int snd_card_get_index(const char *string)
return card;
return err;
}
+ if (string[0] == '/') /* device name */
+ return snd_card_load2(string);
for (card = 0; card < 32; card++) {
#ifdef SUPPORT_ALOAD
if (! snd_card_load(card))
diff --git a/src/control/control.c b/src/control/control.c
index c090797..ae70e76 100644
--- a/src/control/control.c
+++ b/src/control/control.c
@@ -211,7 +211,7 @@ int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsign
}
/**
- * \brief Ask to be informed about events (poll, #snd_ctl_async, #snd_ctl_read)
+ * \brief Ask to be informed about events (poll, #snd_async_add_ctl_handler, #snd_ctl_read)
* \param ctl CTL handle
* \param subscribe 0 = unsubscribe, 1 = subscribe
* \return 0 on success otherwise a negative error code
@@ -674,8 +674,8 @@ int snd_ctl_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
int snd_ctl_wait(snd_ctl_t *ctl, int timeout)
{
struct pollfd *pfd;
- unsigned short *revents;
- int i, npfds, pollio, err, err_poll;
+ unsigned short revents;
+ int i, npfds, err, err_poll;
npfds = snd_ctl_poll_descriptors_count(ctl);
if (npfds <= 0 || npfds >= 16) {
@@ -683,7 +683,6 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout)
return -EIO;
}
pfd = alloca(sizeof(*pfd) * npfds);
- revents = alloca(sizeof(*revents) * npfds);
err = snd_ctl_poll_descriptors(ctl, pfd, npfds);
if (err < 0)
return err;
@@ -691,26 +690,20 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout)
SNDMSG("invalid poll descriptors %d\n", err);
return -EIO;
}
- do {
+ for (;;) {
err_poll = poll(pfd, npfds, timeout);
if (err_poll < 0)
return -errno;
if (! err_poll)
- break;
- err = snd_ctl_poll_descriptors_revents(ctl, pfd, npfds, revents);
+ return 0;
+ err = snd_ctl_poll_descriptors_revents(ctl, pfd, npfds, &revents);
if (err < 0)
return err;
- pollio = 0;
- for (i = 0; i < npfds; i++) {
- if (revents[i] & (POLLERR | POLLNVAL))
- return -EIO;
- if ((revents[i] & (POLLIN | POLLOUT)) == 0)
- continue;
- pollio++;
- }
- } while (! pollio);
-
- return err_poll > 0 ? 1 : 0;
+ if (revents & (POLLERR | POLLNVAL))
+ return -EIO;
+ if (revents & (POLLIN | POLLOUT))
+ return 1;
+ }
}
/**
diff --git a/src/control/control_ext.c b/src/control/control_ext.c
index d1fe8ea..e20d4f3 100644
--- a/src/control/control_ext.c
+++ b/src/control/control_ext.c
@@ -586,7 +586,7 @@ total number of control elements. The elem_list returns the control element ID
of the corresponding element offset (the offset is from 0 to elem_count - 1).
The id field is initialized to all zero in prior to elem_list callback. The callback
has to fill the necessary field (typically iface, name and index) in return via the
-standard control API functions like #snd_ctl_elem_id_set_ifarce,
+standard control API functions like #snd_ctl_elem_id_set_interface,
#snd_ctl_elem_id_set_name and #snd_ctl_elem_id_set_index, etc. The callbacks should
return 0 if successful, or a negative error code.
diff --git a/src/control/tlv.c b/src/control/tlv.c
index 0006a87..cfd9b97 100644
--- a/src/control/tlv.c
+++ b/src/control/tlv.c
@@ -89,6 +89,8 @@ int snd_tlv_parse_dB_info(unsigned int *tlv,
}
break;
case SND_CTL_TLVT_DB_SCALE:
+ case SND_CTL_TLVT_DB_MINMAX:
+ case SND_CTL_TLVT_DB_MINMAX_MUTE:
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR:
#endif
@@ -165,6 +167,8 @@ int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax,
*max = *min + (long)(step * (rangemax - rangemin));
return 0;
}
+ case SND_CTL_TLVT_DB_MINMAX:
+ case SND_CTL_TLVT_DB_MINMAX_MUTE:
case SND_CTL_TLVT_DB_LINEAR:
*min = (int)tlv[2];
*max = (int)tlv[3];
@@ -214,6 +218,23 @@ int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax,
*db_gain = (volume - rangemin) * step + min;
return 0;
}
+ case SND_CTL_TLVT_DB_MINMAX:
+ case SND_CTL_TLVT_DB_MINMAX_MUTE: {
+ int mindb, maxdb;
+ mindb = tlv[2];
+ maxdb = tlv[3];
+ if (volume <= rangemin || rangemax <= rangemin) {
+ if (tlv[0] == SND_CTL_TLVT_DB_MINMAX_MUTE)
+ *db_gain = SND_CTL_TLV_DB_GAIN_MUTE;
+ else
+ *db_gain = mindb;
+ } else if (volume >= rangemax)
+ *db_gain = maxdb;
+ else
+ *db_gain = (maxdb - mindb) * (volume - rangemin) /
+ (rangemax - rangemin) + mindb;
+ return 0;
+ }
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
int mindb = tlv[2];
@@ -297,6 +318,24 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax,
}
return 0;
}
+ case SND_CTL_TLVT_DB_MINMAX:
+ case SND_CTL_TLVT_DB_MINMAX_MUTE: {
+ int min, max;
+ min = tlv[2];
+ max = tlv[3];
+ if (db_gain <= min)
+ *value = rangemin;
+ else if (db_gain >= max)
+ *value = rangemax;
+ else {
+ long v = (db_gain - min) * (rangemax - rangemin);
+ if (xdir > 0)
+ v += (max - min) - 1;
+ v = v / (max - min) + rangemin;
+ *value = v;
+ }
+ return 0;
+ }
#ifndef HAVE_SOFT_FLOAT
case SND_CTL_TLVT_DB_LINEAR: {
int min, max;
@@ -366,7 +405,6 @@ static int get_tlv_info(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id,
* \brief Get the dB min/max values on the given control element
* \param ctl the control handler
* \param id the element id
- * \param volume the raw volume value to convert
* \param min the pointer to store the minimum dB value (in 0.01dB unit)
* \param max the pointer to store the maximum dB value (in 0.01dB unit)
* \return 0 if successful, or a negative error code
diff --git a/src/output.c b/src/output.c
index 8a723f8..adc461a 100644
--- a/src/output.c
+++ b/src/output.c
@@ -333,7 +333,7 @@ static const snd_output_ops_t snd_output_buffer_ops = {
#endif
/**
- * \brief Returns the address of the buffer of a #SND_OUTPUT_TYPE_BUFFER output handle.
+ * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle.
* \param output The output handle.
* \param buf The functions puts the current address of the buffer at the
* address specified by \p buf.
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index e63bbe9..29d4492 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -2460,8 +2460,8 @@ snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm)
/**
* \brief Combine snd_pcm_avail and snd_pcm_delay functions
* \param pcm PCM handle
- * \param avail Number of available frames in the ring buffer
- * \param delay Total I/O latency in frames
+ * \param availp Number of available frames in the ring buffer
+ * \param delayp Total I/O latency in frames
* \return zero on success otherwise a negative error code
*
* The avail and delay values retuned are in sync.
diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c
index 5b967e8..90470e0 100644
--- a/src/pcm/pcm_dmix.c
+++ b/src/pcm/pcm_dmix.c
@@ -145,9 +145,11 @@ static void dmix_server_free(snd_pcm_direct_t *dmix)
#elif defined(__x86_64__)
#include "pcm_dmix_x86_64.c"
#else
+#ifndef DOC_HIDDEN
#define mix_select_callbacks(x) generic_mix_select_callbacks(x)
#define dmix_supported_format generic_dmix_supported_format
#endif
+#endif
static void mix_areas(snd_pcm_direct_t *dmix,
const snd_pcm_channel_area_t *src_areas,
diff --git a/src/pcm/pcm_dmix_i386.h b/src/pcm/pcm_dmix_i386.h
index 9ea155d..462371a 100644
--- a/src/pcm/pcm_dmix_i386.h
+++ b/src/pcm/pcm_dmix_i386.h
@@ -400,8 +400,8 @@ static void MIX_AREAS_24(unsigned int size,
"\tmovzwl (%%esi), %%ecx\n"
"\tmovl (%%ebx), %%edx\n"
"\tsall $16, %%eax\n"
+ "\torl %%eax, %%ecx\n"
"\t" LOCK_PREFIX "btsw $0, (%%edi)\n"
- "\tleal (%%ecx,%%eax,1), %%ecx\n"
"\tjc 2f\n"
"\t" XSUB " %%edx, %%ecx\n"
"2:"
diff --git a/src/pcm/pcm_dmix_x86_64.h b/src/pcm/pcm_dmix_x86_64.h
index b4d0a41..ab40f50 100644
--- a/src/pcm/pcm_dmix_x86_64.h
+++ b/src/pcm/pcm_dmix_x86_64.h
@@ -284,11 +284,11 @@ static void MIX_AREAS_24(unsigned int size,
* *sum += sample;
*/
"\tmovsbl 2(%%rsi), %%eax\n"
- "\tmovswl (%%rsi), %%ecx\n"
+ "\tmovzwl (%%rsi), %%ecx\n"
"\tmovl (%%rbx), %%edx\n"
"\tsall $16, %%eax\n"
+ "\torl %%eax, %%ecx\n"
"\t" LOCK_PREFIX "btsw $0, (%%rdi)\n"
- "\t.byte 0x67, 0x8d, 0x0c, 0x01\n"
"\tjc 2f\n"
"\t" XSUB " %%edx, %%ecx\n"
"2:"
diff --git a/src/pcm/pcm_hooks.c b/src/pcm/pcm_hooks.c
index 826685f..3a99d55 100644
--- a/src/pcm/pcm_hooks.c
+++ b/src/pcm/pcm_hooks.c
@@ -43,12 +43,39 @@ struct _snd_pcm_hook {
struct list_head list;
};
+struct snd_pcm_hook_dllist {
+ void *dlobj;
+ struct list_head list;
+};
+
typedef struct {
snd_pcm_generic_t gen;
struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1];
+ struct list_head dllist;
} snd_pcm_hooks_t;
#endif
+static int hook_add_dlobj(snd_pcm_t *pcm, void *dlobj)
+{
+ snd_pcm_hooks_t *h = pcm->private_data;
+ struct snd_pcm_hook_dllist *dl;
+
+ dl = malloc(sizeof(*dl));
+ if (!dl)
+ return -ENOMEM;
+
+ dl->dlobj = dlobj;
+ list_add_tail(&dl->list, &h->dllist);
+ return 0;
+}
+
+static void hook_remove_dlobj(struct snd_pcm_hook_dllist *dl)
+{
+ list_del(&dl->list);
+ snd_dlclose(dl->dlobj);
+ free(dl);
+}
+
static int snd_pcm_hooks_close(snd_pcm_t *pcm)
{
snd_pcm_hooks_t *h = pcm->private_data;
@@ -71,6 +98,10 @@ static int snd_pcm_hooks_close(snd_pcm_t *pcm)
snd_pcm_hook_remove(hook);
}
}
+ while (!list_empty(&h->dllist)) {
+ pos = h->dllist.next;
+ hook_remove_dlobj(list_entry(pos, struct snd_pcm_hook_dllist, list));
+ }
err = snd_pcm_generic_close(pcm);
if (err < 0)
res = err;
@@ -193,6 +224,7 @@ int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int
for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) {
INIT_LIST_HEAD(&h->hooks[k]);
}
+ INIT_LIST_HEAD(&h->dllist);
err = snd_pcm_new(&pcm, SND_PCM_TYPE_HOOKS, name, slave->stream, slave->mode);
if (err < 0) {
free(h);
@@ -312,6 +344,7 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
snd_config_iterator_t i, next;
int (*install_func)(snd_pcm_t *pcm, snd_config_t *args) = NULL;
void *h = NULL;
+
if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid hook definition");
return -EINVAL;
@@ -402,20 +435,26 @@ static int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *root, snd_config_
_err:
if (type)
snd_config_delete(type);
- if (err >= 0) {
- if (args && snd_config_get_string(args, &str) >= 0) {
- err = snd_config_search_definition(root, "hook_args", str, &args);
- if (err < 0)
- SNDERR("unknown hook_args %s", str);
- else
- err = install_func(pcm, args);
- snd_config_delete(args);
- } else
+ if (err < 0)
+ return err;
+
+ if (args && snd_config_get_string(args, &str) >= 0) {
+ err = snd_config_search_definition(root, "hook_args", str, &args);
+ if (err < 0)
+ SNDERR("unknown hook_args %s", str);
+ else
err = install_func(pcm, args);
+ snd_config_delete(args);
+ } else
+ err = install_func(pcm, args);
+
+ if (err >= 0)
+ err = hook_add_dlobj(pcm, h);
+
+ if (err < 0) {
snd_dlclose(h);
- }
- if (err < 0)
return err;
+ }
return 0;
}
diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c
index e43d354..c582f5a 100644
--- a/src/pcm/pcm_ioplug.c
+++ b/src/pcm/pcm_ioplug.c
@@ -877,7 +877,7 @@ callback.
Finally, the dump callback is used to print the status of the plugin.
The hw_params constraints can be defined via either
-#snd_pcm_iplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
+#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
functions after calling #snd_pcm_ioplug_create().
The former defines the minimal and maximal acceptable values for the
given hw_params parameter (SND_PCM_IOPLUG_HW_XXX).
diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c
index 0357921..5acc7bc 100644
--- a/src/pcm/pcm_meter.c
+++ b/src/pcm/pcm_meter.c
@@ -46,7 +46,7 @@ const char *_snd_module_pcm_meter = "";
struct _snd_pcm_scope {
int enabled;
char *name;
- snd_pcm_scope_ops_t *ops;
+ const snd_pcm_scope_ops_t *ops;
void *private_data;
struct list_head list;
};
@@ -960,7 +960,7 @@ const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope)
* \param scope PCM meter scope
* \param val callbacks
*/
-void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val)
+void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, const snd_pcm_scope_ops_t *val)
{
scope->ops = val;
}
diff --git a/src/pcm/pcm_mmap_emul.c b/src/pcm/pcm_mmap_emul.c
index 0dc1973..e356e0b 100644
--- a/src/pcm/pcm_mmap_emul.c
+++ b/src/pcm/pcm_mmap_emul.c
@@ -34,6 +34,7 @@
const char *_snd_module_pcm_mmap_emul = "";
#endif
+#ifndef DOC_HIDDEN
/*
*
*/
@@ -44,6 +45,7 @@ typedef struct {
snd_pcm_uframes_t hw_ptr;
snd_pcm_uframes_t appl_ptr;
} mmap_emul_t;
+#endif
/*
* here goes a really tricky part; hw_refine falls back to ACCESS_RW_* type
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index a97a5de..ecf0022 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -69,12 +69,17 @@ struct _snd_pcm_rate {
int16_t *dst_buf;
int start_pending; /* start is triggered but not commited to slave */
snd_htimestamp_t trigger_tstamp;
+ unsigned int plugin_version;
+ unsigned int rate_min, rate_max;
};
+#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
+
#endif /* DOC_HIDDEN */
static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
{
+ snd_pcm_rate_t *rate = pcm->private_data;
int err;
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
@@ -89,14 +94,18 @@ static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_
err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
if (err < 0)
return err;
- err = _snd_pcm_hw_param_set_min(params,
- SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MIN, 0);
- if (err < 0)
- return err;
- err = _snd_pcm_hw_param_set_max(params,
- SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MAX, 0);
- if (err < 0)
- return err;
+ if (rate->rate_min) {
+ err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
+ rate->rate_min, 0);
+ if (err < 0)
+ return err;
+ }
+ if (rate->rate_max) {
+ err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
+ rate->rate_max, 0);
+ if (err < 0)
+ return err;
+ }
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
return 0;
}
@@ -186,7 +195,7 @@ static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *p
if (!snd_interval_checkempty(period_size) &&
period_size->openmin && period_size->openmax &&
period_size->min + 1 == period_size->max) {
- if ((buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
+ if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
snd_interval_set_value(period_size, period_size->min);
} else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
snd_interval_set_value(period_size, period_size->max);
@@ -1178,6 +1187,9 @@ static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
rate->srate,
snd_pcm_format_name(rate->sformat));
+ if (rate->ops.dump)
+ rate->ops.dump(rate->obj, out);
+ snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
if (pcm->setup) {
snd_output_printf(out, "Its setup is:\n");
snd_pcm_dump_setup(pcm, out);
@@ -1264,6 +1276,7 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
{
char open_name[64];
snd_pcm_rate_open_func_t open_func;
+ int err;
snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
open_func = snd_dlobj_cache_lookup(open_name);
@@ -1285,7 +1298,25 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
}
snd_dlobj_cache_add(open_name, h, open_func);
}
- return open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+
+ rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
+ rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
+ rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
+
+ err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+ if (!err) {
+ rate->plugin_version = rate->ops.version;
+ if (rate->ops.get_supported_rates)
+ rate->ops.get_supported_rates(rate->obj,
+ &rate->rate_min,
+ &rate->rate_max);
+ return 0;
+ }
+
+ /* try to open with the old protocol version */
+ rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
+ return open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
+ &rate->obj, &rate->ops);
}
#endif
@@ -1295,7 +1326,7 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
* \param name Name of PCM
* \param sformat Slave format
* \param srate Slave rate
- * \param type SRC type string
+ * \param converter SRC type string node
* \param slave Slave PCM handle
* \param close_slave When set, the slave PCM handle is closed with copy PCM
* \retval zero on success otherwise a negative error code
diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c
index 20e119b..8b2d2d0 100644
--- a/src/pcm/pcm_rate_linear.c
+++ b/src/pcm/pcm_rate_linear.c
@@ -405,6 +405,19 @@ static void linear_close(void *obj)
free(obj);
}
+static int get_supported_rates(void *rate, unsigned int *rate_min,
+ unsigned int *rate_max)
+{
+ *rate_min = SND_PCM_PLUGIN_RATE_MIN;
+ *rate_max = SND_PCM_PLUGIN_RATE_MAX;
+ return 0;
+}
+
+static void linear_dump(void *rate, snd_output_t *out)
+{
+ snd_output_printf(out, "Converter: linear-interpolation\n");
+}
+
static const snd_pcm_rate_ops_t linear_ops = {
.close = linear_close,
.init = linear_init,
@@ -414,17 +427,15 @@ static const snd_pcm_rate_ops_t linear_ops = {
.convert = linear_convert,
.input_frames = input_frames,
.output_frames = output_frames,
+ .version = SND_PCM_RATE_PLUGIN_VERSION,
+ .get_supported_rates = get_supported_rates,
+ .dump = linear_dump,
};
int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
{
struct rate_linear *rate;
- if (version != SND_PCM_RATE_PLUGIN_VERSION) {
- SNDERR("Invalid plugin version %x\n", version);
- return -EINVAL;
- }
-
rate = calloc(1, sizeof(*rate));
if (! rate)
return -ENOMEM;
diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c
index f50cc05..b28488a 100644
--- a/src/rawmidi/rawmidi.c
+++ b/src/rawmidi/rawmidi.c
@@ -153,7 +153,7 @@ static int snd_rawmidi_params_default(snd_rawmidi_t *rawmidi, snd_rawmidi_params
assert(params);
params->buffer_size = page_size();
params->avail_min = 1;
- params->no_active_sensing = 0;
+ params->no_active_sensing = 1;
return 0;
}
@@ -987,6 +987,7 @@ ssize_t snd_rawmidi_read(snd_rawmidi_t *rawmidi, void *buffer, size_t size)
return (rawmidi->ops->read)(rawmidi, buffer, size);
}
+#ifndef DOC_HIDDEN
int snd_rawmidi_conf_generic_id(const char *id)
{
static const char ids[][8] = {
@@ -1002,3 +1003,4 @@ int snd_rawmidi_conf_generic_id(const char *id)
}
return 0;
}
+#endif
diff --git a/src/seq/seq.c b/src/seq/seq.c
index 7b777a1..f5dfe9c 100644
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -463,7 +463,7 @@ void set_tempo(snd_seq_t *handle)
\endcode
For changing the (running) queue's tempo on the fly, you can either
-set the tempo via #snd_seq_queue_tempo() or send a MIDI tempo event
+set the tempo via #snd_seq_set_queue_tempo() or send a MIDI tempo event
to the system timer port. For example,
\code
int change_tempo(snd_seq_t *handle, int q, unsigned int tempo)
@@ -540,7 +540,7 @@ to the specified port.
For example, when a port makes READ subscription
to MIDI input port, this port must have #SND_SEQ_PORT_CAP_WRITE capability,
but no #SND_SEQ_PORT_CAP_SUBS_WRITE capability is required.
-Only MIDI input port must have #SND_SEQ_PORT_SUBS_READ capability.
+Only MIDI input port must have #SND_SEQ_PORT_CAP_SUBS_READ capability.
As default, the connection of ports via the third client is always allowed
if proper read and write (subscription) capabilities are set both to the
@@ -579,7 +579,7 @@ snd_seq_subscribe_port(handle, subs);
When the connection should be exclusively done only between
a certain pair, set <i>exclusive</i> attribute to the subscription
-record before calling #snd_seq_port_subscribe.
+record before calling #snd_seq_subscribe_port.
\code
snd_seq_port_subscribe_set_exclusive(subs, 1);
\endcode
@@ -663,7 +663,7 @@ Assume connection from application 128:0 to 129:0,
and that subscription is done by the third application (130:0).
The sender must have capabilities both
#SND_SEQ_PORT_CAP_READ and
-#SND_SEQ_PORT_SUBS_READ,
+#SND_SEQ_PORT_CAP_SUBS_READ,
and the receiver
#SND_SEQ_PORT_CAP_WRITE and
#SND_SEQ_PORT_CAP_SUBS_WRITE, respectively.
@@ -1471,7 +1471,7 @@ int snd_seq_client_info_get_client(const snd_seq_client_info_t *info)
* \param info client_info container
* \return client type
*
- * The client type is either #SEQ_CLIENT_TYPE_KERNEL or #SEQ_CLIENT_TYPE_USER
+ * The client type is either #SND_SEQ_KERNEL_CLIENT or #SND_SEQ_USER_CLIENT
* for kernel or user client respectively.
*
* \sa snd_seq_get_client_info()
diff --git a/src/seq/seq_midi_event.c b/src/seq/seq_midi_event.c
index b5caa1b..ddaac5a 100644
--- a/src/seq/seq_midi_event.c
+++ b/src/seq/seq_midi_event.c
@@ -129,12 +129,23 @@ static const struct extra_event_list_t {
#endif /* DOC_HIDDEN */
/**
- * \brief Initialize MIDI event parser
- * \param bufsize buffer size for MIDI message
- * \param rdev allocated MIDI event parser
- * \return 0 on success otherwise a negative error code
+ * \brief Creates a MIDI event parser.
+ * \param[in] bufsize Size of the buffer used for encoding; this should be
+ * large enough to hold the largest MIDI message to be
+ * encoded.
+ * \param[out] rdev The new MIDI event parser.
+ * \return Zero on success, otherwise a negative error code.
*
- * Allocates and initializes MIDI event parser.
+ * This function creates and initializes a MIDI parser object that can be used
+ * to convert a MIDI byte stream to sequencer events (encoding) and/or to
+ * convert sequencer events to a MIDI byte stream (decoding).
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
int snd_midi_event_new(size_t bufsize, snd_midi_event_t **rdev)
{
@@ -159,11 +170,13 @@ int snd_midi_event_new(size_t bufsize, snd_midi_event_t **rdev)
}
/**
- * \brief Free MIDI event parser
- * \param dev MIDI event parser
- * \return 0 on success otherwise a negative error code
+ * \brief Frees a MIDI event parser.
+ * \param dev MIDI event parser.
*
- * Frees MIDI event parser.
+ * Frees a MIDI event parser.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
void snd_midi_event_free(snd_midi_event_t *dev)
{
@@ -174,11 +187,15 @@ void snd_midi_event_free(snd_midi_event_t *dev)
}
/**
- * \brief Enable/disable MIDI command merging
- * \param dev MIDI event parser
- * \param on 0 - enable MIDI command merging, 1 - always pass the command
+ * \brief Enables/disables MIDI command merging.
+ * \param dev MIDI event parser.
+ * \param on 0 to enable MIDI command merging,
+ * 1 to always write the command byte.
+ *
+ * This function enables or disables MIDI command merging (running status).
*
- * Enable/disable MIDI command merging
+ * When MIDI command merging is not disabled, #snd_midi_event_decode is allowed
+ * to omit any status byte that is identical to the previous status byte.
*/
void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
{
@@ -196,11 +213,15 @@ inline static void reset_encode(snd_midi_event_t *dev)
}
/**
- * \brief Reset MIDI encode parser
- * \param dev MIDI event parser
- * \return 0 on success otherwise a negative error code
+ * \brief Resets MIDI encode parser.
+ * \param dev MIDI event parser.
*
- * Resets MIDI encode parser
+ * This function resets the MIDI encoder of the parser \a dev.
+ * Any partially encoded MIDI message is dropped,
+ * and running status state is cleared.
+ *
+ * \par Conforming to:
+ * LSB 3.2
*/
void snd_midi_event_reset_encode(snd_midi_event_t *dev)
{
@@ -208,11 +229,15 @@ void snd_midi_event_reset_encode(snd_midi_event_t *dev)
}
/**
- * \brief Reset MIDI decode parser
- * \param dev MIDI event parser
- * \return 0 on success otherwise a negative error code
+ * \brief Resets MIDI decode parser.
+ * \param dev MIDI event parser.
+ *
+ * This function resets the MIDI decoder of the parser \a dev.
+ * The next decoded message does not use running status from before the call to
+ * \a snd_midi_event_reset_decode.
*
- * Resets MIDI decode parser
+ * \par Conforming to:
+ * LSB 3.2
*/
void snd_midi_event_reset_decode(snd_midi_event_t *dev)
{
@@ -220,11 +245,14 @@ void snd_midi_event_reset_decode(snd_midi_event_t *dev)
}
/**
- * \brief Initializes MIDI parsers
- * \param dev MIDI event parser
- * \return 0 on success otherwise a negative error code
+ * \brief Resets MIDI encode/decode parsers.
+ * \param dev MIDI event parser.
+ *
+ * This function resets both encoder and decoder of the MIDI event parser.
+ * \sa snd_midi_event_reset_encode, snd_midi_event_reset_decode
*
- * Initializes MIDI parsers (both encode and decode)
+ * \par Conforming to:
+ * LSB 3.2
*/
void snd_midi_event_init(snd_midi_event_t *dev)
{
@@ -233,12 +261,21 @@ void snd_midi_event_init(snd_midi_event_t *dev)
}
/**
- * \brief Resize MIDI message (event) buffer
- * \param dev MIDI event parser
- * \param bufsize new requested buffer size
- * \return 0 on success otherwise a negative error code
+ * \brief Resizes the MIDI message encoding buffer.
+ * \param dev MIDI event parser.
+ * \param bufsize The new buffer size.
+ * \return Zero on success, otherwise a negative error code.
*
- * Resizes MIDI message (event) buffer.
+ * This function resizes the buffer that is used to hold partially encoded MIDI
+ * messages.
+ *
+ * If there is a partially encoded message in the buffer, it is dropped.
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-ENOMEM<dd>Out of memory.
+ *
+ * \sa snd_midi_event_encode, snd_midi_event_reset_encode
*/
int snd_midi_event_resize_buffer(snd_midi_event_t *dev, size_t bufsize)
{
@@ -258,16 +295,67 @@ int snd_midi_event_resize_buffer(snd_midi_event_t *dev, size_t bufsize)
}
/**
- * \brief Read bytes and encode to sequencer event if finished
- * \param dev MIDI event parser
- * \param buf MIDI byte stream
- * \param count count of bytes of MIDI byte stream to encode
- * \param ev Result - sequencer event
- * \return count of encoded bytes otherwise a negative error code
- *
- * Read bytes and encode to sequencer event if finished.
- * If complete sequencer event is available, ev->type is not
- * equal to #SND_SEQ_EVENT_NONE.
+ * \brief Encodes bytes to sequencer event.
+ * \param[in] dev MIDI event parser.
+ * \param[in] buf Buffer containing bytes of a raw MIDI stream.
+ * \param[in] count Number of bytes in \a buf.
+ * \param[out] ev Sequencer event.
+ * \return The number of bytes consumed, or a negative error code.
+ *
+ * This function tries to use up to \a count bytes from the beginning of the
+ * buffer to encode a sequencer event. If a complete MIDI message has been
+ * encoded, the sequencer event is written to \a ev; otherwise, \a ev->type is
+ * set to #SND_SEQ_EVENT_NONE, and further bytes are required to complete
+ * a message.
+ *
+ * The buffer in \a dev is used to hold any bytes of a not-yet-complete MIDI
+ * message. If a System Exclusive message is larger than the buffer, the
+ * message is split into multiple parts, and a sequencer event is returned at
+ * the end of each part.
+ *
+ * Any bytes that are not part of a valid MIDI message are silently ignored,
+ * i.e., they are consumed without signaling an error.
+ *
+ * When this function returns a system exclusive sequencer event (\a ev->type
+ * is #SND_SEQ_EVENT_SYSEX), the data pointer (\a ev->data.ext.ptr) points into
+ * the MIDI event parser's buffer. Therefore, the sequencer event can only be
+ * used as long as that buffer remains valid, i.e., until the next call to
+ * #snd_midi_event_encode, #snd_midi_event_encode_byte,
+ * #snd_midi_event_resize_buffer, #snd_midi_event_init,
+ * #snd_midi_event_reset_encode, or #snd_midi_event_free for that MIDI event
+ * parser.
+ *
+ * This function can generate any sequencer event that corresponds to a MIDI
+ * message, i.e.:
+ * - #SND_SEQ_EVENT_NOTEOFF
+ * - #SND_SEQ_EVENT_NOTEON
+ * - #SND_SEQ_EVENT_KEYPRESS
+ * - #SND_SEQ_EVENT_CONTROLLER
+ * - #SND_SEQ_EVENT_PGMCHANGE
+ * - #SND_SEQ_EVENT_CHANPRESS
+ * - #SND_SEQ_EVENT_PITCHBEND
+ * - #SND_SEQ_EVENT_SYSEX
+ * - #SND_SEQ_EVENT_QFRAME
+ * - #SND_SEQ_EVENT_SONGPOS
+ * - #SND_SEQ_EVENT_SONGSEL
+ * - #SND_SEQ_EVENT_TUNE_REQUEST
+ * - #SND_SEQ_EVENT_CLOCK
+ * - #SND_SEQ_EVENT_START
+ * - #SND_SEQ_EVENT_CONTINUE
+ * - #SND_SEQ_EVENT_STOP
+ * - #SND_SEQ_EVENT_SENSING
+ * - #SND_SEQ_EVENT_RESET
+ * .
+ * Some implementations may also be able to generate the following events
+ * for a sequence of controller change messages:
+ * - #SND_SEQ_EVENT_CONTROL14
+ * - #SND_SEQ_EVENT_NONREGPARAM
+ * - #SND_SEQ_EVENT_REGPARAM
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_midi_event_new, snd_midi_event_reset_encode, snd_midi_event_encode_byte
*/
long snd_midi_event_encode(snd_midi_event_t *dev, const unsigned char *buf, long count, snd_seq_event_t *ev)
{
@@ -289,13 +377,23 @@ long snd_midi_event_encode(snd_midi_event_t *dev, const unsigned char *buf, long
}
/**
- * \brief Read one byte and encode to sequencer event if finished
- * \param dev MIDI event parser
- * \param c a byte of MIDI stream
- * \param ev Result - sequencer event
- * \return 1 - sequencer event is completed, 0 - next byte is required for completion, otherwise a negative error code
+ * \brief Encodes byte to sequencer event.
+ * \param[in] dev MIDI event parser.
+ * \param[in] c A byte of a raw MIDI stream.
+ * \param[out] ev Sequencer event.
+ * \return 1 if a sequenver event has been completed, 0 if more bytes are
+ * required to complete an event, or a negative error code.
+ *
+ * This function tries to use the byte \a c to encode a sequencer event. If
+ * a complete MIDI message has been encoded, the sequencer event is written to
+ * \a ev; otherwise, further bytes are required to complete a message.
*
- * Read byte and encode to sequencer event if finished.
+ * See also the description of #snd_midi_event_encode.
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_midi_event_new, snd_midi_event_reset_encode, snd_midi_event_encode
*/
int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
{
@@ -405,14 +503,56 @@ static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
}
/**
- * \brief Decode sequencer event to MIDI byte stream
- * \param dev MIDI event parser
- * \param buf Result - MIDI byte stream
- * \param count Available bytes in MIDI byte stream
- * \param ev Event to decode
- * \return count of decoded bytes otherwise a negative error code
- *
- * Decode sequencer event to MIDI byte stream.
+ * \brief Decodes sequencer event to MIDI byte stream.
+ * \param[in] dev MIDI event parser.
+ * \param[out] buf Buffer for the resulting MIDI byte stream.
+ * \param[in] count Number of bytes in \a buf.
+ * \param[in] ev The sequencer event to decode.
+ * \return The number of bytes written to \a buf, or a negative error code.
+ *
+ * This function tries to decode the sequencer event into one or more MIDI
+ * messages, and writes the raw MIDI byte(s) into \a buf.
+ *
+ * The generated MIDI messages may use running status, unless disabled with
+ * #snd_midi_event_no_status.
+ *
+ * The required buffer size for a sequencer event it as most 12 bytes, except
+ * for System Exclusive events (\a ev->type == #SND_SEQ_EVENT_SYSEX) which can
+ * have any length (as specified by \a ev->data.ext.len).
+ *
+ * The following sequencer events correspond to MIDI messages:
+ * - #SND_SEQ_EVENT_NOTEOFF
+ * - #SND_SEQ_EVENT_NOTEON
+ * - #SND_SEQ_EVENT_KEYPRESS
+ * - #SND_SEQ_EVENT_CONTROLLER
+ * - #SND_SEQ_EVENT_PGMCHANGE
+ * - #SND_SEQ_EVENT_CHANPRESS
+ * - #SND_SEQ_EVENT_PITCHBEND
+ * - #SND_SEQ_EVENT_SYSEX
+ * - #SND_SEQ_EVENT_QFRAME
+ * - #SND_SEQ_EVENT_SONGPOS
+ * - #SND_SEQ_EVENT_SONGSEL
+ * - #SND_SEQ_EVENT_TUNE_REQUEST
+ * - #SND_SEQ_EVENT_CLOCK
+ * - #SND_SEQ_EVENT_START
+ * - #SND_SEQ_EVENT_CONTINUE
+ * - #SND_SEQ_EVENT_STOP
+ * - #SND_SEQ_EVENT_SENSING
+ * - #SND_SEQ_EVENT_RESET
+ * - #SND_SEQ_EVENT_CONTROL14
+ * - #SND_SEQ_EVENT_NONREGPARAM
+ * - #SND_SEQ_EVENT_REGPARAM
+ *
+ * \par Errors:
+ * <dl>
+ * <dt>-EINVAL<dd>\a ev is not a valid sequencer event.
+ * <dt>-ENOENT<dd>The sequencer event does not correspond to one or more MIDI messages.
+ * <dt>-ENOMEM<dd>The MIDI message(s) would not fit into \a count bytes.
+ *
+ * \par Conforming to:
+ * LSB 3.2
+ *
+ * \sa snd_midi_event_reset_decode, snd_midi_event_no_status
*/
long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, const snd_seq_event_t *ev)
{
@@ -442,6 +582,7 @@ long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count
if (cmd == MIDI_CMD_COMMON_SYSEX) {
+ snd_midi_event_reset_decode(dev);
qlen = ev->data.ext.len;
if (count < qlen)
return -ENOMEM;
@@ -566,10 +707,10 @@ static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int coun
if (dev->nostat && count < 12)
return -ENOMEM;
cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
- bytes[0] = ev->data.control.param & 0x007f;
- bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
- bytes[2] = ev->data.control.value & 0x007f;
- bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+ bytes[0] = (ev->data.control.param & 0x3f80) >> 7;
+ bytes[1] = ev->data.control.param & 0x007f;
+ bytes[2] = (ev->data.control.value & 0x3f80) >> 7;
+ bytes[3] = ev->data.control.value & 0x007f;
if (cmd != dev->lastcmd && !dev->nostat) {
if (count < 9)
return -ENOMEM;
diff --git a/src/timer/timer_local.h b/src/timer/timer_local.h
index 19cadfe..8040b05 100644
--- a/src/timer/timer_local.h
+++ b/src/timer/timer_local.h
@@ -64,7 +64,7 @@ struct _snd_timer_query {
snd_timer_type_t type;
int mode;
int poll_fd;
- snd_timer_query_ops_t *ops;
+ const snd_timer_query_ops_t *ops;
void *private_data;
};
#endif /* DOC_HIDDEN */
diff --git a/test/Makefile.am b/test/Makefile.am
index 2d7e92b..ae524a4 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS=. lsb
+
check_PROGRAMS=control pcm pcm_min latency seq \
playmidi1 timer rawmidi midiloop \
oldapi queue_timer namehint client_event_filter
diff --git a/test/lsb/Makefile.am b/test/lsb/Makefile.am
new file mode 100644
index 0000000..ceb4d71
--- /dev/null
+++ b/test/lsb/Makefile.am
@@ -0,0 +1,7 @@
+TESTS = config
+TESTS += midi_event
+check_PROGRAMS = $(TESTS)
+noinst_HEADERS = test.h
+
+AM_CFLAGS = -Wall -pipe
+LDADD = ../../src/libasound.la
diff --git a/test/lsb/config.c b/test/lsb/config.c
new file mode 100644
index 0000000..3503798
--- /dev/null
+++ b/test/lsb/config.c
@@ -0,0 +1,582 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "test.h"
+
+static int configs_equal(snd_config_t *c1, snd_config_t *c2);
+
+/* checks if all children of c1 also occur in c2 */
+static int subset_of(snd_config_t *c1, snd_config_t *c2)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *e1, *e2;
+ const char *id;
+
+ snd_config_for_each(i, next, c1) {
+ e1 = snd_config_iterator_entry(i);
+ if (snd_config_get_id(e1, &id) < 0 || !id)
+ return 0;
+ if (snd_config_search(c2, id, &e2) < 0)
+ return 0;
+ if (!configs_equal(e1, e2))
+ return 0;
+ }
+ return 1;
+}
+
+/* checks if two configuration nodes are equal */
+static int configs_equal(snd_config_t *c1, snd_config_t *c2)
+{
+ long i1, i2;
+ long long i641, i642;
+ const char *s1, *s2;
+
+ if (snd_config_get_type(c1) != snd_config_get_type(c2))
+ return 0;
+ switch (snd_config_get_type(c1)) {
+ case SND_CONFIG_TYPE_INTEGER:
+ return snd_config_get_integer(c1, &i1) >= 0 &&
+ snd_config_get_integer(c2, &i2) >= 0 &&
+ i1 == i2;
+ case SND_CONFIG_TYPE_INTEGER64:
+ return snd_config_get_integer64(c1, &i641) >= 0 &&
+ snd_config_get_integer64(c2, &i642) >= 0 &&
+ i641 == i642;
+ case SND_CONFIG_TYPE_STRING:
+ return snd_config_get_string(c1, &s1) >= 0 &&
+ snd_config_get_string(c2, &s2) >= 0 &&
+ !s1 == !s2 &&
+ (!s1 || !strcmp(s1, s2));
+ case SND_CONFIG_TYPE_COMPOUND:
+ return subset_of(c1, c2) && subset_of(c2, c1);
+ default:
+ fprintf(stderr, "unknown configuration node type %d\n",
+ (int)snd_config_get_type(c1));
+ return 0;
+ }
+}
+
+static void test_top(void)
+{
+ snd_config_t *top;
+ const char *id;
+
+ if (ALSA_CHECK(snd_config_top(&top)) < 0)
+ return;
+
+ TEST_CHECK(snd_config_get_type(top) == SND_CONFIG_TYPE_COMPOUND);
+ TEST_CHECK(snd_config_iterator_first(top) == snd_config_iterator_end(top));
+ TEST_CHECK(snd_config_get_id(top, &id) >= 0 && id == NULL);
+
+ ALSA_CHECK(snd_config_delete(top));
+}
+
+static void test_load(void)
+{
+ const char *config_text1 = "s='world';";
+ const char *config_text2 = "c.elem 0";
+ snd_config_t *loaded, *made, *c, *c2;
+ snd_input_t *input;
+
+ ALSA_CHECK(snd_config_top(&loaded));
+ ALSA_CHECK(snd_config_imake_integer(&c, "i", 42));
+ ALSA_CHECK(snd_config_add(loaded, c));
+ ALSA_CHECK(snd_config_imake_string(&c, "s", "hello"));
+ ALSA_CHECK(snd_config_add(loaded, c));
+
+ ALSA_CHECK(snd_config_top(&made));
+ ALSA_CHECK(snd_config_imake_string(&c, "s", "world"));
+ ALSA_CHECK(snd_config_add(made, c));
+ ALSA_CHECK(snd_config_imake_integer(&c, "i", 42));
+ ALSA_CHECK(snd_config_add(made, c));
+
+ ALSA_CHECK(snd_input_buffer_open(&input, config_text1, strlen(config_text1)));
+ ALSA_CHECK(snd_config_load(loaded, input));
+ ALSA_CHECK(snd_input_close(input));
+ TEST_CHECK(configs_equal(loaded, made));
+
+ ALSA_CHECK(snd_config_make_compound(&c, "c", 0));
+ ALSA_CHECK(snd_config_add(made, c));
+ ALSA_CHECK(snd_config_imake_integer(&c2, "elem", 0));
+ ALSA_CHECK(snd_config_add(c, c2));
+
+ ALSA_CHECK(snd_input_buffer_open(&input, config_text2, strlen(config_text2)));
+ ALSA_CHECK(snd_config_load(loaded, input));
+ ALSA_CHECK(snd_input_close(input));
+ TEST_CHECK(configs_equal(loaded, made));
+
+ ALSA_CHECK(snd_config_delete(loaded));
+ ALSA_CHECK(snd_config_delete(made));
+}
+
+static void test_save(void)
+{
+ const char *text =
+ "a.b.c 'x.y.z'\n"
+ "xxx = yyy;\n"
+ "q { qq=qqq }\n"
+ "a [ 1 2 3 4 5 '...' ]\n";
+ snd_config_t *orig, *saved;
+ snd_input_t *input;
+ snd_output_t *output;
+ char *buf;
+ size_t buf_size;
+
+ ALSA_CHECK(snd_input_buffer_open(&input, text, strlen(text)));
+ ALSA_CHECK(snd_config_top(&orig));
+ ALSA_CHECK(snd_config_load(orig, input));
+ ALSA_CHECK(snd_input_close(input));
+ ALSA_CHECK(snd_output_buffer_open(&output));
+ ALSA_CHECK(snd_config_save(orig, output));
+ buf_size = snd_output_buffer_string(output, &buf);
+ ALSA_CHECK(snd_input_buffer_open(&input, buf, buf_size));
+ ALSA_CHECK(snd_config_top(&saved));
+ ALSA_CHECK(snd_config_load(saved, input));
+ ALSA_CHECK(snd_input_close(input));
+ ALSA_CHECK(snd_output_close(output));
+ TEST_CHECK(configs_equal(orig, saved));
+ ALSA_CHECK(snd_config_delete(orig));
+ ALSA_CHECK(snd_config_delete(saved));
+}
+
+static void test_update(void)
+{
+ ALSA_CHECK(snd_config_update_free_global());
+ TEST_CHECK(snd_config == NULL);
+ ALSA_CHECK(snd_config_update());
+ TEST_CHECK(snd_config_get_type(snd_config) == SND_CONFIG_TYPE_COMPOUND);
+ ALSA_CHECK(snd_config_update());
+ TEST_CHECK(snd_config_get_type(snd_config) == SND_CONFIG_TYPE_COMPOUND);
+ ALSA_CHECK(snd_config_update_free_global());
+ TEST_CHECK(snd_config == NULL);
+}
+
+static void test_search(void)
+{
+ const char *text =
+ "a 42\n"
+ "b {\n"
+ " c cee\n"
+ " d {\n"
+ " e 2.71828\n"
+ " }\n"
+ "}\n";
+ snd_input_t *input;
+ snd_config_t *top, *c;
+ const char *id;
+
+ ALSA_CHECK(snd_input_buffer_open(&input, text, strlen(text)));
+ ALSA_CHECK(snd_config_top(&top));
+ ALSA_CHECK(snd_config_load(top, input));
+ ALSA_CHECK(snd_input_close(input));
+
+ ALSA_CHECK(snd_config_search(top, "a", &c));
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "a"));
+ ALSA_CHECK(snd_config_search(top, "b.d.e", &c));
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "e"));
+ ALSA_CHECK(snd_config_search(top, "b.c", NULL));
+ TEST_CHECK(snd_config_search(top, "x", NULL) == -ENOENT);
+ TEST_CHECK(snd_config_search(top, "b.y", &c) == -ENOENT);
+ TEST_CHECK(snd_config_search(top, "a.z", &c) == -ENOENT);
+
+ ALSA_CHECK(snd_config_delete(top));
+}
+
+static void test_searchv(void)
+{
+ const char *text =
+ "a 42\n"
+ "b {\n"
+ " c cee\n"
+ " d {\n"
+ " e 2.71828\n"
+ " }\n"
+ "}\n";
+ snd_input_t *input;
+ snd_config_t *top, *c;
+ const char *id;
+
+ ALSA_CHECK(snd_input_buffer_open(&input, text, strlen(text)));
+ ALSA_CHECK(snd_config_top(&top));
+ ALSA_CHECK(snd_config_load(top, input));
+ ALSA_CHECK(snd_input_close(input));
+
+ ALSA_CHECK(snd_config_searchv(top, &c, "a", NULL));
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "a"));
+ ALSA_CHECK(snd_config_searchv(top, &c, "b", "d.e", NULL));
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "e"));
+ ALSA_CHECK(snd_config_searchv(top, NULL, "b.c", NULL));
+ TEST_CHECK(snd_config_searchv(top, NULL, "x", NULL) == -ENOENT);
+ TEST_CHECK(snd_config_searchv(top, &c, "b.y", NULL) == -ENOENT);
+ TEST_CHECK(snd_config_searchv(top, &c, "a", "z", NULL) == -ENOENT);
+
+ ALSA_CHECK(snd_config_delete(top));
+}
+
+static void test_add(void)
+{
+ snd_config_t *c1, *c2, *c3, *c4, *c5;
+ snd_config_iterator_t i;
+ unsigned int count = 0;
+
+ ALSA_CHECK(snd_config_top(&c1));
+ ALSA_CHECK(snd_config_imake_integer(&c2, "c2", 0xc2));
+ ALSA_CHECK(snd_config_add(c1, c2));
+ ALSA_CHECK(snd_config_imake_string(&c3, "c3", "c3"));
+ ALSA_CHECK(snd_config_add(c1, c3));
+ for (i = snd_config_iterator_first(c1);
+ i != snd_config_iterator_end(c1);
+ i = snd_config_iterator_next(i))
+ ++count;
+ TEST_CHECK(count == 2);
+ ALSA_CHECK(snd_config_search(c1, "c2", &c2));
+ ALSA_CHECK(snd_config_search(c1, "c3", &c3));
+ ALSA_CHECK(snd_config_top(&c4));
+ TEST_CHECK(snd_config_add(c1, c4) == -EINVAL);
+ ALSA_CHECK(snd_config_imake_integer(&c5, "c5", 5));
+ ALSA_CHECK(snd_config_add(c4, c5));
+ TEST_CHECK(snd_config_add(c1, c5) == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c4));
+ ALSA_CHECK(snd_config_imake_integer(&c3, "c3", 333));
+ TEST_CHECK(snd_config_add(c1, c3) == -EEXIST);
+ ALSA_CHECK(snd_config_delete(c3));
+ ALSA_CHECK(snd_config_delete(c1));
+}
+
+static void test_delete(void)
+{
+ snd_config_t *c;
+
+ ALSA_CHECK(snd_config_top(&c));
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_imake_string(&c, "s", "..."));
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_copy(void)
+{
+ snd_config_t *c1, *c2, *c3;
+ long value;
+
+ ALSA_CHECK(snd_config_imake_integer(&c1, "c1", 123));
+ ALSA_CHECK(snd_config_copy(&c2, c1));
+ ALSA_CHECK(snd_config_set_integer(c1, 456));
+ TEST_CHECK(snd_config_get_type(c2) == SND_CONFIG_TYPE_INTEGER);
+ ALSA_CHECK(snd_config_get_integer(c2, &value));
+ TEST_CHECK(value == 123);
+ ALSA_CHECK(snd_config_delete(c1));
+ ALSA_CHECK(snd_config_delete(c2));
+ ALSA_CHECK(snd_config_top(&c1));
+ ALSA_CHECK(snd_config_imake_integer(&c2, "a", 1));
+ ALSA_CHECK(snd_config_add(c1, c2));
+ ALSA_CHECK(snd_config_copy(&c3, c1));
+ ALSA_CHECK(snd_config_set_integer(c2, 2));
+ TEST_CHECK(!configs_equal(c1, c3));
+ ALSA_CHECK(snd_config_search(c3, "a", &c2));
+ ALSA_CHECK(snd_config_set_integer(c2, 2));
+ TEST_CHECK(configs_equal(c1, c3));
+ ALSA_CHECK(snd_config_delete(c1));
+ ALSA_CHECK(snd_config_delete(c3));
+}
+
+static void test_make_integer(void)
+{
+ snd_config_t *c;
+ const char *id;
+ long value;
+
+ ALSA_CHECK(snd_config_make_integer(&c, "i"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_INTEGER);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "i"));
+ ALSA_CHECK(snd_config_get_integer(c, &value));
+ TEST_CHECK(value == 0);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_make_integer64(void)
+{
+ snd_config_t *c;
+ const char *id;
+ long long value;
+
+ ALSA_CHECK(snd_config_make_integer64(&c, "i"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_INTEGER64);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "i"));
+ ALSA_CHECK(snd_config_get_integer64(c, &value));
+ TEST_CHECK(value == 0);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_make_string(void)
+{
+ snd_config_t *c;
+ const char *id;
+ const char *value;
+
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_STRING);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "s"));
+ ALSA_CHECK(snd_config_get_string(c, &value));
+ TEST_CHECK(value == NULL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_make_compound(void)
+{
+ snd_config_t *c;
+ const char *id;
+
+ ALSA_CHECK(snd_config_make_compound(&c, "c", 0));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_COMPOUND);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "c"));
+ TEST_CHECK(snd_config_iterator_first(c) == snd_config_iterator_end(c));
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_imake_integer(void)
+{
+ snd_config_t *c;
+ const char *id;
+ long value;
+
+ ALSA_CHECK(snd_config_imake_integer(&c, "i", 123));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_INTEGER);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "i"));
+ ALSA_CHECK(snd_config_get_integer(c, &value));
+ TEST_CHECK(value == 123);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_imake_integer64(void)
+{
+ snd_config_t *c;
+ const char *id;
+ long long value;
+
+ ALSA_CHECK(snd_config_imake_integer64(&c, "i", 123456789012345LL));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_INTEGER64);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "i"));
+ ALSA_CHECK(snd_config_get_integer64(c, &value));
+ TEST_CHECK(value == 123456789012345LL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_imake_string(void)
+{
+ snd_config_t *c;
+ const char *id;
+ const char *value;
+
+ ALSA_CHECK(snd_config_imake_string(&c, "s", "xyzzy"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_STRING);
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "s"));
+ ALSA_CHECK(snd_config_get_string(c, &value));
+ TEST_CHECK(!strcmp(value, "xyzzy"));
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_get_type(void)
+{
+ snd_config_t *c;
+
+ ALSA_CHECK(snd_config_top(&c));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_COMPOUND);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_integer(&c, "i"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_INTEGER);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ TEST_CHECK(snd_config_get_type(c) == SND_CONFIG_TYPE_STRING);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_set_integer(void)
+{
+ snd_config_t *c;
+ long value;
+
+ ALSA_CHECK(snd_config_make_integer(&c, "i"));
+ ALSA_CHECK(snd_config_set_integer(c, 123));
+ ALSA_CHECK(snd_config_get_integer(c, &value));
+ TEST_CHECK(value == 123);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ TEST_CHECK(snd_config_set_integer(c, 123) == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_set_integer64(void)
+{
+ snd_config_t *c;
+ long long value;
+
+ ALSA_CHECK(snd_config_make_integer64(&c, "i"));
+ ALSA_CHECK(snd_config_set_integer64(c, 123456789012345LL));
+ ALSA_CHECK(snd_config_get_integer64(c, &value));
+ TEST_CHECK(value == 123456789012345LL);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ TEST_CHECK(snd_config_set_integer64(c, 123) == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_set_string(void)
+{
+ snd_config_t *c;
+ const char *value;
+
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ ALSA_CHECK(snd_config_set_string(c, "string"));
+ ALSA_CHECK(snd_config_get_string(c, &value));
+ TEST_CHECK(!strcmp(value, "string"));
+ ALSA_CHECK(snd_config_set_string(c, NULL));
+ ALSA_CHECK(snd_config_get_string(c, &value));
+ TEST_CHECK(value == NULL);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_integer(&c, "i"));
+ TEST_CHECK(snd_config_set_string(c, "") == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_set_ascii(void)
+{
+ snd_config_t *c;
+ const char *s;
+ long i;
+
+ ALSA_CHECK(snd_config_make_string(&c, "s"));
+ ALSA_CHECK(snd_config_set_ascii(c, "foo"));
+ ALSA_CHECK(snd_config_get_string(c, &s));
+ TEST_CHECK(!strcmp(s, "foo"));
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_make_integer(&c, "i"));
+ ALSA_CHECK(snd_config_set_ascii(c, "23"));
+ ALSA_CHECK(snd_config_get_integer(c, &i));
+ TEST_CHECK(i == 23);
+ TEST_CHECK(snd_config_set_ascii(c, "half blue") == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_top(&c));
+ TEST_CHECK(snd_config_set_ascii(c, "0") == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_get_id(void)
+{
+ snd_config_t *c;
+ const char *id;
+
+ ALSA_CHECK(snd_config_make_integer(&c, "my_id"));
+ ALSA_CHECK(snd_config_get_id(c, &id));
+ TEST_CHECK(!strcmp(id, "my_id"));
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+#define test_get_integer test_set_integer
+#define test_get_integer64 test_set_integer64
+#define test_get_string test_set_string
+
+static void test_get_ascii(void)
+{
+ snd_config_t *c;
+ char *value;
+
+ ALSA_CHECK(snd_config_imake_integer(&c, "i", 123));
+ ALSA_CHECK(snd_config_get_ascii(c, &value));
+ TEST_CHECK(!strcmp(value, "123"));
+ free(value);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_imake_string(&c, "s", "bar"));
+ ALSA_CHECK(snd_config_get_ascii(c, &value));
+ TEST_CHECK(!strcmp(value, "bar"));
+ free(value);
+ ALSA_CHECK(snd_config_delete(c));
+ ALSA_CHECK(snd_config_top(&c));
+ TEST_CHECK(snd_config_get_ascii(c, &value) == -EINVAL);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_iterators(void)
+{
+ snd_config_t *c, *c2;
+ snd_config_iterator_t i;
+ long v;
+
+ ALSA_CHECK(snd_config_top(&c));
+ i = snd_config_iterator_first(c);
+ TEST_CHECK(i == snd_config_iterator_end(c));
+ ALSA_CHECK(snd_config_imake_integer(&c2, "one", 1));
+ ALSA_CHECK(snd_config_add(c, c2));
+ i = snd_config_iterator_first(c);
+ TEST_CHECK(i != snd_config_iterator_end(c));
+ c2 = snd_config_iterator_entry(i);
+ ALSA_CHECK(snd_config_get_integer(c2, &v));
+ TEST_CHECK(v == 1);
+ i = snd_config_iterator_next(i);
+ TEST_CHECK(i == snd_config_iterator_end(c));
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+static void test_for_each(void)
+{
+ snd_config_t *c, *c2;
+ snd_config_iterator_t i, next;
+ long v;
+ unsigned int count = 0;
+
+ ALSA_CHECK(snd_config_top(&c));
+ ALSA_CHECK(snd_config_imake_integer(&c2, "one", 1));
+ ALSA_CHECK(snd_config_add(c, c2));
+ snd_config_for_each(i, next, c) {
+ TEST_CHECK(i != snd_config_iterator_end(c));
+ c2 = snd_config_iterator_entry(i);
+ ALSA_CHECK(snd_config_get_integer(c2, &v));
+ TEST_CHECK(v == 1);
+ ++count;
+ }
+ TEST_CHECK(count == 1);
+ ALSA_CHECK(snd_config_delete(c));
+}
+
+int main(void)
+{
+ test_top();
+ test_load();
+ test_save();
+ test_update();
+ test_search();
+ test_searchv();
+ test_add();
+ test_delete();
+ test_copy();
+ test_make_integer();
+ test_make_integer64();
+ test_make_string();
+ test_make_compound();
+ test_imake_integer();
+ test_imake_integer64();
+ test_imake_string();
+ test_get_type();
+ test_set_integer();
+ test_set_integer64();
+ test_set_string();
+ test_set_ascii();
+ test_get_id();
+ test_get_integer();
+ test_get_integer64();
+ test_get_string();
+ test_get_ascii();
+ test_iterators();
+ test_for_each();
+ return TEST_EXIT_CODE();
+}
diff --git a/test/lsb/midi_event.c b/test/lsb/midi_event.c
new file mode 100644
index 0000000..2ae90a7
--- /dev/null
+++ b/test/lsb/midi_event.c
@@ -0,0 +1,371 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+#include "test.h"
+
+/*
+ * Checks whether the regular expression matches the entire MIDI data, printed
+ * as hex.
+ */
+static int midi_matches_regex(unsigned char *midi, int count, const char *regex)
+{
+ char *text;
+ regex_t re;
+ regmatch_t match;
+ int i;
+
+ text = malloc(2 * count + 1);
+ if (!text)
+ return 0;
+ for (i = 0; i < count; ++i)
+ sprintf(text + 2 * i, "%02x", midi[i]);
+ if (regcomp(&re, regex, REG_EXTENDED) != 0) {
+ free(text);
+ return 0;
+ }
+ i = regexec(&re, text, 1, &match, 0);
+ i = i == 0 && match.rm_so == 0 && match.rm_eo == strlen(text);
+ regfree(&re);
+ free(text);
+ return i;
+}
+
+static void test_decode(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+ unsigned char buf[50];
+ int count;
+
+ if (ALSA_CHECK(snd_midi_event_new(256 /* ? */, &midi_event)) < 0)
+ return;
+
+#define DECODE() snd_midi_event_decode(midi_event, buf, sizeof(buf), &ev)
+#define BUF_MATCHES(str) midi_matches_regex(buf, count, str)
+#define DECODES_TO(str) ((count = DECODE()), BUF_MATCHES(str))
+
+ snd_seq_ev_clear(&ev);
+
+ snd_seq_ev_set_fixed(&ev);
+ ev.type = SND_SEQ_EVENT_NONE;
+ TEST_CHECK(DECODE() == -ENOENT);
+
+ snd_seq_ev_set_noteoff(&ev, 1, 2, 3);
+ TEST_CHECK(DECODES_TO("810203"));
+
+ snd_seq_ev_set_noteon(&ev, 4, 5, 6);
+ TEST_CHECK(DECODES_TO("940506"));
+
+ snd_seq_ev_set_keypress(&ev, 7, 8, 9);
+ TEST_CHECK(DECODES_TO("a70809"));
+
+ snd_seq_ev_set_controller(&ev, 10, 11, 12);
+ TEST_CHECK(DECODES_TO("ba0b0c"));
+
+ snd_seq_ev_set_pgmchange(&ev, 13, 14);
+ TEST_CHECK(DECODES_TO("cd0e"));
+
+ snd_seq_ev_set_chanpress(&ev, 15, 16);
+ TEST_CHECK(DECODES_TO("df10"));
+
+ snd_seq_ev_set_pitchbend(&ev, 1, 0x222);
+ TEST_CHECK(DECODES_TO("e12244"));
+
+ snd_seq_ev_set_sysex(&ev, 6, "\xf0\x7e\x7f\x06\x01\xf7");
+ TEST_CHECK(DECODES_TO("f07e7f0601f7"));
+
+ snd_seq_ev_set_fixed(&ev);
+ ev.type = SND_SEQ_EVENT_QFRAME;
+ ev.data.control.value = 3;
+ TEST_CHECK(DECODES_TO("f103"));
+
+ ev.type = SND_SEQ_EVENT_SONGPOS;
+ ev.data.control.value = 0x444;
+ TEST_CHECK(DECODES_TO("f24408"));
+
+ ev.type = SND_SEQ_EVENT_SONGSEL;
+ ev.data.control.value = 5;
+ TEST_CHECK(DECODES_TO("f305"));
+
+ ev.type = SND_SEQ_EVENT_TUNE_REQUEST;
+ TEST_CHECK(DECODES_TO("f6"));
+
+ ev.type = SND_SEQ_EVENT_CLOCK;
+ TEST_CHECK(DECODES_TO("f8"));
+
+ ev.type = SND_SEQ_EVENT_START;
+ TEST_CHECK(DECODES_TO("fa"));
+
+ ev.type = SND_SEQ_EVENT_CONTINUE;
+ TEST_CHECK(DECODES_TO("fb"));
+
+ ev.type = SND_SEQ_EVENT_STOP;
+ TEST_CHECK(DECODES_TO("fc"));
+
+ ev.type = SND_SEQ_EVENT_SENSING;
+ TEST_CHECK(DECODES_TO("fe"));
+
+ ev.type = SND_SEQ_EVENT_RESET;
+ TEST_CHECK(DECODES_TO("ff"));
+
+ ev.type = SND_SEQ_EVENT_CONTROL14;
+ ev.data.control.channel = 6;
+ ev.data.control.param = 7;
+ ev.data.control.value = 0x888;
+ /*
+ * This regular expression catches all allowed combinations of LSB/MSB
+ * order and running status.
+ */
+ TEST_CHECK(DECODES_TO("b6(0711(b6)?2708|2708(b6)?0711)"));
+
+ ev.type = SND_SEQ_EVENT_NONREGPARAM;
+ ev.data.control.channel = 9;
+ ev.data.control.param = 0xaaa;
+ ev.data.control.value = 0xbbb;
+ TEST_CHECK(DECODES_TO("b9(622a(b9)?6315|6315(b9)?622a)(b9)?(0617(b9)?263b|263b(b9)?0617)"));
+
+ ev.type = SND_SEQ_EVENT_REGPARAM;
+ ev.data.control.channel = 12;
+ ev.data.control.param = 0xddd;
+ ev.data.control.value = 0xeee;
+ TEST_CHECK(DECODES_TO("bc(645d(bc)?651b|651b(bc)?645d)(bc)?(061d(bc)?266e|266e(bc)?061d)"));
+
+ /* no running status after SysEx */
+ snd_seq_ev_set_pgmchange(&ev, 0, 0x11);
+ TEST_CHECK(DECODES_TO("c011"));
+ snd_seq_ev_set_sysex(&ev, 6, "\xf0\x7e\x7f\x09\x02\xf7");
+ TEST_CHECK(DECODES_TO("f07e7f0902f7"));
+ snd_seq_ev_set_pgmchange(&ev, 0, 0x11);
+ TEST_CHECK(DECODES_TO("c011"));
+
+ /* no running status for non-realtime common messages */
+ ev.type = SND_SEQ_EVENT_QFRAME;
+ ev.data.control.value = 0x11;
+ TEST_CHECK(DECODES_TO("f111"));
+ TEST_CHECK(DECODES_TO("f111"));
+
+ /* buffer overflow */
+ TEST_CHECK(snd_midi_event_decode(midi_event, buf, 1, &ev) == -ENOMEM);
+
+ snd_midi_event_free(midi_event);
+}
+
+static void test_reset_decode(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+ unsigned char buf[50];
+ int count;
+
+ if (ALSA_CHECK(snd_midi_event_new(256 /* ? */, &midi_event)) < 0)
+ return;
+
+ snd_seq_ev_clear(&ev);
+
+ snd_seq_ev_set_noteon(&ev, 1, 2, 3);
+ TEST_CHECK(DECODES_TO("910203"));
+
+ snd_midi_event_reset_decode(midi_event);
+
+ TEST_CHECK(DECODES_TO("910203"));
+
+ snd_midi_event_free(midi_event);
+}
+
+static void test_encode(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+
+ if (ALSA_CHECK(snd_midi_event_new(256, &midi_event)) < 0)
+ return;
+
+#define ENCODE(str) snd_midi_event_encode(midi_event, \
+ (const unsigned char *)str, \
+ sizeof(str) - 1, &ev)
+ TEST_CHECK(ENCODE("\x81\x02\x03") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NOTEOFF);
+ TEST_CHECK((ev.flags & SND_SEQ_EVENT_LENGTH_MASK) == SND_SEQ_EVENT_LENGTH_FIXED);
+ TEST_CHECK(ev.data.note.channel == 1);
+ TEST_CHECK(ev.data.note.note == 2);
+ TEST_CHECK(ev.data.note.velocity == 3);
+
+ TEST_CHECK(ENCODE("\x94\x05\x06") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NOTEON);
+ TEST_CHECK(ev.data.note.channel == 4);
+ TEST_CHECK(ev.data.note.note == 5);
+ TEST_CHECK(ev.data.note.velocity == 6);
+
+ TEST_CHECK(ENCODE("\xa7\x08\x09") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_KEYPRESS);
+ TEST_CHECK(ev.data.note.channel == 7);
+ TEST_CHECK(ev.data.note.note == 8);
+ TEST_CHECK(ev.data.note.velocity == 9);
+
+ TEST_CHECK(ENCODE("\xba\x0b\x0c") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CONTROLLER);
+ TEST_CHECK(ev.data.control.channel == 10);
+ TEST_CHECK(ev.data.control.param == 11);
+ TEST_CHECK(ev.data.control.value == 12);
+
+ TEST_CHECK(ENCODE("\xcd\x0e") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_PGMCHANGE);
+ TEST_CHECK(ev.data.control.channel == 13);
+ TEST_CHECK(ev.data.control.value == 14);
+
+ TEST_CHECK(ENCODE("\xdf\x10") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CHANPRESS);
+ TEST_CHECK(ev.data.control.channel == 15);
+ TEST_CHECK(ev.data.control.value == 16);
+
+ TEST_CHECK(ENCODE("\xe1\x22\x33") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_PITCHBEND);
+ TEST_CHECK(ev.data.control.channel == 1);
+ TEST_CHECK(ev.data.control.value == -1630);
+
+ TEST_CHECK(ENCODE("\xf0\x7f\x7f\x04\x01\x7f\x7f\xf7") == 8);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_SYSEX);
+ TEST_CHECK((ev.flags & SND_SEQ_EVENT_LENGTH_MASK) == SND_SEQ_EVENT_LENGTH_VARIABLE);
+ TEST_CHECK(ev.data.ext.len == 8);
+ TEST_CHECK(!memcmp(ev.data.ext.ptr, "\xf0\x7f\x7f\x04\x01\x7f\x7f\xf7", 8));
+
+ TEST_CHECK(ENCODE("\xf1\x04") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_QFRAME);
+ TEST_CHECK(ev.data.control.value == 4);
+
+ TEST_CHECK(ENCODE("\xf2\x55\x66") == 3);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_SONGPOS);
+ TEST_CHECK(ev.data.control.value == 13141);
+
+ TEST_CHECK(ENCODE("\xf3\x07") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_SONGSEL);
+ TEST_CHECK(ev.data.control.value == 7);
+
+ TEST_CHECK(ENCODE("\xf6") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_TUNE_REQUEST);
+
+ TEST_CHECK(ENCODE("\xf8") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CLOCK);
+
+ TEST_CHECK(ENCODE("\xfa") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_START);
+
+ TEST_CHECK(ENCODE("\xfb") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CONTINUE);
+
+ TEST_CHECK(ENCODE("\xfc") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_STOP);
+
+ TEST_CHECK(ENCODE("\xfe") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_SENSING);
+
+ TEST_CHECK(ENCODE("\xff") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_RESET);
+
+ TEST_CHECK(ENCODE("\xc1\xf8") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CLOCK);
+ TEST_CHECK(ENCODE("\x22") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_PGMCHANGE);
+ TEST_CHECK(ev.data.control.channel == 1);
+ TEST_CHECK(ev.data.control.value == 0x22);
+ TEST_CHECK(ENCODE("\xf8") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CLOCK);
+ TEST_CHECK(ENCODE("\x33") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_PGMCHANGE);
+ TEST_CHECK(ev.data.control.channel == 1);
+ TEST_CHECK(ev.data.control.value == 0x33);
+
+ TEST_CHECK(ENCODE("\xc1\xf6") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_TUNE_REQUEST);
+ TEST_CHECK(ENCODE("\x44\x44") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NONE);
+
+ snd_midi_event_free(midi_event);
+}
+
+static void test_reset_encode(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+
+ if (ALSA_CHECK(snd_midi_event_new(256, &midi_event)) < 0)
+ return;
+
+ TEST_CHECK(ENCODE("\x91\x02") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NONE);
+
+ snd_midi_event_reset_encode(midi_event);
+
+ TEST_CHECK(ENCODE("\x03") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NONE);
+
+ snd_midi_event_free(midi_event);
+}
+
+static void test_init(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+ unsigned char buf[50];
+ int count;
+
+ if (ALSA_CHECK(snd_midi_event_new(256, &midi_event)) < 0)
+ return;
+
+ snd_seq_ev_set_noteon(&ev, 1, 2, 3);
+ TEST_CHECK(DECODES_TO("910203"));
+
+ TEST_CHECK(ENCODE("\x94\x05") == 2);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NONE);
+
+ snd_midi_event_init(midi_event);
+
+ snd_seq_ev_set_noteon(&ev, 1, 2, 3);
+ TEST_CHECK(DECODES_TO("910203"));
+
+ TEST_CHECK(ENCODE("\x06") == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NONE);
+
+ snd_midi_event_free(midi_event);
+}
+
+static void test_encode_byte(void)
+{
+ snd_midi_event_t *midi_event;
+ snd_seq_event_t ev;
+
+ if (ALSA_CHECK(snd_midi_event_new(256, &midi_event)) < 0)
+ return;
+
+#define ENCODE_BYTE(c) snd_midi_event_encode_byte(midi_event, c, &ev)
+ TEST_CHECK(ENCODE_BYTE(0x81) == 0);
+ TEST_CHECK(ENCODE_BYTE(0x02) == 0);
+ TEST_CHECK(ENCODE_BYTE(0x03) == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NOTEOFF);
+ TEST_CHECK((ev.flags & SND_SEQ_EVENT_LENGTH_MASK) == SND_SEQ_EVENT_LENGTH_FIXED);
+ TEST_CHECK(ev.data.note.channel == 1);
+ TEST_CHECK(ev.data.note.note == 2);
+ TEST_CHECK(ev.data.note.velocity == 3);
+ TEST_CHECK(ENCODE_BYTE(0x04) == 0);
+ TEST_CHECK(ENCODE_BYTE(0xf8) == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_CLOCK);
+ TEST_CHECK(ENCODE_BYTE(0x05) == 1);
+ TEST_CHECK(ev.type == SND_SEQ_EVENT_NOTEOFF);
+ TEST_CHECK(ev.data.note.channel == 1);
+ TEST_CHECK(ev.data.note.note == 4);
+ TEST_CHECK(ev.data.note.velocity == 5);
+
+ snd_midi_event_free(midi_event);
+}
+
+int main(void)
+{
+ test_decode();
+ test_reset_decode();
+ test_encode();
+ test_reset_encode();
+ test_encode_byte();
+ test_init();
+ return TEST_EXIT_CODE();
+}
diff --git a/test/lsb/test.h b/test/lsb/test.h
new file mode 100644
index 0000000..ff697c5
--- /dev/null
+++ b/test/lsb/test.h
@@ -0,0 +1,29 @@
+#ifndef TEST_H_INCLUDED
+#define TEST_H_INCLUDED
+
+#include <stdio.h>
+#include <alsa/asoundlib.h>
+
+/* XXX this variable definition does not belong in a header file */
+static int any_test_failed;
+
+#define TEST_CHECK(cond) do \
+ if (!(cond)) { \
+ fprintf(stderr, "%s:%d: test failed: %s\n", __FILE__, __LINE__, #cond); \
+ any_test_failed = 1; \
+ } \
+ while (0)
+
+#define ALSA_CHECK(fn) ({ \
+ int err = fn; \
+ if (err < 0) { \
+ fprintf(stderr, "%s:%d: ALSA function call failed (%s): %s\n", \
+ __FILE__, __LINE__, snd_strerror(err), #fn); \
+ any_test_failed = 1; \
+ } \
+ err; \
+ })
+
+#define TEST_EXIT_CODE() any_test_failed
+
+#endif
diff --git a/test/pcm.c b/test/pcm.c
index ee27422..abb83e4 100644
--- a/test/pcm.c
+++ b/test/pcm.c
@@ -34,17 +34,18 @@ static void generate_sine(const snd_pcm_channel_area_t *areas,
static double max_phase = 2. * M_PI;
double phase = *_phase;
double step = max_phase*freq/(double)rate;
- double res;
- unsigned char *samples[channels], *tmp;
+ unsigned char *samples[channels];
int steps[channels];
- unsigned int chn, byte;
- union {
- int i;
- unsigned char c[4];
- } ires;
- unsigned int maxval = (1 << (snd_pcm_format_width(format) - 1)) - 1;
- int bps = snd_pcm_format_width(format) / 8; /* bytes per sample */
-
+ unsigned int chn;
+ int format_bits = snd_pcm_format_width(format);
+ unsigned int maxval = (1 << (format_bits - 1)) - 1;
+ int bps = format_bits / 8; /* bytes per sample */
+ int phys_bps = snd_pcm_format_physical_width(format) / 8;
+ int big_endian = snd_pcm_format_big_endian(format) == 1;
+ int to_unsigned = snd_pcm_format_unsigned(format) == 1;
+ int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
+ format == SND_PCM_FORMAT_FLOAT_BE);
+
/* verify and prepare the contents of areas */
for (chn = 0; chn < channels; chn++) {
if ((areas[chn].first % 8) != 0) {
@@ -61,12 +62,27 @@ static void generate_sine(const snd_pcm_channel_area_t *areas,
}
/* fill the channel areas */
while (count-- > 0) {
- res = sin(phase) * maxval;
- ires.i = res;
- tmp = ires.c;
+ union {
+ float f;
+ int i;
+ } fval;
+ int res, i;
+ if (is_float) {
+ fval.f = sin(phase) * maxval;
+ res = fval.i;
+ } else
+ res = sin(phase) * maxval;
+ if (to_unsigned)
+ res ^= 1U << (format_bits - 1);
for (chn = 0; chn < channels; chn++) {
- for (byte = 0; byte < (unsigned int)bps; byte++)
- *(samples[chn] + byte) = tmp[byte];
+ /* Generate data in native endian format */
+ if (big_endian) {
+ for (i = 0; i < bps; i++)
+ *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
+ } else {
+ for (i = 0; i < bps; i++)
+ *(samples[chn] + i) = (res >> i * 8) & 0xff;
+ }
samples[chn] += steps[chn];
}
phase += step;
@@ -827,6 +843,13 @@ int main(int argc, char *argv[])
}
if (format == SND_PCM_FORMAT_LAST)
format = SND_PCM_FORMAT_S16;
+ if (!snd_pcm_format_linear(format) &&
+ !(format == SND_PCM_FORMAT_FLOAT_LE ||
+ format == SND_PCM_FORMAT_FLOAT_BE)) {
+ printf("Invalid (non-linear/float) format %s\n",
+ optarg);
+ return 1;
+ }
break;
case 'v':
verbose = 1;