At Wed, 21 Nov 2007 12:40:54 +0100, I wrote:
At Tue, 20 Nov 2007 01:51:51 +0100, Lennart Poettering wrote:
- If I open an audio device with "plughw:" i can disable the software resampling that takes place via "snd_pcm_hw_params_get_rate_resample()". However, there is no equivalent for disabling the channel number adjustment or the sample format conversion. This would be very useful in PA however, since this would allow me to use only the parts of plughw I am interested in (softvol), and disable all the rest (resampling, conversion, channel remixing).
Hmm.. It's a bit difficult to implement in a clean way. For example, what would be the reason to disable softvol? It's basically irrelevant with the PCM parameters. It's added just because of lack of hardware volume controls.
One idea I have is to make an API like
snd_pcm_alias_plugin(src, dst);
For example,
snd_pcm_alias_plugin("softvol", "passthru");
would take passthru plugin instead of softvol plugin. (Suppose passthru plugin as a simply pass-through plugin to its slave.pcm)
The below is an experimental patch. We have already "empty" plugin as passthru. So, call snd_pcm_alias_plugin("softvol", "empty"); before snd_pcm_open() to suppress the softvol in all configs.
Takashi
diff -r 3539f279ec38 include/pcm.h --- a/include/pcm.h Wed Nov 21 12:19:43 2007 +0100 +++ b/include/pcm.h Wed Nov 21 16:03:51 2007 +0100 @@ -935,6 +935,7 @@ int snd_pcm_areas_copy(const snd_pcm_cha int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset, unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format); + int snd_pcm_alias_plugin(const char *plugin, const char *target);
/** } */
diff -r 3539f279ec38 src/pcm/pcm.c --- a/src/pcm/pcm.c Wed Nov 21 12:19:43 2007 +0100 +++ b/src/pcm/pcm.c Wed Nov 21 16:03:51 2007 +0100 @@ -1987,6 +1987,33 @@ static char *build_in_pcms[] = { NULL };
+/* plugin alias list */ +struct plugin_alias { + char *name; + char *target; + struct plugin_alias *next; +}; + +static struct plugin_alias *plugin_alias_list; + +/* find a plugin alias and set previous pointer */ +static struct plugin_alias *find_plugin_alias(const char *name, + struct plugin_alias **prevp) +{ + struct plugin_alias *c, *prev; + prev = NULL; + for (c = plugin_alias_list; c; prev = c, c = c->next) { + if (!strcmp(c->name, name)) { + if (prevp) + *prevp = prev; + return c; + } + } + if (prevp) + *prevp = prev; + return NULL; +} + static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name, snd_config_t *pcm_root, snd_config_t *pcm_conf, snd_pcm_stream_t stream, int mode) @@ -2004,6 +2031,7 @@ static int snd_pcm_open_conf(snd_pcm_t * #ifndef PIC extern void *snd_pcm_open_symbols(void); #endif + struct plugin_alias *palias; void *h = NULL; if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) { char *val; @@ -2030,6 +2058,10 @@ static int snd_pcm_open_conf(snd_pcm_t * SNDERR("Invalid type for %s", id); return err; } + + while ((palias = find_plugin_alias(str, NULL)) != NULL) + str = palias->target; + err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf); if (err >= 0) { if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) { @@ -2241,6 +2273,67 @@ int snd_pcm_open_slave(snd_pcm_t **pcmp, return snd_pcm_open_conf(pcmp, NULL, root, conf, stream, mode); } #endif + +/** + * \brief Make or remove an alias for a plugin + * \param plugin The plugin + * \param target The name of the aliased target plugin + * \return zero if successful or a negative error code + */ +int snd_pcm_alias_plugin(const char *plugin, const char *target) +{ + struct plugin_alias *c, *prev; + + if (target) { + /* check any loop in the alias list */ + const char *aname; + if (!strcmp(plugin, target)) { + SNDERR("Cannot alias to itself for plugin %s", plugin); + return -EINVAL; + } + aname = target; + while ((c = find_plugin_alias(aname, NULL)) != NULL) { + aname = c->target; + if (!strcmp(aname, plugin)) { + SNDERR("Loop is detected for alias %s", aname); + return -EINVAL; + } + } + } + + c = find_plugin_alias(plugin, &prev); + if (c) { + /* remove the existing one */ + if (prev) + prev->next = c->next; + else + plugin_alias_list = c->next; + free(c->name); + free(c->target); + free(c); + if (!target) + return 0; + } else { + if (!target) + return -ENOENT; + } + + /* create a new list member and add it */ + c = malloc(sizeof(*c)); + if (!c) + return -ENOMEM; + c->name = strdup(plugin); + c->target = strdup(target); + if (!c->name || !c->target) { + free(c->name); + free(c->target); + free(c); + return -ENOMEM; + } + c->next = plugin_alias_list; + plugin_alias_list = c; + return 0; +}
/** * \brief Wait for a PCM to become ready diff -r 3539f279ec38 src/pcm/pcm_empty.c --- a/src/pcm/pcm_empty.c Wed Nov 21 12:19:43 2007 +0100 +++ b/src/pcm/pcm_empty.c Wed Nov 21 16:03:51 2007 +0100 @@ -90,8 +90,7 @@ int _snd_pcm_empty_open(snd_pcm_t **pcmp slave = n; continue; } - SNDERR("Unknown field %s", id); - return -EINVAL; + continue; } if (!slave) { SNDERR("slave is not defined");