Blame src/pcm/pcm_plug.c

Packit 4a16fb
/*
Packit 4a16fb
 * \file pcm/pcm_plug.c
Packit 4a16fb
 * \ingroup PCM_Plugins
Packit 4a16fb
 * \brief PCM Route & Volume Plugin Interface
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \date 2000-2001
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Plug
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
  
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "pcm_plugin.h"
Packit 4a16fb
Packit 4a16fb
#ifndef PIC
Packit 4a16fb
/* entry for static linking */
Packit 4a16fb
const char *_snd_module_pcm_plug = "";
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
enum snd_pcm_plug_route_policy {
Packit 4a16fb
	PLUG_ROUTE_POLICY_NONE,
Packit 4a16fb
	PLUG_ROUTE_POLICY_DEFAULT,
Packit 4a16fb
	PLUG_ROUTE_POLICY_COPY,
Packit 4a16fb
	PLUG_ROUTE_POLICY_AVERAGE,
Packit 4a16fb
	PLUG_ROUTE_POLICY_DUP,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	snd_pcm_generic_t gen;
Packit 4a16fb
	snd_pcm_t *req_slave;
Packit 4a16fb
	snd_pcm_format_t sformat;
Packit 4a16fb
	int schannels;
Packit 4a16fb
	int srate;
Packit 4a16fb
	snd_config_t *rate_converter;
Packit 4a16fb
	enum snd_pcm_plug_route_policy route_policy;
Packit 4a16fb
	snd_pcm_route_ttable_entry_t *ttable;
Packit 4a16fb
	int ttable_ok;
Packit 4a16fb
	unsigned int tt_ssize, tt_cused, tt_sused;
Packit 4a16fb
} snd_pcm_plug_t;
Packit 4a16fb
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_close(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err, result = 0;
Packit 4a16fb
	free(plug->ttable);
Packit 4a16fb
	if (plug->rate_converter) {
Packit 4a16fb
		snd_config_delete(plug->rate_converter);
Packit 4a16fb
		plug->rate_converter = NULL;
Packit 4a16fb
	}
Packit 4a16fb
	assert(plug->gen.slave == plug->req_slave);
Packit 4a16fb
	if (plug->gen.close_slave) {
Packit 4a16fb
		snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
Packit 4a16fb
		snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
Packit 4a16fb
		err = snd_pcm_close(plug->req_slave);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			result = err;
Packit 4a16fb
	}
Packit 4a16fb
	free(plug);
Packit 4a16fb
	return result;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plug->req_slave;
Packit 4a16fb
	int err;
Packit 4a16fb
	
Packit 4a16fb
	if ((err = snd_pcm_info(slave, info)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_format_t linear_preferred_formats[] = {
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S16_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U16_LE,
Packit 4a16fb
	SND_PCM_FORMAT_S16_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U16_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S16_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U16_BE,
Packit 4a16fb
	SND_PCM_FORMAT_S16_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U16_LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S32_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U32_LE,
Packit 4a16fb
	SND_PCM_FORMAT_S32_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U32_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S32_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U32_BE,
Packit 4a16fb
	SND_PCM_FORMAT_S32_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U32_LE,
Packit 4a16fb
#endif
Packit 4a16fb
	SND_PCM_FORMAT_S8,
Packit 4a16fb
	SND_PCM_FORMAT_U8,
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S24_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_LE,
Packit 4a16fb
	SND_PCM_FORMAT_S24_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S24_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_BE,
Packit 4a16fb
	SND_PCM_FORMAT_S24_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S20_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_LE,
Packit 4a16fb
	SND_PCM_FORMAT_S20_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S20_BE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_BE,
Packit 4a16fb
	SND_PCM_FORMAT_S20_LE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S24_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_S24_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_3BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S24_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_S24_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U24_3LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S20_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_S20_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_3BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S20_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_S20_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U20_3LE,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_S18_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U18_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_S18_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U18_3BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_S18_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_U18_3BE,
Packit 4a16fb
	SND_PCM_FORMAT_S18_3LE,
Packit 4a16fb
	SND_PCM_FORMAT_U18_3LE,
Packit 4a16fb
#endif
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
#if defined(BUILD_PCM_PLUGIN_MULAW) || \
Packit 4a16fb
	defined(BUILD_PCM_PLUGIN_ALAW) || \
Packit 4a16fb
	defined(BUILD_PCM_PLUGIN_ADPCM)
Packit 4a16fb
#define BUILD_PCM_NONLINEAR
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifdef BUILD_PCM_NONLINEAR
Packit 4a16fb
static const snd_pcm_format_t nonlinear_preferred_formats[] = {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit 4a16fb
	SND_PCM_FORMAT_MU_LAW,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit 4a16fb
	SND_PCM_FORMAT_A_LAW,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit 4a16fb
	SND_PCM_FORMAT_IMA_ADPCM,
Packit 4a16fb
#endif
Packit 4a16fb
};
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
static const snd_pcm_format_t float_preferred_formats[] = {
Packit 4a16fb
#ifdef SND_LITTLE_ENDIAN
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_BE,
Packit 4a16fb
#else
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_BE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT_LE,
Packit 4a16fb
	SND_PCM_FORMAT_FLOAT64_LE,
Packit 4a16fb
#endif
Packit 4a16fb
};
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
static const char linear_format_widths[32] = {
Packit 4a16fb
	0, 0, 0, 0, 0, 0, 0, 1,
Packit 4a16fb
	0, 0, 0, 0, 0, 0, 0, 1,
Packit 4a16fb
	0, 1, 0, 1, 0, 0, 0, 1,
Packit 4a16fb
	0, 0, 0, 0, 0, 0, 0, 1,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
Packit 4a16fb
{
Packit 4a16fb
	int e, s;
Packit 4a16fb
	if (! linear_format_widths[wid - 1])
Packit 4a16fb
		return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
	for (e = 0; e < 2; e++) {
Packit 4a16fb
		for (s = 0; s < 2; s++) {
Packit 4a16fb
			int pw = ((wid + 7) / 8) * 8;
Packit 4a16fb
			for (; pw <= 32; pw += 8) {
Packit 4a16fb
				snd_pcm_format_t f;
Packit 4a16fb
				f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
Packit 4a16fb
				if (f != SND_PCM_FORMAT_UNKNOWN &&
Packit 4a16fb
				    snd_pcm_format_mask_test(format_mask, f))
Packit 4a16fb
					return f;
Packit 4a16fb
			}
Packit 4a16fb
			sgn = !sgn;
Packit 4a16fb
		}
Packit 4a16fb
		ed = !ed;
Packit 4a16fb
	}
Packit 4a16fb
	return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
Packit 4a16fb
{
Packit 4a16fb
	int w, w1, u, e;
Packit 4a16fb
	snd_pcm_format_t f;
Packit 4a16fb
	snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
Packit 4a16fb
	snd_pcm_format_mask_t fl = {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
		SND_PCM_FMTBIT_FLOAT
Packit 4a16fb
#else
Packit 4a16fb
		{ 0 }
Packit 4a16fb
#endif
Packit 4a16fb
	};
Packit 4a16fb
	if (snd_pcm_format_mask_test(format_mask, format))
Packit 4a16fb
		return format;
Packit 4a16fb
	if (!snd_pcm_format_mask_test(&lin, format) &&
Packit 4a16fb
	    !snd_pcm_format_mask_test(&fl, format)) {
Packit 4a16fb
		unsigned int i;
Packit 4a16fb
		switch (format) {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit 4a16fb
		case SND_PCM_FORMAT_MU_LAW:
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit 4a16fb
		case SND_PCM_FORMAT_A_LAW:
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit 4a16fb
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit 4a16fb
#endif
Packit 4a16fb
			for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
Packit 4a16fb
				snd_pcm_format_t f = linear_preferred_formats[i];
Packit 4a16fb
				if (snd_pcm_format_mask_test(format_mask, f))
Packit 4a16fb
					return f;
Packit 4a16fb
			}
Packit 4a16fb
			/* Fall through */
Packit 4a16fb
		default:
Packit 4a16fb
			return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
		}
Packit 4a16fb
Packit 4a16fb
	}
Packit 4a16fb
	snd_mask_intersect(&lin, format_mask);
Packit 4a16fb
	snd_mask_intersect(&fl, format_mask);
Packit 4a16fb
	if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
Packit 4a16fb
#ifdef BUILD_PCM_NONLINEAR
Packit 4a16fb
		unsigned int i;
Packit 4a16fb
		for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
Packit 4a16fb
			snd_pcm_format_t f = nonlinear_preferred_formats[i];
Packit 4a16fb
			if (snd_pcm_format_mask_test(format_mask, f))
Packit 4a16fb
				return f;
Packit 4a16fb
		}
Packit 4a16fb
#endif
Packit 4a16fb
		return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
	}
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
	if (snd_pcm_format_float(format)) {
Packit 4a16fb
		if (snd_pcm_format_mask_test(&fl, format)) {
Packit 4a16fb
			unsigned int i;
Packit 4a16fb
			for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
Packit 4a16fb
				snd_pcm_format_t f = float_preferred_formats[i];
Packit 4a16fb
				if (snd_pcm_format_mask_test(format_mask, f))
Packit 4a16fb
					return f;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		w = 32;
Packit 4a16fb
		u = 0;
Packit 4a16fb
		e = snd_pcm_format_big_endian(format);
Packit 4a16fb
	} else
Packit 4a16fb
#endif
Packit 4a16fb
	if (snd_mask_empty(&lin)) {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
		unsigned int i;
Packit 4a16fb
		for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
Packit 4a16fb
			snd_pcm_format_t f = float_preferred_formats[i];
Packit 4a16fb
			if (snd_pcm_format_mask_test(format_mask, f))
Packit 4a16fb
				return f;
Packit 4a16fb
		}
Packit 4a16fb
#endif
Packit 4a16fb
		return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
	} else {
Packit 4a16fb
		w = snd_pcm_format_width(format);
Packit 4a16fb
		u = snd_pcm_format_unsigned(format);
Packit 4a16fb
		e = snd_pcm_format_big_endian(format);
Packit 4a16fb
	}
Packit 4a16fb
	for (w1 = w; w1 <= 32; w1++) {
Packit 4a16fb
		f = check_linear_format(format_mask, w1, u, e);
Packit 4a16fb
		if (f != SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
			return f;
Packit 4a16fb
	}
Packit 4a16fb
	for (w1 = w - 1; w1 > 0; w1--) {
Packit 4a16fb
		f = check_linear_format(format_mask, w1, u, e);
Packit 4a16fb
		if (f != SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
			return f;
Packit 4a16fb
	}
Packit 4a16fb
	return SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_plug_clear(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plug->req_slave;
Packit 4a16fb
	/* Clear old plugins */
Packit 4a16fb
	if (plug->gen.slave != slave) {
Packit 4a16fb
		snd_pcm_unlink_hw_ptr(pcm, plug->gen.slave);
Packit 4a16fb
		snd_pcm_unlink_appl_ptr(pcm, plug->gen.slave);
Packit 4a16fb
		snd_pcm_close(plug->gen.slave);
Packit 4a16fb
		plug->gen.slave = slave;
Packit 4a16fb
		pcm->fast_ops = slave->fast_ops;
Packit 4a16fb
		pcm->fast_op_arg = slave->fast_op_arg;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
typedef struct {
Packit 4a16fb
	snd_pcm_access_t access;
Packit 4a16fb
	snd_pcm_format_t format;
Packit 4a16fb
	unsigned int channels;
Packit 4a16fb
	unsigned int rate;
Packit 4a16fb
} snd_pcm_plug_params_t;
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_RATE
Packit 4a16fb
static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (clt->rate == slv->rate)
Packit 4a16fb
		return 0;
Packit 4a16fb
	assert(snd_pcm_format_linear(slv->format));
Packit 4a16fb
	err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
Packit 4a16fb
				plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	slv->access = clt->access;
Packit 4a16fb
	slv->rate = clt->rate;
Packit 4a16fb
	if (snd_pcm_format_linear(clt->format))
Packit 4a16fb
		slv->format = clt->format;
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit 4a16fb
static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	unsigned int tt_ssize, tt_cused, tt_sused;
Packit 4a16fb
	snd_pcm_route_ttable_entry_t *ttable;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (clt->channels == slv->channels &&
Packit 4a16fb
	    (!plug->ttable || plug->ttable_ok))
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (clt->rate != slv->rate &&
Packit 4a16fb
	    clt->channels > slv->channels)
Packit 4a16fb
		return 0;
Packit 4a16fb
	assert(snd_pcm_format_linear(slv->format));
Packit 4a16fb
	tt_ssize = slv->channels;
Packit 4a16fb
	tt_cused = clt->channels;
Packit 4a16fb
	tt_sused = slv->channels;
Packit 4a16fb
	ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
Packit 4a16fb
	if (plug->ttable) {	/* expand or shrink table */
Packit 4a16fb
		unsigned int c = 0, s = 0;
Packit 4a16fb
		for (c = 0; c < tt_cused; c++) {
Packit 4a16fb
			for (s = 0; s < tt_sused; s++) {
Packit 4a16fb
				snd_pcm_route_ttable_entry_t v;
Packit 4a16fb
				if (c >= plug->tt_cused)
Packit 4a16fb
					v = 0;
Packit 4a16fb
				else if (s >= plug->tt_sused)
Packit 4a16fb
					v = 0;
Packit 4a16fb
				else
Packit 4a16fb
					v = plug->ttable[c * plug->tt_ssize + s];
Packit 4a16fb
				ttable[c * tt_ssize + s] = v;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		plug->ttable_ok = 1;
Packit 4a16fb
	} else {
Packit 4a16fb
		unsigned int k;
Packit 4a16fb
		unsigned int c = 0, s = 0;
Packit 4a16fb
		enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
Packit 4a16fb
		int n;
Packit 4a16fb
		for (k = 0; k < tt_cused * tt_sused; ++k)
Packit 4a16fb
			ttable[k] = 0;
Packit 4a16fb
		if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
Packit 4a16fb
			rpolicy = PLUG_ROUTE_POLICY_COPY;
Packit 4a16fb
			/* it's hack for mono conversion */
Packit 4a16fb
			if (clt->channels == 1 || slv->channels == 1)
Packit 4a16fb
				rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
Packit 4a16fb
		}
Packit 4a16fb
		switch (rpolicy) {
Packit 4a16fb
		case PLUG_ROUTE_POLICY_AVERAGE:
Packit 4a16fb
		case PLUG_ROUTE_POLICY_DUP:
Packit 4a16fb
			if (clt->channels > slv->channels) {
Packit 4a16fb
				n = clt->channels;
Packit 4a16fb
			} else {
Packit 4a16fb
				n = slv->channels;
Packit 4a16fb
			}
Packit 4a16fb
			while (n-- > 0) {
Packit 4a16fb
				snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
Packit 4a16fb
				if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
Packit 4a16fb
					if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
Packit 4a16fb
					    clt->channels > slv->channels) {
Packit 4a16fb
						int srcs = clt->channels / slv->channels;
Packit 4a16fb
						if (s < clt->channels % slv->channels)
Packit 4a16fb
							srcs++;
Packit 4a16fb
						v /= srcs;
Packit 4a16fb
					} else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
Packit 4a16fb
						   slv->channels > clt->channels) {
Packit 4a16fb
							int srcs = slv->channels / clt->channels;
Packit 4a16fb
						if (s < slv->channels % clt->channels)
Packit 4a16fb
							srcs++;
Packit 4a16fb
						v /= srcs;
Packit 4a16fb
					}
Packit 4a16fb
				}
Packit 4a16fb
				ttable[c * tt_ssize + s] = v;
Packit 4a16fb
				if (++c == clt->channels)
Packit 4a16fb
					c = 0;
Packit 4a16fb
				if (++s == slv->channels)
Packit 4a16fb
					s = 0;
Packit 4a16fb
			}
Packit 4a16fb
			break;
Packit 4a16fb
		case PLUG_ROUTE_POLICY_COPY:
Packit 4a16fb
			if (clt->channels < slv->channels) {
Packit 4a16fb
				n = clt->channels;
Packit 4a16fb
			} else {
Packit 4a16fb
				n = slv->channels;
Packit 4a16fb
			}
Packit 4a16fb
			for (c = 0; (int)c < n; c++)
Packit 4a16fb
				ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
Packit 4a16fb
			break;
Packit 4a16fb
		default:
Packit 4a16fb
			SNDERR("Invalid route policy");
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	slv->channels = clt->channels;
Packit 4a16fb
	slv->access = clt->access;
Packit 4a16fb
	if (snd_pcm_format_linear(clt->format))
Packit 4a16fb
		slv->format = clt->format;
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_format_t cfmt;
Packit 4a16fb
	int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
Packit 4a16fb
Packit 4a16fb
	/* No conversion is needed */
Packit 4a16fb
	if (clt->format == slv->format &&
Packit 4a16fb
	    clt->rate == slv->rate &&
Packit 4a16fb
	    clt->channels == slv->channels &&
Packit 4a16fb
	    (!plug->ttable || plug->ttable_ok))
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	if (snd_pcm_format_linear(slv->format)) {
Packit 4a16fb
		/* Conversion is done in another plugin */
Packit 4a16fb
		if (clt->rate != slv->rate ||
Packit 4a16fb
		    clt->channels != slv->channels ||
Packit 4a16fb
		    (plug->ttable && !plug->ttable_ok))
Packit 4a16fb
			return 0;
Packit 4a16fb
		cfmt = clt->format;
Packit 4a16fb
		switch (clt->format) {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit 4a16fb
		case SND_PCM_FORMAT_MU_LAW:
Packit 4a16fb
			f = snd_pcm_mulaw_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit 4a16fb
		case SND_PCM_FORMAT_A_LAW:
Packit 4a16fb
			f = snd_pcm_alaw_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit 4a16fb
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit 4a16fb
			f = snd_pcm_adpcm_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
		default:
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
			if (snd_pcm_format_float(clt->format))
Packit 4a16fb
				f = snd_pcm_lfloat_open;
Packit 4a16fb
Packit 4a16fb
			else
Packit 4a16fb
#endif
Packit 4a16fb
				f = snd_pcm_linear_open;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit 4a16fb
	} else if (snd_pcm_format_float(slv->format)) {
Packit 4a16fb
		if (snd_pcm_format_linear(clt->format)) {
Packit 4a16fb
			cfmt = clt->format;
Packit 4a16fb
			f = snd_pcm_lfloat_open;
Packit 4a16fb
		} else if (clt->rate != slv->rate || clt->channels != slv->channels ||
Packit 4a16fb
			   (plug->ttable && !plug->ttable_ok)) {
Packit 4a16fb
			cfmt = SND_PCM_FORMAT_S16;
Packit 4a16fb
			f = snd_pcm_lfloat_open;
Packit 4a16fb
		} else
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_NONLINEAR
Packit 4a16fb
	} else {
Packit 4a16fb
		switch (slv->format) {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit 4a16fb
		case SND_PCM_FORMAT_MU_LAW:
Packit 4a16fb
			f = snd_pcm_mulaw_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit 4a16fb
		case SND_PCM_FORMAT_A_LAW:
Packit 4a16fb
			f = snd_pcm_alaw_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit 4a16fb
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit 4a16fb
			f = snd_pcm_adpcm_open;
Packit 4a16fb
			break;
Packit 4a16fb
#endif
Packit 4a16fb
		default:
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		if (snd_pcm_format_linear(clt->format))
Packit 4a16fb
			cfmt = clt->format;
Packit 4a16fb
		else
Packit 4a16fb
			cfmt = SND_PCM_FORMAT_S16;
Packit 4a16fb
#endif /* NONLINEAR */
Packit 4a16fb
	}
Packit 4a16fb
	err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	slv->format = cfmt;
Packit 4a16fb
	slv->access = clt->access;
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (clt->access == slv->access)
Packit 4a16fb
		return 0;
Packit 4a16fb
	err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	slv->access = clt->access;
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit 4a16fb
static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
Packit 4a16fb
				    snd_pcm_plug_params_t *clt,
Packit 4a16fb
				    snd_pcm_plug_params_t *slv)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if (clt->access == slv->access)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	switch (slv->access) {
Packit 4a16fb
	case SND_PCM_ACCESS_MMAP_INTERLEAVED:
Packit 4a16fb
	case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
Packit 4a16fb
	case SND_PCM_ACCESS_MMAP_COMPLEX:
Packit 4a16fb
		return 0;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
Packit 4a16fb
				       plug->gen.slave != plug->req_slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	switch (slv->access) {
Packit 4a16fb
	case SND_PCM_ACCESS_RW_INTERLEAVED:
Packit 4a16fb
		slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_ACCESS_RW_NONINTERLEAVED:
Packit 4a16fb
		slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
Packit 4a16fb
				       snd_pcm_plug_params_t *client,
Packit 4a16fb
				       snd_pcm_plug_params_t *slave)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit 4a16fb
		snd_pcm_plug_change_mmap,
Packit 4a16fb
#endif
Packit 4a16fb
		snd_pcm_plug_change_format,
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit 4a16fb
		snd_pcm_plug_change_channels,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_RATE
Packit 4a16fb
		snd_pcm_plug_change_rate,
Packit 4a16fb
#endif
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit 4a16fb
		snd_pcm_plug_change_channels,
Packit 4a16fb
#endif
Packit 4a16fb
		snd_pcm_plug_change_format,
Packit 4a16fb
		snd_pcm_plug_change_access
Packit 4a16fb
	};
Packit 4a16fb
	snd_pcm_plug_params_t p = *slave;
Packit 4a16fb
	unsigned int k = 0;
Packit 4a16fb
	plug->ttable_ok = 0;
Packit 4a16fb
	while (client->format != p.format ||
Packit 4a16fb
	       client->channels != p.channels ||
Packit 4a16fb
	       client->rate != p.rate ||
Packit 4a16fb
	       client->access != p.access ||
Packit 4a16fb
	       (plug->ttable && !plug->ttable_ok)) {
Packit 4a16fb
		snd_pcm_t *new;
Packit 4a16fb
		int err;
Packit 4a16fb
		if (k >= sizeof(funcs)/sizeof(*funcs)) {
Packit 4a16fb
			snd_pcm_plug_clear(pcm);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		err = funcs[k](pcm, &new, client, &p);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			snd_pcm_plug_clear(pcm);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		if (err) {
Packit 4a16fb
			plug->gen.slave = new;
Packit 4a16fb
		}
Packit 4a16fb
		k++;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int rate_min, channels_max;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	/* HACK: to avoid overflow in PARTBIT_RATE code */
Packit 4a16fb
	err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (rate_min < 4000) {
Packit 4a16fb
		_snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
Packit 4a16fb
		if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	/* HACK: to avoid overflow in PERIOD_SIZE code */
Packit 4a16fb
	err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (channels_max > 10000) {
Packit 4a16fb
		_snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
Packit 4a16fb
		if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	
Packit 4a16fb
	_snd_pcm_hw_params_any(sparams);
Packit 4a16fb
	if (plug->sformat >= 0) {
Packit 4a16fb
		_snd_pcm_hw_params_set_format(sparams, plug->sformat);
Packit 4a16fb
		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	}
Packit 4a16fb
	if (plug->schannels > 0)
Packit 4a16fb
		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
Packit 4a16fb
				      plug->schannels, 0);
Packit 4a16fb
	if (plug->srate > 0)
Packit 4a16fb
		_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
Packit 4a16fb
					      plug->srate, 0, plug->srate + 1, -1);
Packit 4a16fb
	/* reduce the available configurations */
Packit 4a16fb
	err = snd_pcm_hw_refine(plug->req_slave, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int check_access_change(snd_pcm_hw_params_t *cparams,
Packit 4a16fb
			       snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_access_mask_t *smask;
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit 4a16fb
	const snd_pcm_access_mask_t *cmask;
Packit 4a16fb
	snd_pcm_access_mask_t mask;
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
	smask = (snd_pcm_access_mask_t *)
Packit 4a16fb
		snd_pcm_hw_param_get_mask(sparams,
Packit 4a16fb
					  SND_PCM_HW_PARAM_ACCESS);
Packit 4a16fb
	if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
Packit 4a16fb
		return 0; /* OK, we have mmap support */
Packit 4a16fb
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit 4a16fb
	/* no mmap support - we need mmap emulation */
Packit 4a16fb
Packit 4a16fb
	if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
Packit 4a16fb
	    !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) 
Packit 4a16fb
		return -EINVAL; /* even no RW access?  no way! */
Packit 4a16fb
Packit 4a16fb
	cmask = (const snd_pcm_access_mask_t *)
Packit 4a16fb
		snd_pcm_hw_param_get_mask(cparams,
Packit 4a16fb
					  SND_PCM_HW_PARAM_ACCESS);
Packit 4a16fb
	snd_mask_none(&mask);
Packit 4a16fb
	if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
Packit 4a16fb
		if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
Packit 4a16fb
			snd_pcm_access_mask_set(&mask,
Packit 4a16fb
						SND_PCM_ACCESS_RW_INTERLEAVED);
Packit 4a16fb
	}
Packit 4a16fb
	if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
Packit 4a16fb
		if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
Packit 4a16fb
			snd_pcm_access_mask_set(&mask,
Packit 4a16fb
						SND_PCM_ACCESS_RW_NONINTERLEAVED);
Packit 4a16fb
	}
Packit 4a16fb
	if (!snd_mask_empty(&mask))
Packit 4a16fb
		*smask = mask; /* prefer the straight conversion */
Packit 4a16fb
	return 0;
Packit 4a16fb
#else
Packit 4a16fb
	return -EINVAL;
Packit 4a16fb
#endif
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
Packit 4a16fb
					  snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plug->req_slave;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	const snd_pcm_format_mask_t *format_mask, *sformat_mask;
Packit 4a16fb
	snd_pcm_format_mask_t sfmt_mask;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_format_t format;
Packit 4a16fb
	snd_interval_t t, buffer_size;
Packit 4a16fb
	const snd_interval_t *srate, *crate;
Packit 4a16fb
Packit 4a16fb
	if (plug->srate == -2 ||
Packit 4a16fb
	    (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
Packit 4a16fb
	    (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_RATE;
Packit 4a16fb
	else {
Packit 4a16fb
		err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit 4a16fb
	else {
Packit 4a16fb
		err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_FORMAT;
Packit 4a16fb
	else {
Packit 4a16fb
		format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
Packit 4a16fb
		sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
Packit 4a16fb
		snd_mask_none(&sfmt_mask);
Packit 4a16fb
		for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
			snd_pcm_format_t f;
Packit 4a16fb
			if (!snd_pcm_format_mask_test(format_mask, format))
Packit 4a16fb
				continue;
Packit 4a16fb
			if (snd_pcm_format_mask_test(sformat_mask, format))
Packit 4a16fb
				f = format;
Packit 4a16fb
			else {
Packit 4a16fb
				f = snd_pcm_plug_slave_format(format, sformat_mask);
Packit 4a16fb
				if (f == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
					continue;
Packit 4a16fb
			}
Packit 4a16fb
			snd_pcm_format_mask_set(&sfmt_mask, f);
Packit 4a16fb
		}
Packit 4a16fb
Packit 4a16fb
		if (snd_pcm_format_mask_empty(&sfmt_mask)) {
Packit 4a16fb
			SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
Packit 4a16fb
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
				if (!snd_pcm_format_mask_test(format_mask, format))
Packit 4a16fb
					continue;
Packit 4a16fb
				SNDERR("Format: %s", snd_pcm_format_name(format));
Packit 4a16fb
			}
Packit 4a16fb
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
				if (!snd_pcm_format_mask_test(sformat_mask, format))
Packit 4a16fb
					continue;
Packit 4a16fb
				SNDERR("Slave format: %s", snd_pcm_format_name(format));
Packit 4a16fb
			}
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
Packit 4a16fb
						SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
Packit 4a16fb
		err = check_access_change(params, sparams);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			SNDERR("Unable to find an usable access for '%s'",
Packit 4a16fb
			       pcm->name);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if ((links & SND_PCM_HW_PARBIT_RATE) ||
Packit 4a16fb
	    snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_BUFFER_SIZE);
Packit 4a16fb
	else {
Packit 4a16fb
		snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
Packit 4a16fb
		snd_interval_unfloor(&buffer_size);
Packit 4a16fb
		crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
Packit 4a16fb
		srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
Packit 4a16fb
		snd_interval_muldiv(&buffer_size, srate, crate, &t);
Packit 4a16fb
		err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
	
Packit 4a16fb
static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit 4a16fb
					  snd_pcm_hw_params_t *params,
Packit 4a16fb
					  snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	const snd_pcm_format_mask_t *format_mask, *sformat_mask;
Packit 4a16fb
	snd_pcm_format_mask_t fmt_mask;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_format_t format;
Packit 4a16fb
	snd_interval_t t;
Packit 4a16fb
	const snd_interval_t *sbuffer_size;
Packit 4a16fb
	const snd_interval_t *srate, *crate;
Packit 4a16fb
Packit 4a16fb
	if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit 4a16fb
Packit 4a16fb
	if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_FORMAT;
Packit 4a16fb
	else {
Packit 4a16fb
		format_mask = snd_pcm_hw_param_get_mask(params,
Packit 4a16fb
							SND_PCM_HW_PARAM_FORMAT);
Packit 4a16fb
		sformat_mask = snd_pcm_hw_param_get_mask(sparams,
Packit 4a16fb
							 SND_PCM_HW_PARAM_FORMAT);
Packit 4a16fb
		snd_mask_none(&fmt_mask);
Packit 4a16fb
		for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
			snd_pcm_format_t f;
Packit 4a16fb
			if (!snd_pcm_format_mask_test(format_mask, format))
Packit 4a16fb
				continue;
Packit 4a16fb
			if (snd_pcm_format_mask_test(sformat_mask, format))
Packit 4a16fb
				f = format;
Packit 4a16fb
			else {
Packit 4a16fb
				f = snd_pcm_plug_slave_format(format, sformat_mask);
Packit 4a16fb
				if (f == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
					continue;
Packit 4a16fb
			}
Packit 4a16fb
			snd_pcm_format_mask_set(&fmt_mask, format);
Packit 4a16fb
		}
Packit 4a16fb
Packit 4a16fb
		if (snd_pcm_format_mask_empty(&fmt_mask)) {
Packit 4a16fb
			SNDERR("Unable to find an usable client format");
Packit 4a16fb
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
				if (!snd_pcm_format_mask_test(format_mask, format))
Packit 4a16fb
					continue;
Packit 4a16fb
				SNDERR("Format: %s", snd_pcm_format_name(format));
Packit 4a16fb
			}
Packit 4a16fb
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit 4a16fb
				if (!snd_pcm_format_mask_test(sformat_mask, format))
Packit 4a16fb
					continue;
Packit 4a16fb
				SNDERR("Slave format: %s", snd_pcm_format_name(format));
Packit 4a16fb
			}
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		
Packit 4a16fb
		err = _snd_pcm_hw_param_set_mask(params, 
Packit 4a16fb
						 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (plug->srate == -2 ||
Packit 4a16fb
	    (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
Packit 4a16fb
	    (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
Packit 4a16fb
		links |= SND_PCM_HW_PARBIT_RATE;
Packit 4a16fb
	else {
Packit 4a16fb
		unsigned int rate_min, srate_min;
Packit 4a16fb
		int rate_mindir, srate_mindir;
Packit 4a16fb
		
Packit 4a16fb
		/* This is a temporary hack, waiting for a better solution */
Packit 4a16fb
		err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		if (rate_min == srate_min && srate_mindir > rate_mindir) {
Packit 4a16fb
			err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	if ((links & SND_PCM_HW_PARBIT_RATE) ||
Packit 4a16fb
	    snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_BUFFER_SIZE);
Packit 4a16fb
	else {
Packit 4a16fb
		sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
Packit 4a16fb
		crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
Packit 4a16fb
		srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
Packit 4a16fb
		snd_interval_muldiv(sbuffer_size, crate, srate, &t);
Packit 4a16fb
		snd_interval_floor(&t);
Packit 4a16fb
		if (snd_interval_empty(&t))
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	/* FIXME */
Packit 4a16fb
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	return snd_pcm_hw_refine(plug->req_slave, params);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_hw_refine_slave(pcm, params,
Packit 4a16fb
				       snd_pcm_plug_hw_refine_cprepare,
Packit 4a16fb
				       snd_pcm_plug_hw_refine_cchange,
Packit 4a16fb
				       snd_pcm_plug_hw_refine_sprepare,
Packit 4a16fb
				       snd_pcm_plug_hw_refine_schange,
Packit 4a16fb
				       snd_pcm_plug_hw_refine_slave);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plug->req_slave;
Packit 4a16fb
	snd_pcm_plug_params_t clt_params, slv_params;
Packit 4a16fb
	snd_pcm_hw_params_t sparams;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = snd_pcm_hw_refine_soft(slave, &sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
Packit 4a16fb
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
Packit 4a16fb
	INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
Packit 4a16fb
	snd_pcm_plug_clear(pcm);
Packit 4a16fb
	if (!(clt_params.format == slv_params.format &&
Packit 4a16fb
	      clt_params.channels == slv_params.channels &&
Packit 4a16fb
	      clt_params.rate == slv_params.rate &&
Packit 4a16fb
	      !plug->ttable &&
Packit 4a16fb
	      snd_pcm_hw_params_test_access(slave, &sparams,
Packit 4a16fb
					    clt_params.access) >= 0)) {
Packit 4a16fb
		INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
Packit 4a16fb
		err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	slave = plug->gen.slave;
Packit 4a16fb
	err = _snd_pcm_hw_params_internal(slave, params);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		snd_pcm_plug_clear(pcm);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
Packit 4a16fb
	snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
Packit 4a16fb
Packit 4a16fb
	pcm->fast_ops = slave->fast_ops;
Packit 4a16fb
	pcm->fast_op_arg = slave->fast_op_arg;
Packit 4a16fb
	snd_pcm_link_hw_ptr(pcm, slave);
Packit 4a16fb
	snd_pcm_link_appl_ptr(pcm, slave);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plug->gen.slave;
Packit 4a16fb
	int err = snd_pcm_hw_free(slave);
Packit 4a16fb
	snd_pcm_plug_clear(pcm);
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plug_t *plug = pcm->private_data;
Packit 4a16fb
	snd_output_printf(out, "Plug PCM: ");
Packit 4a16fb
	snd_pcm_dump(plug->gen.slave, out);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_ops_t snd_pcm_plug_ops = {
Packit 4a16fb
	.close = snd_pcm_plug_close,
Packit 4a16fb
	.info = snd_pcm_plug_info,
Packit 4a16fb
	.hw_refine = snd_pcm_plug_hw_refine,
Packit 4a16fb
	.hw_params = snd_pcm_plug_hw_params,
Packit 4a16fb
	.hw_free = snd_pcm_plug_hw_free,
Packit 4a16fb
	.sw_params = snd_pcm_generic_sw_params,
Packit 4a16fb
	.channel_info = snd_pcm_generic_channel_info,
Packit 4a16fb
	.dump = snd_pcm_plug_dump,
Packit 4a16fb
	.nonblock = snd_pcm_generic_nonblock,
Packit 4a16fb
	.async = snd_pcm_generic_async,
Packit 4a16fb
	.mmap = snd_pcm_generic_mmap,
Packit 4a16fb
	.munmap = snd_pcm_generic_munmap,
Packit 4a16fb
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit 4a16fb
	.get_chmap = snd_pcm_generic_get_chmap,
Packit 4a16fb
	.set_chmap = snd_pcm_generic_set_chmap,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Creates a new Plug PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param sformat Slave (destination) format
Packit 4a16fb
 * \param slave Slave PCM handle
Packit 4a16fb
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 * \warning Using of this function might be dangerous in the sense
Packit 4a16fb
 *          of compatibility reasons. The prototype might be freely
Packit 4a16fb
 *          changed in future.
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_plug_open(snd_pcm_t **pcmp,
Packit 4a16fb
		      const char *name,
Packit 4a16fb
		      snd_pcm_format_t sformat, int schannels, int srate,
Packit 4a16fb
		      const snd_config_t *rate_converter,
Packit 4a16fb
		      enum snd_pcm_plug_route_policy route_policy,
Packit 4a16fb
		      snd_pcm_route_ttable_entry_t *ttable,
Packit 4a16fb
		      unsigned int tt_ssize,
Packit 4a16fb
		      unsigned int tt_cused, unsigned int tt_sused,
Packit 4a16fb
		      snd_pcm_t *slave, int close_slave)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_t *pcm;
Packit 4a16fb
	snd_pcm_plug_t *plug;
Packit 4a16fb
	int err;
Packit 4a16fb
	assert(pcmp && slave);
Packit 4a16fb
Packit 4a16fb
	plug = calloc(1, sizeof(snd_pcm_plug_t));
Packit 4a16fb
	if (!plug)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	plug->sformat = sformat;
Packit 4a16fb
	plug->schannels = schannels;
Packit 4a16fb
	plug->srate = srate;
Packit 4a16fb
	plug->gen.slave = plug->req_slave = slave;
Packit 4a16fb
	plug->gen.close_slave = close_slave;
Packit 4a16fb
	plug->route_policy = route_policy;
Packit 4a16fb
	plug->ttable = ttable;
Packit 4a16fb
	plug->tt_ssize = tt_ssize;
Packit 4a16fb
	plug->tt_cused = tt_cused;
Packit 4a16fb
	plug->tt_sused = tt_sused;
Packit 4a16fb
	
Packit 4a16fb
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		free(plug);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	pcm->ops = &snd_pcm_plug_ops;
Packit 4a16fb
	pcm->fast_ops = slave->fast_ops;
Packit 4a16fb
	pcm->fast_op_arg = slave->fast_op_arg;
Packit 4a16fb
	if (rate_converter) {
Packit 4a16fb
		err = snd_config_copy(&plug->rate_converter,
Packit 4a16fb
				      (snd_config_t *)rate_converter);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			snd_pcm_free(pcm);
Packit 4a16fb
			free(plug);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	pcm->private_data = plug;
Packit 4a16fb
	pcm->poll_fd = slave->poll_fd;
Packit 4a16fb
	pcm->poll_events = slave->poll_events;
Packit 4a16fb
	pcm->mmap_shadow = 1;
Packit 4a16fb
	pcm->tstamp_type = slave->tstamp_type;
Packit 4a16fb
	snd_pcm_link_hw_ptr(pcm, slave);
Packit 4a16fb
	snd_pcm_link_appl_ptr(pcm, slave);
Packit 4a16fb
	*pcmp = pcm;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*! \page pcm_plugins
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_plug Automatic conversion plugin
Packit 4a16fb
Packit 4a16fb
This plugin converts channels, rate and format on request.
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.name {
Packit 4a16fb
        type plug               # Automatic conversion PCM
Packit 4a16fb
        slave STR               # Slave name
Packit 4a16fb
        # or
Packit 4a16fb
        slave {                 # Slave definition
Packit 4a16fb
                pcm STR         # Slave PCM name
Packit 4a16fb
                # or
Packit 4a16fb
                pcm { }         # Slave PCM definition
Packit 4a16fb
		[format STR]	# Slave format (default nearest) or "unchanged"
Packit 4a16fb
		[channels INT]	# Slave channels (default nearest) or "unchanged"
Packit 4a16fb
		[rate INT]	# Slave rate (default nearest) or "unchanged"
Packit 4a16fb
        }
Packit 4a16fb
	route_policy STR	# route policy for automatic ttable generation
Packit 4a16fb
				# STR can be 'default', 'average', 'copy', 'duplicate'
Packit 4a16fb
				# average: result is average of input channels
Packit 4a16fb
				# copy: only first channels are copied to destination
Packit 4a16fb
				# duplicate: duplicate first set of channels
Packit 4a16fb
				# default: copy policy, except for mono capture - sum
Packit 4a16fb
	ttable {		# Transfer table (bi-dimensional compound of cchannels * schannels numbers)
Packit 4a16fb
		CCHANNEL {
Packit 4a16fb
			SCHANNEL REAL	# route value (0.0 - 1.0)
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	rate_converter STR	# type of rate converter
Packit 4a16fb
	# or
Packit 4a16fb
	rate_converter [ STR1 STR2 ... ]
Packit 4a16fb
				# type of rate converter
Packit 4a16fb
				# default value is taken from defaults.pcm.rate_converter
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
\subsection pcm_plugins_plug_funcref Function reference
Packit 4a16fb
Packit 4a16fb
    Packit 4a16fb
      
  • snd_pcm_plug_open()
  • Packit 4a16fb
      
  • _snd_pcm_plug_open()
  • Packit 4a16fb
    Packit 4a16fb
    Packit 4a16fb
    */
    Packit 4a16fb
    Packit 4a16fb
    /**
    Packit 4a16fb
     * \brief Creates a new Plug PCM
    Packit 4a16fb
     * \param pcmp Returns created PCM handle
    Packit 4a16fb
     * \param name Name of PCM
    Packit 4a16fb
     * \param root Root configuration node
    Packit 4a16fb
     * \param conf Configuration node with Plug PCM description
    Packit 4a16fb
     * \param stream Stream type
    Packit 4a16fb
     * \param mode Stream mode
    Packit 4a16fb
     * \retval zero on success otherwise a negative error code
    Packit 4a16fb
     * \warning Using of this function might be dangerous in the sense
    Packit 4a16fb
     *          of compatibility reasons. The prototype might be freely
    Packit 4a16fb
     *          changed in future.
    Packit 4a16fb
     */
    Packit 4a16fb
    int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
    Packit 4a16fb
    		       snd_config_t *root, snd_config_t *conf, 
    Packit 4a16fb
    		       snd_pcm_stream_t stream, int mode)
    Packit 4a16fb
    {
    Packit 4a16fb
    	snd_config_iterator_t i, next;
    Packit 4a16fb
    	int err;
    Packit 4a16fb
    	snd_pcm_t *spcm;
    Packit 4a16fb
    	snd_config_t *slave = NULL, *sconf;
    Packit 4a16fb
    	snd_config_t *tt = NULL;
    Packit 4a16fb
    	enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
    Packit 4a16fb
    	snd_pcm_route_ttable_entry_t *ttable = NULL;
    Packit 4a16fb
    	unsigned int csize, ssize;
    Packit 4a16fb
    	unsigned int cused, sused;
    Packit 4a16fb
    	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    Packit 4a16fb
    	int schannels = -1, srate = -1;
    Packit 4a16fb
    	const snd_config_t *rate_converter = NULL;
    Packit 4a16fb
    Packit 4a16fb
    	snd_config_for_each(i, next, conf) {
    Packit 4a16fb
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit 4a16fb
    		const char *id;
    Packit 4a16fb
    		if (snd_config_get_id(n, &id) < 0)
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (snd_pcm_conf_generic_id(id))
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (strcmp(id, "slave") == 0) {
    Packit 4a16fb
    			slave = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    #ifdef BUILD_PCM_PLUGIN_ROUTE
    Packit 4a16fb
    		if (strcmp(id, "ttable") == 0) {
    Packit 4a16fb
    			route_policy = PLUG_ROUTE_POLICY_NONE;
    Packit 4a16fb
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				return -EINVAL;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			tt = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "route_policy") == 0) {
    Packit 4a16fb
    			const char *str;
    Packit 4a16fb
    			if ((err = snd_config_get_string(n, &str)) < 0) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				return -EINVAL;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			if (tt != NULL)
    Packit 4a16fb
    				SNDERR("Table is defined, route policy is ignored");
    Packit 4a16fb
    			if (!strcmp(str, "default"))
    Packit 4a16fb
    				route_policy = PLUG_ROUTE_POLICY_DEFAULT;
    Packit 4a16fb
    			else if (!strcmp(str, "average"))
    Packit 4a16fb
    				route_policy = PLUG_ROUTE_POLICY_AVERAGE;
    Packit 4a16fb
    			else if (!strcmp(str, "copy"))
    Packit 4a16fb
    				route_policy = PLUG_ROUTE_POLICY_COPY;
    Packit 4a16fb
    			else if (!strcmp(str, "duplicate"))
    Packit 4a16fb
    				route_policy = PLUG_ROUTE_POLICY_DUP;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    #endif
    Packit 4a16fb
    #ifdef BUILD_PCM_PLUGIN_RATE
    Packit 4a16fb
    		if (strcmp(id, "rate_converter") == 0) {
    Packit 4a16fb
    			rate_converter = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    #endif
    Packit 4a16fb
    		SNDERR("Unknown field %s", id);
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (!slave) {
    Packit 4a16fb
    		SNDERR("slave is not defined");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = snd_pcm_slave_conf(root, slave, &sconf, 3,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    #ifdef BUILD_PCM_PLUGIN_ROUTE
    Packit 4a16fb
    	if (tt) {
    Packit 4a16fb
    		err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
    Packit 4a16fb
    		if (err < 0) {
    Packit 4a16fb
    			snd_config_delete(sconf);
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		ttable = malloc(csize * ssize * sizeof(*ttable));
    Packit 4a16fb
    		if (ttable == NULL) {
    Packit 4a16fb
    			snd_config_delete(sconf);
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
    Packit 4a16fb
    		if (err < 0) {
    Packit 4a16fb
    			snd_config_delete(sconf);
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		}
    Packit 4a16fb
    	}
    Packit 4a16fb
    #endif
    Packit 4a16fb
    	
    Packit 4a16fb
    #ifdef BUILD_PCM_PLUGIN_RATE
    Packit 4a16fb
    	if (! rate_converter)
    Packit 4a16fb
    		rate_converter = snd_pcm_rate_get_default_converter(root);
    Packit 4a16fb
    #endif
    Packit 4a16fb
    Packit 4a16fb
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit 4a16fb
    	snd_config_delete(sconf);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
    Packit 4a16fb
    				route_policy, ttable, ssize, cused, sused, spcm, 1);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    	return err;
    Packit 4a16fb
    }
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
    Packit 4a16fb
    #endif