Re: [PATCH - extplug refinement from hw_params 1/1] pcm: extplug: Update slave PCM if extplug slave params were changed

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 12/7/18 12:03, Takashi Iwai wrote:
On Thu, 06 Dec 2018 15:31:43 +0100,
<twischer@xxxxxxxxxxxxxx> wrote:
From: Timo Wischer <twischer@xxxxxxxxxxxxxx>

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
Which condition is supposed for this constraint (1-8 channels)?  If
this is about the input to the extplug itself, it should be set via
snd_pcm_extplug_set_param_minmax() instead.

The snd_pcm_extplug_set_slave_param_minmax() is used to restrict the
output from extplug (i.e. the condition to be passed to the slave).
This is needed only when the slave PCM can't have the proper input
constraints.  So usually this sets to a single condition or alike.


When using snd_pcm_extplug_set_param_minmax() it will ignore the capabilities of the slave device. For example the salve device supports only 2 channels but the extplug supports 1-8 channels and uses snd_pcm_extplug_set_param_minmax(CHANNELS, 1, 8). So the user application can open the device with 8 channels and the extplug has to down mix to 2 channels. But I do not want to support down mixing in my extplug. I only want to limit the capabilities of the slave device with the limitations of my extplug and forward this to the user application.

PS: currently I am investigating in an issue of this patch in case of MMAP access where snd_pcm_hw_params() fails with EBUSY because snd_pcm_mmap() is called twice. Therefore please do not merge this version of the patch.

But anyway I am interested whether you are fine with this approach, now or I have to find another approach.

Best regards

Timo



Takashi

- 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

_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel



[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Pulse Audio]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux