4714 lines
158 KiB
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;
|