alsa-lib/alsa-git.patch
2021-08-31 09:55:42 +02:00

1917 lines
62 KiB
Diff

From 81e7923fbfad45b2f353a4d6e3053af51f5f7d0b Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 15 Jun 2021 23:21:42 +0200
Subject: [PATCH 01/20] control: empty - fix the static build
Reported-by: Jan Palus <atler@pld-linux.org>
Fixes: https://github.com/alsa-project/alsa-lib/issues/157
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/control/control_empty.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/control/control_empty.c b/src/control/control_empty.c
index 49d1026c..c9b048c1 100644
--- a/src/control/control_empty.c
+++ b/src/control/control_empty.c
@@ -30,7 +30,7 @@
#ifndef PIC
/* entry for static linking */
-const char *_snd_module_ctl_empty = "";
+const char *_snd_module_control_empty = "";
#endif
/*! \page control_plugins
--
2.31.1
From 5089358aa99d698bd846b05c1890a09613d740b3 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 17 Jun 2021 10:20:25 +0200
Subject: [PATCH 02/20] pcm: rate: Refactoring temporary buffer allocations
Introduce common helpers to allocate and release the temporary buffers
and the associated snd_pcm_channel. Now two allocated objects are
used instead of one malloc to be split.
Also, change the snd_pcm_channel set up to be in interleaved mode.
This will be necessary in the following change in the rate plugin.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/pcm/pcm_rate.c | 80 ++++++++++++++++++++++++++++------------------
1 file changed, 49 insertions(+), 31 deletions(-)
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index 770aafea..1e996134 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -76,6 +76,45 @@ struct _snd_pcm_rate {
#endif /* DOC_HIDDEN */
+/* allocate a channel area and a temporary buffer for the given size */
+static snd_pcm_channel_area_t *
+rate_alloc_tmp_buf(snd_pcm_rate_t *rate, snd_pcm_format_t format,
+ unsigned int channels, unsigned int frames)
+{
+ snd_pcm_channel_area_t *ap;
+ int width = snd_pcm_format_physical_width(format);
+ int i;
+
+ ap = malloc(sizeof(*ap) * channels);
+ if (!ap)
+ return NULL;
+ ap->addr = malloc(frames * channels * width / 8);
+ if (!ap->addr) {
+ free(ap);
+ return NULL;
+ }
+
+ /* set up in interleaved format */
+ for (i = 0; i < channels; i++) {
+ ap[i].addr = ap[0].addr + (i * width) / 8;
+ ap[i].first = 0;
+ ap[i].step = width * channels;
+ }
+
+ return ap;
+}
+
+static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
+{
+ snd_pcm_channel_area_t *c = *ptr;
+
+ if (c) {
+ free(c->addr);
+ free(c);
+ *ptr = NULL;
+ }
+}
+
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;
@@ -286,28 +325,13 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
if (err < 0)
return err;
- rate->pareas = malloc(2 * channels * sizeof(*rate->pareas));
- if (rate->pareas == NULL)
+ rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels,
+ cinfo->period_size);
+ rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels,
+ sinfo->period_size);
+ if (!rate->pareas || !rate->sareas)
goto error;
- cwidth = snd_pcm_format_physical_width(cinfo->format);
- swidth = snd_pcm_format_physical_width(sinfo->format);
- rate->pareas[0].addr = malloc(((cwidth * channels * cinfo->period_size) / 8) +
- ((swidth * channels * sinfo->period_size) / 8));
- if (rate->pareas[0].addr == NULL)
- goto error;
-
- rate->sareas = rate->pareas + channels;
- rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((cwidth * channels * cinfo->period_size) / 8);
- for (chn = 0; chn < channels; chn++) {
- rate->pareas[chn].addr = (char *)rate->pareas[0].addr + (cwidth * chn * cinfo->period_size) / 8;
- rate->pareas[chn].first = 0;
- rate->pareas[chn].step = cwidth;
- rate->sareas[chn].addr = (char *)rate->sareas[0].addr + (swidth * chn * sinfo->period_size) / 8;
- rate->sareas[chn].first = 0;
- rate->sareas[chn].step = swidth;
- }
-
if (rate->ops.convert_s16) {
rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16);
rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format);
@@ -322,11 +346,8 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
return 0;
error:
- if (rate->pareas) {
- free(rate->pareas[0].addr);
- free(rate->pareas);
- rate->pareas = NULL;
- }
+ rate_free_tmp_buf(&rate->pareas);
+ rate_free_tmp_buf(&rate->sareas);
if (rate->ops.free)
rate->ops.free(rate->obj);
return -ENOMEM;
@@ -335,12 +356,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
- if (rate->pareas) {
- free(rate->pareas[0].addr);
- free(rate->pareas);
- rate->pareas = NULL;
- rate->sareas = NULL;
- }
+
+ rate_free_tmp_buf(&rate->pareas);
+ rate_free_tmp_buf(&rate->sareas);
if (rate->ops.free)
rate->ops.free(rate->obj);
free(rate->src_buf);
--
2.31.1
From 119d9c1678b1193f8b969a6483cae1f7bf95e609 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 17 Jun 2021 10:03:17 +0200
Subject: [PATCH 03/20] pcm: rate: Improve the support multiple formats
This patch extends the PCM rate plugin for allowing its converter
plugin to deal with multiple formats. Currently, the converter plugin
is allowed to take different formats only when convert callback is
defined. And for this way (so far only the standard linear rate
plugin does), all linear formats have to be handled, and it's
cumbersome.
OTOH, most other rate plugins are implemented with convert_s16
callback, which accepts only S16 format. This is often not ideal
because many converter engines can handle 32bit formats. Also, the
target format is often 32bit format, hence this would require
additional conversion even if the converter engine can output 32bit
natively.
In this patch, for addressing the problems above, the rate plugin API
is extended in the following way:
- The new get_supported_formats callback is added; this stores the bit
masks of the supported input and output formats, as well as the
behavior flags. Currently only linear formats are allowed.
- When the plugin accepts only the interleaved stream, set
SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to
handle snd_pcm_channel_area at each call.
- When both input and output formats have to be identical, pass
SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit.
- When the converter wants to process different formats, use convert
callback instead of convert_s16. You can put both in the ops for
compatibility, too.
The input and output formats are found in the info argument of init
callback.
- Now the PCM rate plugin core will skip the temporary buffer
allocation and conversions for pre- and post-process if not needed
(i.e. matching with the requested input or output format).
The rate plugin API version is bumped to 0x010003.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/pcm_rate.h | 36 ++++-
src/pcm/pcm_rate.c | 358 +++++++++++++++++++++++++++------------------
2 files changed, 251 insertions(+), 143 deletions(-)
diff --git a/include/pcm_rate.h b/include/pcm_rate.h
index 4171fb9d..48473ed4 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 0x010002
+#define SND_PCM_RATE_PLUGIN_VERSION 0x010003
/** hw_params information for a single side */
typedef struct snd_pcm_rate_side_info {
@@ -55,6 +55,11 @@ typedef struct snd_pcm_rate_info {
unsigned int channels;
} snd_pcm_rate_info_t;
+enum {
+ SND_PCM_RATE_FLAG_INTERLEAVED = (1U << 0), /** only interleaved format */
+ SND_PCM_RATE_FLAG_SYNC_FORMATS = (1U << 1), /** both input and output formats have to be identical */
+};
+
/** Callback table of rate-converter */
typedef struct snd_pcm_rate_ops {
/**
@@ -114,6 +119,13 @@ typedef struct snd_pcm_rate_ops {
* new ops since version 0x010002
*/
void (*dump)(void *obj, snd_output_t *out);
+ /**
+ * get the supported input and output formats (optional);
+ * new ops since version 0x010003
+ */
+ int (*get_supported_formats)(void *obj, uint64_t *in_formats,
+ uint64_t *out_formats,
+ unsigned int *flags);
} snd_pcm_rate_ops_t;
/** open function type */
@@ -147,6 +159,28 @@ typedef struct snd_pcm_rate_old_ops {
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;
+
+/* old rate_ops for protocol version 0x010002 */
+typedef struct snd_pcm_rate_v2_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);
+ unsigned int version;
+ int (*get_supported_rates)(void *obj, unsigned int *rate_min,
+ unsigned int *rate_max);
+ void (*dump)(void *obj, snd_output_t *out);
+} snd_pcm_rate_v2_ops_t;
#endif
#ifdef __cplusplus
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index 1e996134..13111d29 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -62,18 +62,22 @@ struct _snd_pcm_rate {
void *open_func;
void *obj;
snd_pcm_rate_ops_t ops;
- unsigned int get_idx;
- unsigned int put_idx;
- int16_t *src_buf;
- int16_t *dst_buf;
+ unsigned int src_conv_idx;
+ unsigned int dst_conv_idx;
+ snd_pcm_channel_area_t *src_buf;
+ snd_pcm_channel_area_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;
+ snd_pcm_format_t orig_in_format;
+ snd_pcm_format_t orig_out_format;
+ uint64_t in_formats;
+ uint64_t out_formats;
+ unsigned int format_flags;
};
#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
-
#endif /* DOC_HIDDEN */
/* allocate a channel area and a temporary buffer for the given size */
@@ -274,12 +278,84 @@ static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
snd_pcm_generic_hw_refine);
}
+/* evaluate the best matching available format to the given format */
+static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
+{
+ int pwidth = snd_pcm_format_physical_width(orig);
+ int width = snd_pcm_format_width(orig);
+ int signd = snd_pcm_format_signed(orig);
+ int best_score = -1;
+ int match = -1;
+ int f, score;
+
+ for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
+ if (!(mask & (1ULL << f)))
+ continue;
+ score = 0;
+ if (snd_pcm_format_linear(f)) {
+ if (snd_pcm_format_physical_width(f) == pwidth)
+ score++;
+ if (snd_pcm_format_physical_width(f) >= pwidth)
+ score++;
+ if (snd_pcm_format_width(f) == width)
+ score++;
+ if (snd_pcm_format_signed(f) == signd)
+ score++;
+ }
+ if (score > best_score) {
+ match = f;
+ best_score = score;
+ }
+ }
+
+ return match;
+}
+
+/* set up the input and output formats from the available lists */
+static int choose_preferred_format(snd_pcm_rate_t *rate)
+{
+ uint64_t in_mask = rate->in_formats;
+ uint64_t out_mask = rate->out_formats;
+ int in, out;
+
+ if (!in_mask || !out_mask)
+ return 0;
+
+ if (rate->orig_in_format == rate->orig_out_format)
+ if (in_mask & out_mask & (1ULL << rate->orig_in_format))
+ return 0; /* nothing changed */
+
+ repeat:
+ in = get_best_format(in_mask, rate->orig_in_format);
+ out = get_best_format(out_mask, rate->orig_out_format);
+ if (in < 0 || out < 0)
+ return -ENOENT;
+
+ if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
+ in != out) {
+ if (out_mask & (1ULL << in))
+ out = in;
+ else if (in_mask & (1ULL << out))
+ in = out;
+ else {
+ in_mask &= ~(1ULL << in);
+ out_mask &= ~(1ULL << out);
+ goto repeat;
+ }
+ }
+
+ rate->info.in.format = in;
+ rate->info.out.format = out;
+ return 0;
+}
+
static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_rate_side_info_t *sinfo, *cinfo;
- unsigned int channels, cwidth, swidth, chn;
+ unsigned int channels, cwidth, swidth, chn, acc;
+ int need_src_buf, need_dst_buf;
int err = snd_pcm_hw_params_slave(pcm, params,
snd_pcm_rate_hw_refine_cchange,
snd_pcm_rate_hw_refine_sprepare,
@@ -308,6 +384,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
+ if (err < 0)
+ return err;
+ err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
if (err < 0)
return err;
@@ -321,36 +400,80 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
SNDMSG("rate plugin already in use");
return -EBUSY;
}
- err = rate->ops.init(rate->obj, &rate->info);
- if (err < 0)
- return err;
rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels,
cinfo->period_size);
rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels,
sinfo->period_size);
- if (!rate->pareas || !rate->sareas)
- goto error;
-
- if (rate->ops.convert_s16) {
- rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16);
- rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format);
- free(rate->src_buf);
- rate->src_buf = malloc(channels * rate->info.in.period_size * 2);
- free(rate->dst_buf);
- rate->dst_buf = malloc(channels * rate->info.out.period_size * 2);
- if (! rate->src_buf || ! rate->dst_buf)
+ if (!rate->pareas || !rate->sareas) {
+ err = -ENOMEM;
+ goto error_pareas;
+ }
+
+ rate->orig_in_format = rate->info.in.format;
+ rate->orig_out_format = rate->info.out.format;
+ if (choose_preferred_format(rate) < 0) {
+ SNDERR("No matching format in rate plugin");
+ err = -EINVAL;
+ goto error_pareas;
+ }
+
+ err = rate->ops.init(rate->obj, &rate->info);
+ if (err < 0)
+ goto error_init;
+
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
+
+ need_src_buf = need_dst_buf = 0;
+
+ if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
+ !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
+ acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
+ need_src_buf = need_dst_buf = 1;
+ } else {
+ if (rate->orig_in_format != rate->info.in.format)
+ need_src_buf = 1;
+ if (rate->orig_out_format != rate->info.out.format)
+ need_dst_buf = 1;
+ }
+
+ if (need_src_buf) {
+ rate->src_conv_idx =
+ snd_pcm_linear_convert_index(rate->orig_in_format,
+ rate->info.in.format);
+ rate->src_buf = rate_alloc_tmp_buf(rate, rate->info.in.format,
+ channels, rate->info.in.period_size);
+ if (!rate->src_buf) {
+ err = -ENOMEM;
goto error;
+ }
+ }
+
+ if (need_dst_buf) {
+ rate->dst_conv_idx =
+ snd_pcm_linear_convert_index(rate->info.out.format,
+ rate->orig_out_format);
+ rate->dst_buf = rate_alloc_tmp_buf(rate, rate->info.out.format,
+ channels, rate->info.out.period_size);
+ if (!rate->dst_buf) {
+ err = -ENOMEM;
+ goto error;
+ }
}
return 0;
error:
- rate_free_tmp_buf(&rate->pareas);
- rate_free_tmp_buf(&rate->sareas);
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
+ error_init:
if (rate->ops.free)
rate->ops.free(rate->obj);
- return -ENOMEM;
+ error_pareas:
+ rate_free_tmp_buf(&rate->pareas);
+ rate_free_tmp_buf(&rate->sareas);
+ return err;
}
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
@@ -361,9 +484,8 @@ static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
rate_free_tmp_buf(&rate->sareas);
if (rate->ops.free)
rate->ops.free(rate->obj);
- free(rate->src_buf);
- free(rate->dst_buf);
- rate->src_buf = rate->dst_buf = NULL;
+ rate_free_tmp_buf(&rate->src_buf);
+ rate_free_tmp_buf(&rate->dst_buf);
return snd_pcm_hw_free(rate->gen.slave);
}
@@ -444,82 +566,6 @@ static int snd_pcm_rate_init(snd_pcm_t *pcm)
return 0;
}
-static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf,
- const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, unsigned int frames,
- unsigned int channels)
-{
-#ifndef DOC_HIDDEN
-#define GET16_LABELS
-#include "plugin_ops.h"
-#undef GET16_LABELS
-#endif /* DOC_HIDDEN */
- void *get = get16_labels[rate->get_idx];
- const char *src;
- int16_t sample;
- const char *srcs[channels];
- int src_step[channels];
- unsigned int c;
-
- for (c = 0; c < channels; c++) {
- srcs[c] = snd_pcm_channel_area_addr(areas + c, offset);
- src_step[c] = snd_pcm_channel_area_step(areas + c);
- }
-
- while (frames--) {
- for (c = 0; c < channels; c++) {
- src = srcs[c];
- goto *get;
-#ifndef DOC_HIDDEN
-#define GET16_END after_get
-#include "plugin_ops.h"
-#undef GET16_END
-#endif /* DOC_HIDDEN */
- after_get:
- *buf++ = sample;
- srcs[c] += src_step[c];
- }
- }
-}
-
-static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf,
- const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, unsigned int frames,
- unsigned int channels)
-{
-#ifndef DOC_HIDDEN
-#define PUT16_LABELS
-#include "plugin_ops.h"
-#undef PUT16_LABELS
-#endif /* DOC_HIDDEN */
- void *put = put16_labels[rate->put_idx];
- char *dst;
- int16_t sample;
- char *dsts[channels];
- int dst_step[channels];
- unsigned int c;
-
- for (c = 0; c < channels; c++) {
- dsts[c] = snd_pcm_channel_area_addr(areas + c, offset);
- dst_step[c] = snd_pcm_channel_area_step(areas + c);
- }
-
- while (frames--) {
- for (c = 0; c < channels; c++) {
- dst = dsts[c];
- sample = *buf++;
- goto *put;
-#ifndef DOC_HIDDEN
-#define PUT16_END after_put
-#include "plugin_ops.h"
-#undef PUT16_END
-#endif /* DOC_HIDDEN */
- after_put:
- dsts[c] += dst_step[c];
- }
- }
-}
-
static void do_convert(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,
@@ -527,28 +573,40 @@ static void do_convert(const snd_pcm_channel_area_t *dst_areas,
unsigned int channels,
snd_pcm_rate_t *rate)
{
- if (rate->ops.convert_s16) {
- const int16_t *src;
- int16_t *dst;
- if (! rate->src_buf)
- src = (int16_t *)src_areas->addr + src_offset * channels;
- else {
- convert_to_s16(rate, rate->src_buf, src_areas, src_offset,
- src_frames, channels);
- src = rate->src_buf;
- }
- if (! rate->dst_buf)
- dst = (int16_t *)dst_areas->addr + dst_offset * channels;
- else
- dst = rate->dst_buf;
- rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames);
- if (dst == rate->dst_buf)
- convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset,
- dst_frames, channels);
+ const snd_pcm_channel_area_t *out_areas;
+ snd_pcm_uframes_t out_offset;
+
+ if (rate->dst_buf) {
+ out_areas = rate->dst_buf;
+ out_offset = 0;
} else {
- rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames,
- src_areas, src_offset, src_frames);
+ out_areas = dst_areas;
+ out_offset = dst_offset;
+ }
+
+ if (rate->src_buf) {
+ snd_pcm_linear_convert(rate->src_buf, 0,
+ src_areas, src_offset,
+ channels, src_frames,
+ rate->src_conv_idx);
+ src_areas = rate->src_buf;
+ src_offset = 0;
}
+
+ if (rate->ops.convert)
+ rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
+ src_areas, src_offset, src_frames);
+ else
+ rate->ops.convert_s16(rate->obj,
+ snd_pcm_channel_area_addr(out_areas, out_offset),
+ dst_frames,
+ snd_pcm_channel_area_addr(src_areas, src_offset),
+ src_frames);
+ if (rate->dst_buf)
+ snd_pcm_linear_convert(dst_areas, dst_offset,
+ rate->dst_buf, 0,
+ channels, dst_frames,
+ rate->dst_conv_idx);
}
static inline void
@@ -1276,6 +1334,30 @@ const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
return NULL;
}
+static void rate_initial_setup(snd_pcm_rate_t *rate)
+{
+ if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
+ rate->plugin_version = rate->ops.version;
+
+ if (rate->plugin_version >= 0x010002 &&
+ rate->ops.get_supported_rates)
+ rate->ops.get_supported_rates(rate->obj,
+ &rate->rate_min,
+ &rate->rate_max);
+
+ if (rate->plugin_version >= 0x010003 &&
+ rate->ops.get_supported_formats) {
+ rate->ops.get_supported_formats(rate->obj,
+ &rate->in_formats,
+ &rate->out_formats,
+ &rate->format_flags);
+ } else if (!rate->ops.convert && rate->ops.convert_s16) {
+ rate->in_formats = rate->out_formats =
+ 1ULL << SND_PCM_FORMAT_S16;
+ rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
+ }
+}
+
#ifdef PIC
static int is_builtin_plugin(const char *type)
{
@@ -1301,20 +1383,11 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_conf
lib = lib_name;
}
- rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
- rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
- rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
-
open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
if (open_conf_func) {
err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
&rate->obj, &rate->ops, converter_conf);
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);
rate->open_func = open_conf_func;
return 0;
} else {
@@ -1330,23 +1403,18 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_conf
rate->open_func = open_func;
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);
+ if (!err)
return 0;
- }
/* try to open with the old protocol version */
rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
&rate->obj, &rate->ops);
- if (err) {
- snd_dlobj_cache_put(open_func);
- rate->open_func = NULL;
- }
+ if (!err)
+ return 0;
+
+ snd_dlobj_cache_put(open_func);
+ rate->open_func = NULL;
return err;
}
#endif
@@ -1417,6 +1485,10 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
rate->srate = srate;
rate->sformat = sformat;
+ 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 = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
if (err < 0) {
free(rate);
@@ -1496,6 +1568,8 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
return err;
}
+ rate_initial_setup(rate);
+
pcm->ops = &snd_pcm_rate_ops;
pcm->fast_ops = &snd_pcm_rate_fast_ops;
pcm->private_data = rate;
--
2.31.1
From 3f737a2a2c8d20e78dea3ea836997f9d74f602a0 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 18 Jun 2021 11:35:30 +0200
Subject: [PATCH 04/20] pcm: improve docs for snd_pcm_sw_params_get_avail_min()
and snd_pcm_status_get_avail_max()
Fixes: https://github.com/alsa-project/alsa-lib/issues/44
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/pcm/pcm.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index 09df0f12..892abf98 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -6500,6 +6500,9 @@ int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params,
* \param params Software configuration container
* \param val returned minimum available frames to consider PCM ready
* \return 0 otherwise a negative error code
+ *
+ * This is a threshold value when the PCM stream is considered as ready for
+ * another read/write operation or poll event.
*/
#ifndef DOXYGEN
EXPORT_SYMBOL int INTERNAL(snd_pcm_sw_params_get_avail_min)(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val)
@@ -6960,6 +6963,8 @@ snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj)
/**
* \brief Get maximum number of frames available from a PCM status container after last #snd_pcm_status call
* \return Maximum number of frames ready to be read/written
+ *
+ * This value returns the peak for the available frames between #snd_pcm_status calls.
*/
snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj)
{
--
2.31.1
From 212c6c18c4317af48c007a0866efc029b9c3a593 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 21 Jun 2021 09:23:02 +0200
Subject: [PATCH 05/20] pcm: dmix - use pcm_frame_diff() in
snd_pcm_dmix_sync_ptr0()
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/pcm/pcm_dmix.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c
index 608593f1..0d0d0bff 100644
--- a/src/pcm/pcm_dmix.c
+++ b/src/pcm/pcm_dmix.c
@@ -393,17 +393,13 @@ static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr
old_slave_hw_ptr = dmix->slave_hw_ptr;
dmix->slave_hw_ptr = slave_hw_ptr;
- diff = slave_hw_ptr - old_slave_hw_ptr;
+ diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dmix->slave_boundary);
if (diff == 0) /* fast path */
return 0;
if (dmix->state != SND_PCM_STATE_RUNNING &&
dmix->state != SND_PCM_STATE_DRAINING)
/* not really started yet - don't update hw_ptr */
return 0;
- if (diff < 0) {
- slave_hw_ptr += dmix->slave_boundary;
- diff = slave_hw_ptr - old_slave_hw_ptr;
- }
dmix->hw_ptr += diff;
dmix->hw_ptr %= pcm->boundary;
if (pcm->stop_threshold >= pcm->boundary) /* don't care */
--
2.31.1
From dd609ef9684987d3ca61d5c5cc3c77589ff9c29f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 21 Jun 2021 09:28:41 +0200
Subject: [PATCH 06/20] pcm: direct plugins - fix hw_ptr in the status callback
The parent hw_ptr may be in another range (boundary limit).
Set the correct value for the caller.
BugLink: https://github.com/alsa-project/alsa-lib/issues/155
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/pcm/pcm_dmix.c | 1 +
src/pcm/pcm_dshare.c | 1 +
src/pcm/pcm_dsnoop.c | 1 +
3 files changed, 3 insertions(+)
diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c
index 0d0d0bff..94dbb1e0 100644
--- a/src/pcm/pcm_dmix.c
+++ b/src/pcm/pcm_dmix.c
@@ -491,6 +491,7 @@ static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
}
status->state = snd_pcm_dmix_state(pcm);
+ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
status->trigger_tstamp = dmix->trigger_tstamp;
status->avail = snd_pcm_mmap_playback_avail(pcm);
diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c
index a918512b..01814dc8 100644
--- a/src/pcm/pcm_dshare.c
+++ b/src/pcm/pcm_dshare.c
@@ -243,6 +243,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
break;
}
status->state = snd_pcm_dshare_state(pcm);
+ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
status->trigger_tstamp = dshare->trigger_tstamp;
status->avail = snd_pcm_mmap_playback_avail(pcm);
diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c
index 2c3b9f43..3f28df99 100644
--- a/src/pcm/pcm_dsnoop.c
+++ b/src/pcm/pcm_dsnoop.c
@@ -193,6 +193,7 @@ static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
snd_pcm_status(dsnoop->spcm, status);
state = snd_pcm_state(dsnoop->spcm);
status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state;
+ status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */
status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
status->trigger_tstamp = dsnoop->trigger_tstamp;
status->avail = snd_pcm_mmap_capture_avail(pcm);
--
2.31.1
From a5e11f9a810391777ea7750f04ba66f9c9e624de Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Mon, 21 Jun 2021 14:21:26 +0200
Subject: [PATCH 07/20] pcm: Move snd_pcm_channel_area_addr() and _step() to
public header
Used in the rate plugins commonly.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/pcm.h | 23 +++++++++++++++++++++++
src/pcm/pcm_local.h | 13 -------------
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/include/pcm.h b/include/pcm.h
index e300b951..b5a514fa 100644
--- a/include/pcm.h
+++ b/include/pcm.h
@@ -1173,6 +1173,29 @@ int snd_pcm_areas_copy_wrap(const snd_pcm_channel_area_t *dst_channels,
snd_pcm_uframes_t frames,
const snd_pcm_format_t format);
+/**
+ * \brief get the address of the given PCM channel area
+ * \param area PCM channel area
+ * \param offset Offset in frames
+ *
+ * Returns the pointer corresponding to the given offset on the channel area.
+ */
+static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
+{
+ return (char *)area->addr + (area->first + area->step * offset) / 8;
+}
+
+/**
+ * \brief get the step size of the given PCM channel area in bytes
+ * \param area PCM channel area
+ *
+ * Returns the step size in bytes from the given channel area.
+ */
+static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area)
+{
+ return area->step / 8;
+}
+
/** \} */
/**
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index a63f4be0..6f03365c 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -632,19 +632,6 @@ static inline snd_pcm_sframes_t snd_pcm_mmap_delay(snd_pcm_t *pcm)
return snd_pcm_mmap_capture_delay(pcm);
}
-static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
-{
- unsigned int bitofs = area->first + area->step * offset;
- assert(bitofs % 8 == 0);
- return (char *) area->addr + bitofs / 8;
-}
-
-static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area)
-{
- assert(area->step % 8 == 0);
- return area->step / 8;
-}
-
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
/* lock handled in the callback */
--
2.31.1
From e0e084659083c2ab75d5c894f24227ea2f67010f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 21 Jun 2021 15:14:18 +0200
Subject: [PATCH 08/20] pcm: direct plugins - fix bad memory access when
channel bindings do not match hw
Fix and cleanup snd_pcm_direct_check_interleave() function.
Add requested / hardware channel check and use goto when the interleaved
Fixes: https://github.com/alsa-project/alsa-lib/issues/117
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/pcm/pcm_direct.c | 40 +++++++++++++++++-----------------------
1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c
index d50503e3..90417b2f 100644
--- a/src/pcm/pcm_direct.c
+++ b/src/pcm/pcm_direct.c
@@ -1627,43 +1627,37 @@ int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix)
int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm)
{
unsigned int chn, channels;
- int bits, interleaved = 1;
+ int bits;
const snd_pcm_channel_area_t *dst_areas;
const snd_pcm_channel_area_t *src_areas;
bits = snd_pcm_format_physical_width(pcm->format);
if ((bits % 8) != 0)
- interleaved = 0;
+ goto __nointerleaved;
channels = dmix->channels;
+ if (channels != dmix->spcm->channels)
+ goto __nointerleaved;
dst_areas = snd_pcm_mmap_areas(dmix->spcm);
src_areas = snd_pcm_mmap_areas(pcm);
for (chn = 1; chn < channels; chn++) {
- if (dst_areas[chn-1].addr != dst_areas[chn].addr) {
- interleaved = 0;
- break;
- }
- if (src_areas[chn-1].addr != src_areas[chn].addr) {
- interleaved = 0;
- break;
- }
+ if (dst_areas[chn-1].addr != dst_areas[chn].addr)
+ goto __nointerleaved;
+ if (src_areas[chn-1].addr != src_areas[chn].addr)
+ goto __nointerleaved;
}
for (chn = 0; chn < channels; chn++) {
- if (dmix->bindings && dmix->bindings[chn] != chn) {
- interleaved = 0;
- break;
- }
+ if (dmix->bindings && dmix->bindings[chn] != chn)
+ goto __nointerleaved;
if (dst_areas[chn].first != chn * bits ||
- dst_areas[chn].step != channels * bits) {
- interleaved = 0;
- break;
- }
+ dst_areas[chn].step != channels * bits)
+ goto __nointerleaved;
if (src_areas[chn].first != chn * bits ||
- src_areas[chn].step != channels * bits) {
- interleaved = 0;
- break;
- }
+ src_areas[chn].step != channels * bits)
+ goto __nointerleaved;
}
- return dmix->interleaved = interleaved;
+ return dmix->interleaved = 1;
+__nointerleaved:
+ return dmix->interleaved = 0;
}
/*
--
2.31.1
From ccc14ae897d170156f1c2905ea5d18a3295e7b36 Mon Sep 17 00:00:00 2001
From: "Tanjeff-N. Moos" <tanjeff@cccmz.de>
Date: Thu, 17 Jun 2021 10:36:38 +0200
Subject: [PATCH 09/20] control: Add documentation for snd_ctl_card_* and
friends.
In this patch series, I added a description about control interface
handling and how control interfaces are identified.
In addition, I added/improved Doxygen documentation for the
snd_ctl_card_info_t type and related corresponding functions,
e.g. snd_ctl_card_info(). I also documented other card-related like
snd_card_next().
Along the way I did minor documentation improvements.
Signed-off-by: Tanjeff-N. Moos <tanjeff@cccmz.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/control.h | 40 +++++++++-
src/control/cards.c | 56 +++++++++-----
src/control/control.c | 175 +++++++++++++++++++++++++++++++-----------
3 files changed, 202 insertions(+), 69 deletions(-)
diff --git a/include/control.h b/include/control.h
index ccf906e2..9ebb4905 100644
--- a/include/control.h
+++ b/include/control.h
@@ -50,7 +50,32 @@ typedef struct snd_aes_iec958 {
unsigned char dig_subframe[4]; /**< AES/IEC958 subframe bits */
} snd_aes_iec958_t;
-/** CTL card info container */
+/** \brief CTL card info container.
+ *
+ * This type contains meta information about a sound card, such as the index,
+ * name, longname, etc.
+ *
+ * \par Memory management
+ *
+ * Before using a snd_ctl_card_info_t object, it must be allocated using
+ * snd_ctl_card_info_alloca() or snd_ctl_card_info_malloc(). When using the
+ * latter, it must be freed again using snd_ctl_card_info_free().
+ *
+ * A card info object can be zeroed out using snd_ctl_card_info_clear().
+ *
+ * A card info object can be copied to another one using
+ * snd_ctl_card_info_copy().
+ *
+ * \par Obtaining the Information
+ *
+ * To obtain the card information, it must first be opened using
+ * snd_ctl_open(), and a snd_ctl_card_info_t container must be
+ * allocated. Then, the information can be read using
+ * snd_ctl_card_info_get_card().
+ *
+ * Thereafter, the card properties can be read using the
+ * snd_ctl_card_info_get_*() functions.
+ */
typedef struct _snd_ctl_card_info snd_ctl_card_info_t;
/** CTL element identifier container */
@@ -442,11 +467,20 @@ void snd_ctl_elem_id_set_name(snd_ctl_elem_id_t *obj, const char *val);
void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val);
size_t snd_ctl_card_info_sizeof(void);
+
/** \hideinitializer
- * \brief allocate an invalid #snd_ctl_card_info_t using standard alloca
- * \param ptr returned pointer
+ * \brief Allocate an invalid #snd_ctl_card_info_t on the stack.
+ *
+ * Allocate space for a card info object on the stack. The allocated
+ * memory need not be freed, because it is on the stack.
+ *
+ * See snd_ctl_card_info_t for details.
+ *
+ * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address
+ * of the allocated space will returned here.
*/
#define snd_ctl_card_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_info)
+
int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr);
void snd_ctl_card_info_free(snd_ctl_card_info_t *obj);
void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj);
diff --git a/src/control/cards.c b/src/control/cards.c
index e57457c2..8226c42d 100644
--- a/src/control/cards.c
+++ b/src/control/cards.c
@@ -77,8 +77,8 @@ static int snd_card_load1(int card)
/**
* \brief Try to load the driver for a card.
- * \param card Card number.
- * \return 1 if driver is present, zero if driver is not present
+ * \param card Card index.
+ * \return 1 if driver is present, zero if driver is not present.
*/
int snd_card_load(int card)
{
@@ -86,14 +86,24 @@ int snd_card_load(int card)
}
/**
- * \brief Try to determine the next card.
- * \param rcard pointer to card number
- * \result zero if success, otherwise a negative error code
+ * \brief Iterate over physical sound cards.
+ *
+ * This function takes the index of a physical sound card and sets it to the
+ * index of the next card. If index is -1, it is set to the index of the first
+ * card. After the last card, the index is set to -1.
+ *
+ * For example, if you have 2 sound cards (with index 0 and 1), the index will
+ * be modified as follows:
+ *
+ * - -1 --> 0
+ * - 0 --> 1
+ * - 1 --> -1
+ *
+ * This does not work for virtual sound cards.
*
- * Tries to determine the next card from given card number.
- * If card number is -1, then the first available card is
- * returned. If the result card number is -1, no more cards
- * are available.
+ * \param rcard Index of current card. The index of the next card is stored
+ * here.
+ * \result zero if success, otherwise a negative error code.
*/
int snd_card_next(int *rcard)
{
@@ -114,13 +124,18 @@ int snd_card_next(int *rcard)
}
/**
- * \brief Convert card string to an integer value.
- * \param string String containing card identifier
- * \return zero if success, otherwise a negative error code
+ * \brief Convert a card string to the card index.
*
- * 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.
+ * This works only for physical sound cards, not for virtual cards.
+ *
+ * \param string A string identifying the card.
+ * \return The index of the card. On error, a a negative error code
+ * is returned.
+ *
+ * The accepted formats for "string" are:
+ * - The index of the card (as listed in /proc/asound/cards), given as string
+ * - The ID of the card (as listed in /proc/asound/cards)
+ * - The control device name (like /dev/snd/controlC0)
*/
int snd_card_get_index(const char *string)
{
@@ -163,8 +178,9 @@ int snd_card_get_index(const char *string)
/**
* \brief Obtain the card name.
- * \param card Card number
- * \param name Result - card name corresponding to card number
+ *
+ * \param card The index of the card.
+ * \param name Result - card name corresponding to card index.
* \result zero if success, otherwise a negative error code
*
* The value returned in name is allocated with strdup and should be
@@ -193,9 +209,9 @@ int snd_card_get_name(int card, char **name)
/**
* \brief Obtain the card long name.
- * \param card Card number
- * \param name Result - card long name corresponding to card number
- * \result zero if success, otherwise a negative error code
+ * \param card Index of the card.
+ * \param name Result - card long name corresponding to card index.
+ * \result Zero if success, otherwise a negative error code.
*
* The value returned in name is allocated with strdup and should be
* freed when no longer used.
diff --git a/src/control/control.c b/src/control/control.c
index ed986e54..7cf4decb 100644
--- a/src/control/control.c
+++ b/src/control/control.c
@@ -33,8 +33,50 @@
<P>Control interface is designed to access primitive controls. There is
also an interface for notifying about control and structure changes.
+
\section control_general_overview General overview
+In Alsa, there are physical sound cards, such as USB headsets, and
+virtual sound cards, such as "pulse", which represents the PulseAudio
+Sound system. Each sound card offers a control interface, making its
+settings (e.g. volume knobs) available. The complete list of available
+control interfaces can be obtained using snd_device_name_hint(),
+giving -1 as card index and "ctl" as interface type. Each returned
+NAME hint identifies a control interface.
+
+Sound cards have an ID (a string), an index (an int, sometimes called
+the "card number"), a name, a longname, a mixername and a "components"
+property. The file /proc/asound/cards lists most of these properties
+for physical sound cards. Virtual sound cards are not listed in that
+file. The format is:
+
+\verbatim
+index [ID ] Driver - name
+ longname
+\endverbatim
+
+Note that the mixername and components are not listed.
+
+
+\subsection control_cards_id Identifying and Opening Control Interfaces
+
+To work with a control interface, is must be opened first, using
+snd_ctl_open(). This function takes the interface name.
+
+For physical sound cards, the control interface can be identified
+using the string "hw:<index>" (e.g. `hw:2`). The NAME hint - which is
+"hw:CARD=<ID>" - can also be used. Further, its device file (something
+like `/dev/snd/controlC0`) is also acceptable. Either of them can be
+given to snd_ctl_open().
+
+For virtual sound cards, the NAME hint is given to snd_ctl_open().
+
+The functions snd_card_get_index(), snd_card_get_name() and
+snd_card_get_longname() can be used to find an identifying property
+when another one is already known.
+
+\section control_elements Elements
+
In ALSA control feature, each sound card can have control elements. The elements
are managed according to below model.
@@ -65,7 +107,7 @@ are managed according to below model.
of userspace applications and drivers in kernel.
-\section identifying_elements Identifying Elements
+\subsection identifying_elements Identifying Elements
Each element has the following identifying properties:
@@ -84,7 +126,7 @@ but in practice this is rare). The numid can change on each boot.
In case of an USB sound card, the numid can also change when it
is reconnected. The short numid is used to reduce the lookup time.
-\section element_lists Element Lists
+\subsection element_lists Element Lists
An element list can be used to obtain a list of all elements of the
sound card. The list contains generic information (e.g. how many
@@ -93,7 +135,7 @@ elements the card has), and the identifying properties of the elements
element lists.
-\section working_with_elements Working with Elements
+\subsection working_with_elements Working with Elements
It is possible to obtain information about an element using the
snd_ctl_elem_info_*() functions. For enums, the allowed values can be
@@ -108,7 +150,7 @@ actual values or settings. It is also possible to get and set the ID
values (such as the numid or the name).
-\section element_sets Element Sets
+\subsection element_sets Element Sets
The type of element set is one of integer, integer64, boolean, enumerators,
bytes and IEC958 structure. This indicates the type of value for each member in
@@ -329,10 +371,15 @@ int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe)
/**
- * \brief Get card related information
- * \param ctl CTL handle
- * \param info Card info pointer
- * \return 0 on success otherwise a negative error code
+ * \brief Get information about the sound card.
+ *
+ * Obtain information about the sound card previously opened using
+ * snd_ctl_open(). The object "info" must be allocated prior to calling this
+ * function. See snd_ctl_card_info_t for details.
+ *
+ * \param ctl The CTL handle.
+ * \param info The card information is stored here.
+ * \return 0 on success, otherwise a negative error code.
*/
int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
{
@@ -1508,11 +1555,13 @@ int _snd_ctl_open_named_child(snd_ctl_t **pctl, const char *name,
#endif
/**
- * \brief Opens a CTL
- * \param ctlp Returned CTL handle
- * \param name ASCII identifier of the CTL handle
- * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
- * \return 0 on success otherwise a negative error code
+ * \brief Opens a sound card.
+ *
+ * \param ctlp Returned CTL handle.
+ * \param name A string identifying the card (See \ref control_cards_id).
+ * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC).
+ *
+ * \return 0 on success otherwise a negative error code.
*/
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
@@ -2027,8 +2076,8 @@ void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val)
}
/**
- * \brief get size of #snd_ctl_card_info_t
- * \return size in bytes
+ * \brief get size of #snd_ctl_card_info_t.
+ * \return Size in bytes.
*/
size_t snd_ctl_card_info_sizeof()
{
@@ -2036,9 +2085,16 @@ size_t snd_ctl_card_info_sizeof()
}
/**
- * \brief allocate an invalid #snd_ctl_card_info_t using standard malloc
- * \param ptr returned pointer
- * \return 0 on success otherwise negative error code
+ * \brief Allocate an invalid #snd_ctl_card_info_t on the heap.
+ *
+ * Allocate space for a card info object on the heap. The allocated memory
+ * must be freed using snd_ctl_card_info_free().
+ *
+ * See snd_ctl_card_info_t for details.
+ *
+ * \param ptr Pointer to a snd_ctl_card_info_t pointer. The address
+ * of the allocated space will be returned here.
+ * \return 0 on success, otherwise a negative error code.
*/
int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr)
{
@@ -2050,8 +2106,10 @@ int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr)
}
/**
- * \brief frees a previously allocated #snd_ctl_card_info_t
- * \param obj pointer to object to free
+ * \brief Free an #snd_ctl_card_info_t previously allocated using
+ * snd_ctl_card_info_malloc().
+ *
+ * \param obj Pointer to the snd_ctl_card_info_t.
*/
void snd_ctl_card_info_free(snd_ctl_card_info_t *obj)
{
@@ -2059,8 +2117,11 @@ void snd_ctl_card_info_free(snd_ctl_card_info_t *obj)
}
/**
- * \brief clear given #snd_ctl_card_info_t object
- * \param obj pointer to object to clear
+ * \brief Clear given card info object.
+ *
+ * See snd_ctl_elem_value_t for details.
+ *
+ * \param obj Card info object.
*/
void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj)
{
@@ -2068,9 +2129,10 @@ void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj)
}
/**
- * \brief copy one #snd_ctl_card_info_t to another
- * \param dst pointer to destination
- * \param src pointer to source
+ * \brief Bitwise copy of a #snd_ctl_card_info_t object.
+ *
+ * \param dst Pointer to destination.
+ * \param src Pointer to source.
*/
void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t *src)
{
@@ -2079,9 +2141,12 @@ void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t
}
/**
- * \brief Get card number from a CTL card info
- * \param obj CTL card info
- * \return card number
+ * \brief Get the sound card index from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound card index.
*/
int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj)
{
@@ -2090,9 +2155,12 @@ int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card identifier from a CTL card info
- * \param obj CTL card info
- * \return card identifier
+ * \brief Get the sound card ID from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound card ID.
*/
const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj)
{
@@ -2101,9 +2169,12 @@ const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card driver name from a CTL card info
- * \param obj CTL card info
- * \return card driver name
+ * \brief Get the sound card driver from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return The sound card driver.
*/
const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj)
{
@@ -2112,9 +2183,12 @@ const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card name from a CTL card info
- * \param obj CTL card info
- * \return card name
+ * \brief Get the sound card name from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound card name.
*/
const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj)
{
@@ -2123,9 +2197,12 @@ const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card long name from a CTL card info
- * \param obj CTL card info
- * \return card long name
+ * \brief Get the sound cards long name from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound cards long name.
*/
const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj)
{
@@ -2134,9 +2211,12 @@ const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card mixer name from a CTL card info
- * \param obj CTL card info
- * \return card mixer name
+ * \brief Get the sound card mixer name from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound card mixer name.
*/
const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj)
{
@@ -2145,9 +2225,12 @@ const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj)
}
/**
- * \brief Get card component list from a CTL card info
- * \param obj CTL card info
- * \return card mixer identifier
+ * \brief Get the sound cards "components" property from the given info object.
+ *
+ * See snd_ctl_card_info_t for more details.
+ *
+ * \param obj The card info object.
+ * \return Sound cards "components" property.
*/
const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj)
{
--
2.31.1
From 7ba3f888d07cafbad04391b915db23408c663dad Mon Sep 17 00:00:00 2001
From: "Tanjeff-N. Moos" <tanjeff@cccmz.de>
Date: Thu, 17 Jun 2021 10:36:39 +0200
Subject: [PATCH 10/20] control: Minor documentation fixes.
Signed-off-by: Tanjeff-N. Moos <tanjeff@cccmz.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/control.h | 9 +++++++--
src/control/cards.c | 5 ++++-
src/control/control.c | 2 +-
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/include/control.h b/include/control.h
index 9ebb4905..e386ecec 100644
--- a/include/control.h
+++ b/include/control.h
@@ -155,7 +155,7 @@ typedef struct _snd_ctl_elem_list snd_ctl_elem_list_t;
/** CTL element info container */
typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t;
-/** CTL element value container
+/** CTL element value container.
*
* Contains the value(s) (i.e. members) of a single element. All
* values of a given element are of the same type.
@@ -167,6 +167,11 @@ typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t;
* using the latter, it must be freed again using
* snd_ctl_elem_value_free().
*
+ * A value object can be zeroed out using snd_ctl_elem_value_clear().
+ *
+ * A value object can be copied to another one using
+ * snd_ctl_elem_value_copy().
+ *
* \par Identifier
*
* Then, the ID must be filled. It is sufficient to fill only the
@@ -621,7 +626,7 @@ size_t snd_ctl_elem_value_sizeof(void);
* \brief Allocate an invalid #snd_ctl_elem_value_t on the stack.
*
* Allocate space for a value object on the stack. The allocated
- * memory need not be freed, because is on the stack.
+ * memory need not be freed, because it is on the stack.
*
* See snd_ctl_elem_value_t for details.
*
diff --git a/src/control/cards.c b/src/control/cards.c
index 8226c42d..6145ebcd 100644
--- a/src/control/cards.c
+++ b/src/control/cards.c
@@ -147,6 +147,7 @@ int snd_card_get_index(const char *string)
return -EINVAL;
if ((isdigit(*string) && *(string + 1) == 0) ||
(isdigit(*string) && isdigit(*(string + 1)) && *(string + 2) == 0)) {
+ /* We got an index */
if (sscanf(string, "%i", &card) != 1)
return -EINVAL;
if (card < 0 || card >= SND_MAX_CARDS)
@@ -156,8 +157,10 @@ int snd_card_get_index(const char *string)
return card;
return err;
}
- if (string[0] == '/') /* device name */
+ if (string[0] == '/')
+ /* We got a device name */
return snd_card_load2(string);
+ /* We got in ID */
for (card = 0; card < SND_MAX_CARDS; card++) {
#ifdef SUPPORT_ALOAD
if (! snd_card_load(card))
diff --git a/src/control/control.c b/src/control/control.c
index 7cf4decb..91415b51 100644
--- a/src/control/control.c
+++ b/src/control/control.c
@@ -3127,7 +3127,7 @@ size_t snd_ctl_elem_value_sizeof()
/**
* \brief Allocate an invalid #snd_ctl_elem_value_t on the heap.
*
- * Allocate space for a value object on the head. The allocated memory
+ * Allocate space for a value object on the heap. The allocated memory
* must be freed using snd_ctl_elem_value_free().
*
* See snd_ctl_elem_value_t for details.
--
2.31.1
From f4f29d42be8b8ad60ea4c5697374adad4bfe6868 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 28 Jun 2021 12:08:53 +0200
Subject: [PATCH 11/20] fix build with --disable-ucm
Link: https://mailman.alsa-project.org/pipermail/alsa-devel/2021-June/186729.html
Reported-by: Michael Forney <mforney@mforney.org>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
include/local.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/local.h b/include/local.h
index 4e7d88a0..7cfcec53 100644
--- a/include/local.h
+++ b/include/local.h
@@ -374,6 +374,8 @@ int _snd_config_load_with_include(snd_config_t *config, snd_input_t *in,
void *INTERNAL(snd_dlopen)(const char *name, int mode, char *errbuf, size_t errbuflen);
#endif
+#ifdef BUILD_UCM
+
const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name);
static inline int _snd_is_ucm_device(const char *name)
@@ -381,4 +383,12 @@ static inline int _snd_is_ucm_device(const char *name)
return name && name[0] == '_' && name[1] == 'u' && name[2] == 'c' && name[3] == 'm';
}
+#else
+
+static inline const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name) { return NULL; }
+static inline int _snd_is_ucm_device(const char *name) { return 0; }
+
+
+#endif
+
#endif
--
2.31.1
From 1a1f0fb244c477c430e156da878475ef57d198f9 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 28 Jun 2021 12:11:54 +0200
Subject: [PATCH 12/20] pcm: rate - fix some gcc warnings
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/pcm/pcm_rate.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index 13111d29..c45895a9 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -82,12 +82,12 @@ struct _snd_pcm_rate {
/* allocate a channel area and a temporary buffer for the given size */
static snd_pcm_channel_area_t *
-rate_alloc_tmp_buf(snd_pcm_rate_t *rate, snd_pcm_format_t format,
+rate_alloc_tmp_buf(snd_pcm_format_t format,
unsigned int channels, unsigned int frames)
{
snd_pcm_channel_area_t *ap;
int width = snd_pcm_format_physical_width(format);
- int i;
+ unsigned int i;
ap = malloc(sizeof(*ap) * channels);
if (!ap)
@@ -354,7 +354,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_rate_side_info_t *sinfo, *cinfo;
- unsigned int channels, cwidth, swidth, chn, acc;
+ unsigned int channels, acc;
int need_src_buf, need_dst_buf;
int err = snd_pcm_hw_params_slave(pcm, params,
snd_pcm_rate_hw_refine_cchange,
@@ -401,9 +401,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
return -EBUSY;
}
- rate->pareas = rate_alloc_tmp_buf(rate, cinfo->format, channels,
+ rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
cinfo->period_size);
- rate->sareas = rate_alloc_tmp_buf(rate, sinfo->format, channels,
+ rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
sinfo->period_size);
if (!rate->pareas || !rate->sareas) {
err = -ENOMEM;
@@ -442,7 +442,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
rate->src_conv_idx =
snd_pcm_linear_convert_index(rate->orig_in_format,
rate->info.in.format);
- rate->src_buf = rate_alloc_tmp_buf(rate, rate->info.in.format,
+ rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
channels, rate->info.in.period_size);
if (!rate->src_buf) {
err = -ENOMEM;
@@ -454,7 +454,7 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
rate->dst_conv_idx =
snd_pcm_linear_convert_index(rate->info.out.format,
rate->orig_out_format);
- rate->dst_buf = rate_alloc_tmp_buf(rate, rate->info.out.format,
+ rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
channels, rate->info.out.period_size);
if (!rate->dst_buf) {
err = -ENOMEM;
--
2.31.1
From e47c11822d6b459a9b3704b3ee6a4a5c9a1b85be Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 29 Jun 2021 18:02:27 +0200
Subject: [PATCH 13/20] control: remap - assign right name to the child handle
for no-op
Fixes: https://github.com/alsa-project/alsa-utils/issues/100
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/control/control_remap.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/control/control_remap.c b/src/control/control_remap.c
index a85c1725..81524014 100644
--- a/src/control/control_remap.c
+++ b/src/control/control_remap.c
@@ -1173,6 +1173,10 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
/* no-op check, remove the plugin */
if (priv->map_items == 0 && priv->remap_items == 0) {
remap_free(priv);
+ free(child->name);
+ child->name = name ? strdup(name) : NULL;
+ if (name && !child->name)
+ return -ENOMEM;
*handlep = child;
return 0;
}
--
2.31.1
From 23a191a82c693456e61431ab699cddc1e5782a26 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 29 Jun 2021 19:31:28 +0200
Subject: [PATCH 14/20] control: remap - assign right name to the child handle
for no-op (2nd case)
Fixes: https://github.com/alsa-project/alsa-utils/issues/100
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/control/control_remap.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/control/control_remap.c b/src/control/control_remap.c
index 81524014..4914f960 100644
--- a/src/control/control_remap.c
+++ b/src/control/control_remap.c
@@ -1154,6 +1154,10 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
snd_ctl_t *ctl;
int result, err;
+ /* no-op, remove the plugin */
+ if (!remap && !map)
+ goto _noop;
+
priv = calloc(1, sizeof(*priv));
if (priv == NULL)
return -ENOMEM;
@@ -1173,6 +1177,7 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema
/* no-op check, remove the plugin */
if (priv->map_items == 0 && priv->remap_items == 0) {
remap_free(priv);
+ _noop:
free(child->name);
child->name = name ? strdup(name) : NULL;
if (name && !child->name)
@@ -1316,11 +1321,6 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd
err = _snd_ctl_open_child(&cctl, root, child, mode, conf);
if (err < 0)
return err;
- /* no-op, remove the plugin */
- if (!remap && !map) {
- *handlep = cctl;
- return 0;
- }
err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode);
if (err < 0)
snd_ctl_close(cctl);
--
2.31.1
From 7d40a76ef5494e08af00fa4e7bfefbd43aba4827 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 31 Aug 2021 09:25:12 +0200
Subject: [PATCH 19/20] ucm: avoid zero card instance number
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/ucm/utils.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index f3a8c9ba..10b21c34 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -780,6 +780,9 @@ int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
while (uc_mgr_card_find(ucm_card_assign)) {
ucm_card_assign++;
ucm_card_assign &= 0xffff;
+ /* avoid zero card instance number */
+ if (ucm_card_assign == 0)
+ ucm_card_assign++;
if (ucm_card_assign == prev) {
pthread_mutex_unlock(&ucm_cards_mutex);
return -ENOMEM;
--
2.31.1
From 4a52ae4c329ae17117375a4b85b80f37994a4044 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 31 Aug 2021 09:40:42 +0200
Subject: [PATCH 20/20] ucm: fix the parsing of the hexadecimal prefix
The safe_strtol() function use strtol() which expects
to have the '0x' prefix for the hexadecimal number (when
base argument is zero).
BugLink: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/1553
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/ucm/utils.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index 10b21c34..2fbc4c8c 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -811,7 +811,7 @@ void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
*/
const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
{
- char buf[5];
+ char buf[7];
long card_num;
snd_config_t *config;
snd_use_case_mgr_t *uc_mgr;
@@ -819,8 +819,10 @@ const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
return NULL;
- strncpy(buf, name + 4, 4);
- buf[4] = '\0';
+ buf[0] = '0';
+ buf[1] = 'x';
+ strncpy(buf + 2, name + 4, 4);
+ buf[6] = '\0';
err = safe_strtol(buf, &card_num);
if (err < 0 || card_num < 0 || card_num > 0xffff)
return NULL;
--
2.31.1