- Update alsa-info.sh to match dmesg with snd_ - Run spec-cleaner - Add missing Loopback.conf manually - Upstream fixes: minor clean ups, 2.1 PCM channel config support, portability improvements, PCM float format conversion fix, fix for ioplug 32bit hwptr wrap, etc. 0020-minor-malloc-changes.patch 0021-alsa-lib-minor-typo-in-pcm-doc.patch 0022-control_hw-fix-potential-memory-leak.patch 0023-pcm-initialize-pcm_dmix-monotonic-field.patch 0024-pcm-initialize-monotonic-field-for-dshare-and-dsnoop.patch 0025-pcm-use-CLOCK_REALTIME-for-non-monotonic-ts.patch 0026-alsa-lib-heavy-pcm-atomics-cleanup.patch 0027-conf-Add-basic-infrastructure-for-2.1-surround-sound.patch 0028-conf-Add-surround-2.1-support-to-all-cards.patch 0029-Portability-fix-look-for-sys-endian.h-as-well-as-end.patch 0030-autotools-update-style.patch 0031-pcm-Insert-linear-to-float-conversion-when-rate-or-c.patch 0032-pcm-route-Allow-chmap-syntax-for-slave-channels-in-t.patch 0033-pcm-route-Select-slave-chmap-based-on-ttable-informa.patch 0034-conf-Allow-2.1-surround-to-use-different-number-of-c.patch 0035-pcm-Wrap-hw_ptr-to-boundary-in-pcm_ioplug.patch 0036-src-conf-cards-Add-missing-entry-for-Loopback.conf.patch OBS-URL: https://build.opensuse.org/request/show/225572 OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/alsa?expand=0&rev=152
476 lines
14 KiB
Diff
476 lines
14 KiB
Diff
From 5c4cd46810cef8850b037fca9e38ffd43b0bff22 Mon Sep 17 00:00:00 2001
|
|
From: David Henningsson <david.henningsson@canonical.com>
|
|
Date: Fri, 28 Feb 2014 08:57:06 +0100
|
|
Subject: [PATCH] pcm: route: Select slave chmap based on ttable information
|
|
|
|
It means we need to initialize this order:
|
|
|
|
1) Read the ttable to figure out which channels are present
|
|
2) Open slave pcm and find a matching chmap
|
|
3) Determine size of ttable (this can now depend on the chmap)
|
|
4) Read ttable coefficients
|
|
5) At prepare time, select the matching chmap
|
|
|
|
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
|
|
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
|
---
|
|
src/pcm/pcm_route.c | 319 +++++++++++++++++++++++++++++++++++++++++++++-------
|
|
1 file changed, 276 insertions(+), 43 deletions(-)
|
|
|
|
diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
|
|
index 56318d47bcbf..ab17fa78be2c 100644
|
|
--- a/src/pcm/pcm_route.c
|
|
+++ b/src/pcm/pcm_route.c
|
|
@@ -103,6 +103,7 @@ typedef struct {
|
|
snd_pcm_format_t sformat;
|
|
int schannels;
|
|
snd_pcm_route_params_t params;
|
|
+ snd_pcm_chmap_t *chmap;
|
|
} snd_pcm_route_t;
|
|
|
|
#endif /* DOC_HIDDEN */
|
|
@@ -518,6 +519,7 @@ static int snd_pcm_route_close(snd_pcm_t *pcm)
|
|
}
|
|
free(params->dsts);
|
|
}
|
|
+ free(route->chmap);
|
|
return snd_pcm_generic_close(pcm);
|
|
}
|
|
|
|
@@ -789,21 +791,187 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
snd_pcm_dump(route->plug.gen.slave, out);
|
|
}
|
|
|
|
-static int strtochannel(const char *id, long *channel)
|
|
+/*
|
|
+ * Converts a string to an array of channel indices:
|
|
+ * - Given a number, the result is an array with one element,
|
|
+ * containing that number
|
|
+ * - Given a channel name (e g "FL") and a chmap,
|
|
+ * it will look this up in the chmap and return all matches
|
|
+ * - Given a channel name and no chmap, the result is an array with one element,
|
|
+ containing alsa standard channel map. Note that this might be a negative
|
|
+ number in case of "UNKNOWN", "NA" or "MONO".
|
|
+ * Return value is number of matches written.
|
|
+ */
|
|
+static int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
|
|
+ long *channel, int channel_size)
|
|
{
|
|
- int err;
|
|
int ch;
|
|
- err = safe_strtol(id, channel);
|
|
- if (err >= 0)
|
|
- return err;
|
|
+ if (safe_strtol(id, channel) >= 0)
|
|
+ return 1;
|
|
|
|
ch = (int) snd_pcm_chmap_from_string(id);
|
|
if (ch == -1)
|
|
return -EINVAL;
|
|
|
|
- /* For now, assume standard channel mapping */
|
|
- *channel = ch - SND_CHMAP_FL;
|
|
+ if (chmap) {
|
|
+ int i, r = 0;
|
|
+ /* Start with highest channel to simplify implementation of
|
|
+ determine ttable size */
|
|
+ for (i = chmap->channels - 1; i >= 0; i--) {
|
|
+ if ((int) chmap->pos[i] != ch)
|
|
+ continue;
|
|
+ if (r >= channel_size)
|
|
+ continue;
|
|
+ channel[r++] = i;
|
|
+ }
|
|
+ return r;
|
|
+ }
|
|
+ else {
|
|
+ /* Assume ALSA standard channel mapping */
|
|
+ *channel = ch - SND_CHMAP_FL;
|
|
+ return 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+#define MAX_CHMAP_CHANNELS 256
|
|
+
|
|
+static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
|
|
+{
|
|
+ snd_config_iterator_t i, inext;
|
|
+ snd_pcm_chmap_t *chmap;
|
|
+
|
|
+ assert(tt && tt_chmap);
|
|
+ chmap = malloc(sizeof(snd_pcm_chmap_t) +
|
|
+ MAX_CHMAP_CHANNELS * sizeof(unsigned int));
|
|
+
|
|
+ chmap->channels = 0;
|
|
+ snd_config_for_each(i, inext, tt) {
|
|
+ const char *id;
|
|
+ snd_config_iterator_t j, jnext;
|
|
+ snd_config_t *in = snd_config_iterator_entry(i);
|
|
+
|
|
+ if (!snd_config_get_id(in, &id) < 0)
|
|
+ continue;
|
|
+ if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
|
+ goto err;
|
|
+ snd_config_for_each(j, jnext, in) {
|
|
+ int ch, k, found;
|
|
+ long schannel;
|
|
+ snd_config_t *jnode = snd_config_iterator_entry(j);
|
|
+ if (snd_config_get_id(jnode, &id) < 0)
|
|
+ continue;
|
|
+ if (safe_strtol(id, &schannel) >= 0)
|
|
+ continue;
|
|
+ ch = (int) snd_pcm_chmap_from_string(id);
|
|
+ if (ch == -1)
|
|
+ goto err;
|
|
+
|
|
+ found = 0;
|
|
+ for (k = 0; k < (int) chmap->channels; k++)
|
|
+ if (ch == (int) chmap->pos[k]) {
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ if (found)
|
|
+ continue;
|
|
+
|
|
+ if (chmap->channels >= MAX_CHMAP_CHANNELS) {
|
|
+ SNDERR("Too many channels in ttable chmap");
|
|
+ goto err;
|
|
+ }
|
|
+ chmap->pos[chmap->channels++] = ch;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ *tt_chmap = chmap;
|
|
return 0;
|
|
+
|
|
+err:
|
|
+ *tt_chmap = NULL;
|
|
+ free(chmap);
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int find_matching_chmap(snd_pcm_t *spcm, snd_pcm_chmap_t *tt_chmap,
|
|
+ snd_pcm_chmap_t **found_chmap, int *schannels)
|
|
+{
|
|
+ snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(spcm);
|
|
+ int i;
|
|
+
|
|
+ *found_chmap = NULL;
|
|
+
|
|
+ if (chmaps == NULL)
|
|
+ return 0; /* chmap API not supported for this slave */
|
|
+
|
|
+ for (i = 0; chmaps[i]; i++) {
|
|
+ unsigned int j, k;
|
|
+ int match = 1;
|
|
+ snd_pcm_chmap_t *c = &chmaps[i]->map;
|
|
+ if (*schannels >= 0 && (int) c->channels != *schannels)
|
|
+ continue;
|
|
+
|
|
+ for (j = 0; j < tt_chmap->channels; j++) {
|
|
+ int found = 0;
|
|
+ unsigned int ch = tt_chmap->pos[j];
|
|
+ for (k = 0; k < c->channels; k++)
|
|
+ if (c->pos[k] == ch) {
|
|
+ found = 1;
|
|
+ break;
|
|
+ }
|
|
+ if (!found) {
|
|
+ match = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (match) {
|
|
+ int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
|
|
+ *found_chmap = malloc(size);
|
|
+ if (!*found_chmap) {
|
|
+ snd_pcm_free_chmaps(chmaps);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memcpy(*found_chmap, c, size);
|
|
+ *schannels = c->channels;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ snd_pcm_free_chmaps(chmaps);
|
|
+
|
|
+ if (*found_chmap == NULL) {
|
|
+ SNDERR("Found no matching channel map");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int route_chmap_init(snd_pcm_t *pcm)
|
|
+{
|
|
+ int set_map = 0;
|
|
+ snd_pcm_chmap_t *current;
|
|
+ snd_pcm_route_t *route = pcm->private_data;
|
|
+ if (!route->chmap)
|
|
+ return 0;
|
|
+ if (snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
|
|
+ return 0;
|
|
+
|
|
+ /* Check if we really need to set the chmap or not.
|
|
+ This is important in case set_chmap is not implemented. */
|
|
+ current = snd_pcm_get_chmap(route->plug.gen.slave);
|
|
+ if (!current)
|
|
+ return -ENOSYS;
|
|
+ if (current->channels != route->chmap->channels)
|
|
+ set_map = 1;
|
|
+ else
|
|
+ set_map = memcmp(current->pos, route->chmap->pos,
|
|
+ current->channels);
|
|
+ free(current);
|
|
+ if (!set_map)
|
|
+ return 0;
|
|
+
|
|
+ return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
|
|
}
|
|
|
|
|
|
@@ -939,6 +1107,7 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
|
route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
|
|
route->plug.gen.slave = slave;
|
|
route->plug.gen.close_slave = close_slave;
|
|
+ route->plug.init = route_chmap_init;
|
|
|
|
err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
|
|
if (err < 0) {
|
|
@@ -963,16 +1132,10 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
|
return 0;
|
|
}
|
|
|
|
-/**
|
|
- * \brief Determine route matrix sizes
|
|
- * \param tt Configuration root describing route matrix
|
|
- * \param tt_csize Returned client size in elements
|
|
- * \param tt_ssize Returned slave size in elements
|
|
- * \retval zero on success otherwise a negative error code
|
|
- */
|
|
-int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
- unsigned int *tt_csize,
|
|
- unsigned int *tt_ssize)
|
|
+static int _snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
+ unsigned int *tt_csize,
|
|
+ unsigned int *tt_ssize,
|
|
+ snd_pcm_chmap_t *chmap)
|
|
{
|
|
snd_config_iterator_t i, inext;
|
|
long csize = 0, ssize = 0;
|
|
@@ -1001,7 +1164,7 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
const char *id;
|
|
if (snd_config_get_id(jnode, &id) < 0)
|
|
continue;
|
|
- err = strtochannel(id, &schannel);
|
|
+ err = strtochannel(id, chmap, &schannel, 1);
|
|
if (err < 0) {
|
|
SNDERR("Invalid slave channel: %s", id);
|
|
return -EINVAL;
|
|
@@ -1020,6 +1183,20 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
}
|
|
|
|
/**
|
|
+ * \brief Determine route matrix sizes
|
|
+ * \param tt Configuration root describing route matrix
|
|
+ * \param tt_csize Returned client size in elements
|
|
+ * \param tt_ssize Returned slave size in elements
|
|
+ * \retval zero on success otherwise a negative error code
|
|
+ */
|
|
+int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
+ unsigned int *tt_csize,
|
|
+ unsigned int *tt_ssize)
|
|
+{
|
|
+ return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
|
|
+}
|
|
+
|
|
+/**
|
|
* \brief Load route matrix
|
|
* \param tt Configuration root describing route matrix
|
|
* \param ttable Returned route matrix
|
|
@@ -1030,10 +1207,10 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
|
|
* \param schannels Slave channels
|
|
* \retval zero on success otherwise a negative error code
|
|
*/
|
|
-int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
|
|
- unsigned int tt_csize, unsigned int tt_ssize,
|
|
- unsigned int *tt_cused, unsigned int *tt_sused,
|
|
- int schannels)
|
|
+static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
|
|
+ unsigned int tt_csize, unsigned int tt_ssize,
|
|
+ unsigned int *tt_cused, unsigned int *tt_sused,
|
|
+ int schannels, snd_pcm_chmap_t *chmap)
|
|
{
|
|
int cused = -1;
|
|
int sused = -1;
|
|
@@ -1060,17 +1237,18 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|
snd_config_for_each(j, jnext, in) {
|
|
snd_config_t *jnode = snd_config_iterator_entry(j);
|
|
double value;
|
|
- long schannel;
|
|
+ int ss;
|
|
+ long *scha = alloca(tt_ssize * sizeof(long));
|
|
const char *id;
|
|
if (snd_config_get_id(jnode, &id) < 0)
|
|
continue;
|
|
- err = strtochannel(id, &schannel);
|
|
- if (err < 0 ||
|
|
- schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
|
- (schannels > 0 && schannel >= schannels)) {
|
|
+
|
|
+ ss = strtochannel(id, chmap, scha, tt_ssize);
|
|
+ if (ss < 0) {
|
|
SNDERR("Invalid slave channel: %s", id);
|
|
return -EINVAL;
|
|
}
|
|
+
|
|
err = snd_config_get_real(jnode, &value);
|
|
if (err < 0) {
|
|
long v;
|
|
@@ -1081,9 +1259,18 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|
}
|
|
value = v;
|
|
}
|
|
- ttable[cchannel * tt_ssize + schannel] = value;
|
|
- if (schannel > sused)
|
|
- sused = schannel;
|
|
+
|
|
+ for (k = 0; (int) k < ss; k++) {
|
|
+ long schannel = scha[k];
|
|
+ if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
|
+ (schannels > 0 && schannel >= schannels)) {
|
|
+ SNDERR("Invalid slave channel: %s", id);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ ttable[cchannel * tt_ssize + schannel] = value;
|
|
+ if (schannel > sused)
|
|
+ sused = schannel;
|
|
+ }
|
|
}
|
|
if (cchannel > cused)
|
|
cused = cchannel;
|
|
@@ -1093,6 +1280,26 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * \brief Load route matrix
|
|
+ * \param tt Configuration root describing route matrix
|
|
+ * \param ttable Returned route matrix
|
|
+ * \param tt_csize Client size in elements
|
|
+ * \param tt_ssize Slave size in elements
|
|
+ * \param tt_cused Used client elements
|
|
+ * \param tt_sused Used slave elements
|
|
+ * \param schannels Slave channels
|
|
+ * \retval zero on success otherwise a negative error code
|
|
+ */
|
|
+int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
|
|
+ unsigned int tt_csize, unsigned int tt_ssize,
|
|
+ unsigned int *tt_cused, unsigned int *tt_sused,
|
|
+ int schannels)
|
|
+{
|
|
+ return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
|
|
+ tt_cused, tt_sused, schannels, NULL);
|
|
+}
|
|
+
|
|
/*! \page pcm_plugins
|
|
|
|
\section pcm_plugins_route Plugin: Route & Volume
|
|
@@ -1100,6 +1307,9 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
|
|
This plugin converts channels and applies volume during the conversion.
|
|
The format and rate must match for both of them.
|
|
|
|
+SCHANNEL can be a channel name instead of a number (e g FL, LFE).
|
|
+If so, a matching channel map will be selected for the slave.
|
|
+
|
|
\code
|
|
pcm.name {
|
|
type route # Route & Volume conversion PCM
|
|
@@ -1150,6 +1360,7 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
|
int err;
|
|
snd_pcm_t *spcm;
|
|
snd_config_t *slave = NULL, *sconf;
|
|
+ snd_pcm_chmap_t *tt_chmap, *chmap;
|
|
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
|
|
int schannels = -1;
|
|
snd_config_t *tt = NULL;
|
|
@@ -1198,37 +1409,59 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
|
|
+ err = determine_chmap(tt, &tt_chmap);
|
|
if (err < 0) {
|
|
- snd_config_delete(sconf);
|
|
+ free(ttable);
|
|
return err;
|
|
}
|
|
- ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
|
|
- if (ttable == NULL) {
|
|
- snd_config_delete(sconf);
|
|
- return -ENOMEM;
|
|
- }
|
|
- err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
|
|
- &cused, &sused, schannels);
|
|
+
|
|
+ err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
|
|
+ snd_config_delete(sconf);
|
|
if (err < 0) {
|
|
+ free(tt_chmap);
|
|
free(ttable);
|
|
- snd_config_delete(sconf);
|
|
return err;
|
|
}
|
|
|
|
- err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
|
|
- snd_config_delete(sconf);
|
|
+ if (tt_chmap) {
|
|
+ err = find_matching_chmap(spcm, tt_chmap, &chmap, &schannels);
|
|
+ free(tt_chmap);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
|
|
+ if (err < 0) {
|
|
+ free(chmap);
|
|
+ snd_pcm_close(spcm);
|
|
+ return err;
|
|
+ }
|
|
+ ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
|
|
+ if (ttable == NULL) {
|
|
+ free(chmap);
|
|
+ snd_pcm_close(spcm);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
|
|
+ &cused, &sused, schannels, chmap);
|
|
if (err < 0) {
|
|
+ free(chmap);
|
|
free(ttable);
|
|
+ snd_pcm_close(spcm);
|
|
return err;
|
|
}
|
|
+
|
|
err = snd_pcm_route_open(pcmp, name, sformat, schannels,
|
|
ttable, ssize,
|
|
cused, sused,
|
|
spcm, 1);
|
|
free(ttable);
|
|
- if (err < 0)
|
|
+ if (err < 0) {
|
|
+ free(chmap);
|
|
snd_pcm_close(spcm);
|
|
+ }
|
|
+ ((snd_pcm_route_t*) (*pcmp)->private_data)->chmap = chmap;
|
|
+
|
|
return err;
|
|
}
|
|
#ifndef DOC_HIDDEN
|
|
--
|
|
1.9.0
|
|
|