* [PATCH 0/3] alsa-lib - improve surround 2.1 support
@ 2014-02-21 15:24 David Henningsson
2014-02-21 15:24 ` [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable David Henningsson
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: David Henningsson @ 2014-02-21 15:24 UTC (permalink / raw)
To: tiwai, alsa-devel; +Cc: David Henningsson
So, in order to support 2.1 surround on both 4-channels (e g FL FR LFE LFE,
on internal subwoofer) and 6-channels (e g FL FR RL RR C LFE, on desktops
with 5.1 out), I needed to make the route plugin more chmap aware.
This also included allowing a new syntax for the ttable, which I think is
quite nice. The new syntax also falls back to ALSAs standard chmap in case
the hardware does not support it.
David Henningsson (3):
route: Allow chmap syntax for slave channels in ttable
route: Select slave chmap based on ttable information
conf: Allow 2.1 surround to use different number of channels
src/conf/pcm/surround21.conf | 7 +-
src/pcm/pcm_route.c | 304 ++++++++++++++++++++++++++++++++++++++-----
2 files changed, 271 insertions(+), 40 deletions(-)
--
1.9.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable
2014-02-21 15:24 [PATCH 0/3] alsa-lib - improve surround 2.1 support David Henningsson
@ 2014-02-21 15:24 ` David Henningsson
2014-02-21 15:42 ` Takashi Iwai
2014-02-21 15:24 ` [PATCH 2/3] route: Select slave chmap based on ttable information David Henningsson
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: David Henningsson @ 2014-02-21 15:24 UTC (permalink / raw)
To: tiwai, alsa-devel; +Cc: David Henningsson
Instead of writing e g "0" and "1", one can now write "FL" and "FR" instead.
E g:
ttable.0.FL 1
ttable.1.FR 1
ttable.2.LFE 1
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
---
src/pcm/pcm_route.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
index 2beedf6..ffc283f 100644
--- a/src/pcm/pcm_route.c
+++ b/src/pcm/pcm_route.c
@@ -789,6 +789,24 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
snd_pcm_dump(route->plug.gen.slave, out);
}
+static int safe_chmapchannel(const char *id, long *channel)
+{
+ int err;
+ int ch;
+ err = safe_strtol(id, channel);
+ if (err >= 0)
+ return err;
+
+ ch = (int) snd_pcm_chmap_from_string(id);
+ if (ch == -1)
+ return -EINVAL;
+
+ /* For now, assume standard channel mapping */
+ *channel = ch - SND_CHMAP_FL;
+ return 0;
+}
+
+
static const snd_pcm_ops_t snd_pcm_route_ops = {
.close = snd_pcm_route_close,
.info = snd_pcm_generic_info,
@@ -983,7 +1001,7 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
const char *id;
if (snd_config_get_id(jnode, &id) < 0)
continue;
- err = safe_strtol(id, &schannel);
+ err = safe_chmapchannel(id, &schannel);
if (err < 0) {
SNDERR("Invalid slave channel: %s", id);
return -EINVAL;
@@ -1046,7 +1064,7 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
const char *id;
if (snd_config_get_id(jnode, &id) < 0)
continue;
- err = safe_strtol(id, &schannel);
+ err = safe_chmapchannel(id, &schannel);
if (err < 0 ||
schannel < 0 || (unsigned int) schannel > tt_ssize ||
(schannels > 0 && schannel >= schannels)) {
--
1.9.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/3] route: Select slave chmap based on ttable information
2014-02-21 15:24 [PATCH 0/3] alsa-lib - improve surround 2.1 support David Henningsson
2014-02-21 15:24 ` [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable David Henningsson
@ 2014-02-21 15:24 ` David Henningsson
2014-02-21 15:49 ` Takashi Iwai
2014-02-21 15:24 ` [PATCH 3/3] conf: Allow 2.1 surround to use different number of channels David Henningsson
2014-02-21 15:50 ` [PATCH 0/3] alsa-lib - improve surround 2.1 support Takashi Iwai
3 siblings, 1 reply; 7+ messages in thread
From: David Henningsson @ 2014-02-21 15:24 UTC (permalink / raw)
To: tiwai, alsa-devel; +Cc: David Henningsson
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>
---
src/pcm/pcm_route.c | 300 ++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 257 insertions(+), 43 deletions(-)
diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
index ffc283f..355da1e 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,168 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
snd_pcm_dump(route->plug.gen.slave, out);
}
-static int safe_chmapchannel(const char *id, long *channel)
+static int safe_chmapchannel(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);
+ 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;
+
+ 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 +1088,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 +1113,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 +1145,7 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
const char *id;
if (snd_config_get_id(jnode, &id) < 0)
continue;
- err = safe_chmapchannel(id, &schannel);
+ err = safe_chmapchannel(id, chmap, &schannel, 1);
if (err < 0) {
SNDERR("Invalid slave channel: %s", id);
return -EINVAL;
@@ -1020,6 +1164,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 +1188,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 +1218,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 = safe_chmapchannel(id, &schannel);
- if (err < 0 ||
- schannel < 0 || (unsigned int) schannel > tt_ssize ||
- (schannels > 0 && schannel >= schannels)) {
+
+ ss = safe_chmapchannel(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 +1240,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 +1261,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 +1288,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 +1341,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 +1390,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
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/3] conf: Allow 2.1 surround to use different number of channels
2014-02-21 15:24 [PATCH 0/3] alsa-lib - improve surround 2.1 support David Henningsson
2014-02-21 15:24 ` [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable David Henningsson
2014-02-21 15:24 ` [PATCH 2/3] route: Select slave chmap based on ttable information David Henningsson
@ 2014-02-21 15:24 ` David Henningsson
2014-02-21 15:50 ` [PATCH 0/3] alsa-lib - improve surround 2.1 support Takashi Iwai
3 siblings, 0 replies; 7+ messages in thread
From: David Henningsson @ 2014-02-21 15:24 UTC (permalink / raw)
To: tiwai, alsa-devel; +Cc: David Henningsson
This way, cards that support LFE on four channels (e g laptop with
internal subwoofer) can do that, and other cards on a six channel setup
can use that as well.
Well, note that there is still a reference to "pcm.surround51" left here.
In practice, for HDA Intel sound cards this does not matter as both
surround51 and surround40 reference the same definition.
(And that's the only card I currently know of that actually does
surround2.1 over four channels.)
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
---
src/conf/pcm/surround21.conf | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/conf/pcm/surround21.conf b/src/conf/pcm/surround21.conf
index be29020..7f4676b 100644
--- a/src/conf/pcm/surround21.conf
+++ b/src/conf/pcm/surround21.conf
@@ -51,10 +51,9 @@ pcm.!surround21 {
]
}
}
- slave.channels 6
- ttable.0.0 1
- ttable.1.1 1
- ttable.2.5 1
+ ttable.0.FL 1
+ ttable.1.FR 1
+ ttable.2.LFE 1
hint {
description "2.1 Surround output to Front and Subwoofer speakers"
device $DEV
--
1.9.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable
2014-02-21 15:24 ` [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable David Henningsson
@ 2014-02-21 15:42 ` Takashi Iwai
0 siblings, 0 replies; 7+ messages in thread
From: Takashi Iwai @ 2014-02-21 15:42 UTC (permalink / raw)
To: David Henningsson; +Cc: alsa-devel
At Fri, 21 Feb 2014 16:24:20 +0100,
David Henningsson wrote:
>
> Instead of writing e g "0" and "1", one can now write "FL" and "FR" instead.
>
> E g:
> ttable.0.FL 1
> ttable.1.FR 1
> ttable.2.LFE 1
>
> Signed-off-by: David Henningsson <david.henningsson@canonical.com>
> ---
> src/pcm/pcm_route.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
> index 2beedf6..ffc283f 100644
> --- a/src/pcm/pcm_route.c
> +++ b/src/pcm/pcm_route.c
> @@ -789,6 +789,24 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
> snd_pcm_dump(route->plug.gen.slave, out);
> }
>
> +static int safe_chmapchannel(const char *id, long *channel)
You need no safe_ prefix for this function. safe_strtol() is just
because strtol() already exists in an unsafe way.
Besides that, I find it also OK to just return the channel (>= 0) or a
negative error. But it's no big matter in which form is.
Takashi
> +{
> + int err;
> + int ch;
> + err = safe_strtol(id, channel);
> + if (err >= 0)
> + return err;
> +
> + ch = (int) snd_pcm_chmap_from_string(id);
> + if (ch == -1)
> + return -EINVAL;
> +
> + /* For now, assume standard channel mapping */
> + *channel = ch - SND_CHMAP_FL;
> + return 0;
> +}
> +
> +
> static const snd_pcm_ops_t snd_pcm_route_ops = {
> .close = snd_pcm_route_close,
> .info = snd_pcm_generic_info,
> @@ -983,7 +1001,7 @@ int snd_pcm_route_determine_ttable(snd_config_t *tt,
> const char *id;
> if (snd_config_get_id(jnode, &id) < 0)
> continue;
> - err = safe_strtol(id, &schannel);
> + err = safe_chmapchannel(id, &schannel);
> if (err < 0) {
> SNDERR("Invalid slave channel: %s", id);
> return -EINVAL;
> @@ -1046,7 +1064,7 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt
> const char *id;
> if (snd_config_get_id(jnode, &id) < 0)
> continue;
> - err = safe_strtol(id, &schannel);
> + err = safe_chmapchannel(id, &schannel);
> if (err < 0 ||
> schannel < 0 || (unsigned int) schannel > tt_ssize ||
> (schannels > 0 && schannel >= schannels)) {
> --
> 1.9.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/3] route: Select slave chmap based on ttable information
2014-02-21 15:24 ` [PATCH 2/3] route: Select slave chmap based on ttable information David Henningsson
@ 2014-02-21 15:49 ` Takashi Iwai
0 siblings, 0 replies; 7+ messages in thread
From: Takashi Iwai @ 2014-02-21 15:49 UTC (permalink / raw)
To: David Henningsson; +Cc: alsa-devel
At Fri, 21 Feb 2014 16:24:21 +0100,
David Henningsson wrote:
>
> 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>
> ---
> src/pcm/pcm_route.c | 300 ++++++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 257 insertions(+), 43 deletions(-)
>
> diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c
> index ffc283f..355da1e 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,168 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
> snd_pcm_dump(route->plug.gen.slave, out);
> }
>
> -static int safe_chmapchannel(const char *id, long *channel)
> +static int safe_chmapchannel(const char *id, snd_pcm_chmap_t *chmap,
> + long *channel, int channel_size)
A brief description of the function would be helpful, as the code
becomes no longer trivial by this patch.
> {
> - 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);
NULL check is missing here.
> + 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;
> +
> + 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);
Please fix the indentation.
Takashi
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 0/3] alsa-lib - improve surround 2.1 support
2014-02-21 15:24 [PATCH 0/3] alsa-lib - improve surround 2.1 support David Henningsson
` (2 preceding siblings ...)
2014-02-21 15:24 ` [PATCH 3/3] conf: Allow 2.1 surround to use different number of channels David Henningsson
@ 2014-02-21 15:50 ` Takashi Iwai
3 siblings, 0 replies; 7+ messages in thread
From: Takashi Iwai @ 2014-02-21 15:50 UTC (permalink / raw)
To: David Henningsson; +Cc: alsa-devel
At Fri, 21 Feb 2014 16:24:19 +0100,
David Henningsson wrote:
>
> So, in order to support 2.1 surround on both 4-channels (e g FL FR LFE LFE,
> on internal subwoofer) and 6-channels (e g FL FR RL RR C LFE, on desktops
> with 5.1 out), I needed to make the route plugin more chmap aware.
>
> This also included allowing a new syntax for the ttable, which I think is
> quite nice. The new syntax also falls back to ALSAs standard chmap in case
> the hardware does not support it.
The idea looks great, a nice improvement.
Spotted a few trivial things I posted individually, but in general
it's in a good shape.
thanks,
Takashi
>
> David Henningsson (3):
> route: Allow chmap syntax for slave channels in ttable
> route: Select slave chmap based on ttable information
> conf: Allow 2.1 surround to use different number of channels
>
> src/conf/pcm/surround21.conf | 7 +-
> src/pcm/pcm_route.c | 304 ++++++++++++++++++++++++++++++++++++++-----
> 2 files changed, 271 insertions(+), 40 deletions(-)
>
> --
> 1.9.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2014-02-21 15:51 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-21 15:24 [PATCH 0/3] alsa-lib - improve surround 2.1 support David Henningsson
2014-02-21 15:24 ` [PATCH 1/3] route: Allow chmap syntax for slave channels in ttable David Henningsson
2014-02-21 15:42 ` Takashi Iwai
2014-02-21 15:24 ` [PATCH 2/3] route: Select slave chmap based on ttable information David Henningsson
2014-02-21 15:49 ` Takashi Iwai
2014-02-21 15:24 ` [PATCH 3/3] conf: Allow 2.1 surround to use different number of channels David Henningsson
2014-02-21 15:50 ` [PATCH 0/3] alsa-lib - improve surround 2.1 support Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox