- 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@xxxxxxxxxxxxxx>
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;
}
/**
--
2.7.4