From: Timo Wischer twischer@de.adit-jv.com
from extplug hw_params() callback function.
This feature is for example required in the following use case: - A filter plugin supports only 1-8 channels. Therefore it calls snd_pcm_extplug_set_slave_param_minmax(SND_PCM_EXTPLUG_HW_CHANNELS, 1, 8) to provide the salve PCM parameters limited to 1-8 channels to the user application - The user application requests 2 channels. But in this case the slave PCM will be configured with 1 channel because it is the first valid configuration - To update the salve PCM snd_pcm_extplug_set_slave_param_minmax(SND_PCM_EXTPLUG_HW_CHANNELS, 2, 2) could be called from the extplug hw_params() callback function
Without this patch the call to snd_pcm_extplug_set_slave_param_minmax() would not have any effect. With this patch the slave device will also be configured with 2 channels and no down mixing has to be done by the extplug.
This new feature can also be used for some specific dependencies. For example a down mixing extplug supports only 2to1 and 4to2 channel down mixing. Therefore the salve PCM has to be opened with 2 channels if the user application requests 4 channels.
Signed-off-by: Timo Wischer twischer@de.adit-jv.com
diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c index 1f887c5..44afadb 100644 --- a/src/pcm/pcm_extplug.c +++ b/src/pcm/pcm_extplug.c @@ -26,6 +26,7 @@ * */
+#include <stdbool.h> #include "pcm_local.h" #include "pcm_plugin.h" #include "pcm_extplug.h" @@ -43,6 +44,7 @@ typedef struct snd_pcm_extplug_priv { snd_pcm_extplug_t *data; struct snd_ext_parm params[SND_PCM_EXTPLUG_HW_PARAMS]; struct snd_ext_parm sparams[SND_PCM_EXTPLUG_HW_PARAMS]; + bool sparams_changed; } extplug_priv_t;
static const int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = { @@ -60,18 +62,49 @@ static const unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = { SND_PCM_HW_PARBIT_FRAME_BITS), };
+static bool snd_ext_parm_equal(const struct snd_ext_parm * const a, + const struct snd_ext_parm * const b) +{ + if (a != b || a->active != b->active || a->num_list != b->num_list) + return false; + + if (a->num_list > 0) { + for (unsigned int i = 0; i++; i < a->num_list) { + if (a->list[i] != b->list[i]) + return false; + } + } else { + if (a->min != b->min || a->max != b->max) + return false; + } + + return true; +} + +static int snd_ext_parm_set(struct snd_ext_parm *parm, unsigned int min, + unsigned int max, unsigned int num_list, + unsigned int *list) +{ + const struct snd_ext_parm new_parm = { + .min = min, + .max = max, + .num_list = num_list, + .list = list, + .active = 1, + }; + const int changed = snd_ext_parm_equal(parm, &new_parm) ? 0 : 1; + + free(parm->list); + *parm = new_parm; + return changed; +} + /* * set min/max values for the given parameter */ int snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max) { - parm->num_list = 0; - free(parm->list); - parm->list = NULL; - parm->min = min; - parm->max = max; - parm->active = 1; - return 0; + return snd_ext_parm_set(parm, min, max, 0, NULL); }
/* @@ -92,11 +125,7 @@ int snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, cons memcpy(new_list, list, sizeof(*new_list) * num_list); qsort(new_list, num_list, sizeof(*new_list), val_compar);
- free(parm->list); - parm->num_list = num_list; - parm->list = new_list; - parm->active = 1; - return 0; + return snd_ext_parm_set(parm, 0, 0, num_list, new_list); }
void snd_ext_parm_clear(struct snd_ext_parm *parm) @@ -291,29 +320,37 @@ static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params */ static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { - extplug_priv_t *ext = pcm->private_data; snd_pcm_t *slave = ext->plug.gen.slave; - int err = snd_pcm_hw_params_slave(pcm, params, - snd_pcm_extplug_hw_refine_cchange, - snd_pcm_extplug_hw_refine_sprepare, - snd_pcm_extplug_hw_refine_schange, - snd_pcm_generic_hw_params); - if (err < 0) - return err; - ext->data->slave_format = slave->format; - ext->data->slave_subformat = slave->subformat; - ext->data->slave_channels = slave->channels; - ext->data->rate = slave->rate; - INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format); - INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat); - INTERNAL(snd_pcm_hw_params_get_channels)(params, &ext->data->channels); - - if (ext->data->callback->hw_params) { - err = ext->data->callback->hw_params(ext->data, params); + + do { + ext->sparams_changed = false; + + int err = snd_pcm_hw_params_slave(pcm, params, + snd_pcm_extplug_hw_refine_cchange, + snd_pcm_extplug_hw_refine_sprepare, + snd_pcm_extplug_hw_refine_schange, + snd_pcm_generic_hw_params); if (err < 0) return err; - } + ext->data->slave_format = slave->format; + ext->data->slave_subformat = slave->subformat; + ext->data->slave_channels = slave->channels; + ext->data->rate = slave->rate; + INTERNAL(snd_pcm_hw_params_get_format)( + params, &ext->data->format); + INTERNAL(snd_pcm_hw_params_get_subformat)( + params, &ext->data->subformat); + INTERNAL(snd_pcm_hw_params_get_channels)( + params, &ext->data->channels); + + if (ext->data->callback->hw_params) { + err = ext->data->callback->hw_params(ext->data, params); + if (err < 0) + return err; + } + } while (ext->sparams_changed); + return 0; }
@@ -411,6 +448,7 @@ static void clear_ext_params(extplug_priv_t *ext) int i; for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) { snd_ext_parm_clear(&ext->params[i]); + ext->sparams_changed |= ext->sparams[i].active; snd_ext_parm_clear(&ext->sparams[i]); } } @@ -637,7 +675,9 @@ parameters are sample format and channels. To define the constraints of the slave PCM configuration, use either #snd_pcm_extplug_set_slave_param_minmax() and #snd_pcm_extplug_set_slave_param_list(). The arguments are as same -as former functions. +as former functions. Both functions can also be called from the hw_params +callback. This can be used to further limit the configuration of the slave +device depending on the configuration of the user device.
To clear the parameter constraints, call #snd_pcm_extplug_params_reset() function. @@ -768,11 +808,17 @@ void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug) int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list) { extplug_priv_t *ext = extplug->pcm->private_data; + int changed = 0; + if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { SNDERR("EXTPLUG: invalid parameter type %d", type); return -EINVAL; } - return snd_ext_parm_set_list(&ext->sparams[type], num_list, list); + changed = snd_ext_parm_set_list(&ext->sparams[type], num_list, list); + if (changed > 0) + ext->sparams_changed = true; + + return changed; }
/** @@ -790,6 +836,8 @@ int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, u int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max) { extplug_priv_t *ext = extplug->pcm->private_data; + int changed = 0; + if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { SNDERR("EXTPLUG: invalid parameter type %d", type); return -EINVAL; @@ -798,7 +846,11 @@ int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, SNDERR("EXTPLUG: invalid parameter type %d", type); return -EINVAL; } - return snd_ext_parm_set_minmax(&ext->sparams[type], min, max); + changed = snd_ext_parm_set_minmax(&ext->sparams[type], min, max); + if (changed > 0) + ext->sparams_changed = true; + + return changed; }
/**