Blame src/pcm/pcm_softvol.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file pcm/pcm_softvol.c
Packit 4a16fb
 * \ingroup PCM_Plugins
Packit 4a16fb
 * \brief PCM Soft Volume Plugin Interface
Packit 4a16fb
 * \author Takashi Iwai <tiwai@suse.de>
Packit 4a16fb
 * \date 2004
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Soft Volume Plugin
Packit 4a16fb
 *  Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
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 "bswap.h"
Packit 4a16fb
#include <math.h>
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "pcm_plugin.h"
Packit 4a16fb
Packit 4a16fb
#include <sound/tlv.h>
Packit 4a16fb
Packit 4a16fb
#ifndef PIC
Packit 4a16fb
/* entry for static linking */
Packit 4a16fb
const char *_snd_module_pcm_softvol = "";
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	/* This field need to be the first */
Packit 4a16fb
	snd_pcm_plugin_t plug;
Packit 4a16fb
	snd_pcm_format_t sformat;
Packit 4a16fb
	unsigned int cchannels;
Packit 4a16fb
	snd_ctl_t *ctl;
Packit 4a16fb
	snd_ctl_elem_value_t elem;
Packit 4a16fb
	unsigned int cur_vol[2];
Packit 4a16fb
	unsigned int max_val;     /* max index */
Packit 4a16fb
	unsigned int zero_dB_val; /* index at 0 dB */
Packit 4a16fb
	double min_dB;
Packit 4a16fb
	double max_dB;
Packit 4a16fb
	unsigned int *dB_value;
Packit 4a16fb
} snd_pcm_softvol_t;
Packit 4a16fb
Packit 4a16fb
#define VOL_SCALE_SHIFT		16
Packit 4a16fb
#define VOL_SCALE_MASK          ((1 << VOL_SCALE_SHIFT) - 1)
Packit 4a16fb
Packit 4a16fb
#define PRESET_RESOLUTION	256
Packit 4a16fb
#define PRESET_MIN_DB		-51.0
Packit 4a16fb
#define ZERO_DB                  0.0
Packit 4a16fb
/*
Packit 4a16fb
 * The gain algorithm as it stands supports gain factors up to 32767, which
Packit 4a16fb
 * is a fraction more than 90 dB, so set 90 dB as the maximum possible gain.
Packit 4a16fb
 */
Packit 4a16fb
#define MAX_DB_UPPER_LIMIT      90
Packit 4a16fb
Packit 4a16fb
static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
Packit 4a16fb
	0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
Packit 4a16fb
	0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
Packit 4a16fb
	0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
Packit 4a16fb
	0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
Packit 4a16fb
	0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
Packit 4a16fb
	0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
Packit 4a16fb
	0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
Packit 4a16fb
	0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
Packit 4a16fb
	0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
Packit 4a16fb
	0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
Packit 4a16fb
	0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
Packit 4a16fb
	0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
Packit 4a16fb
	0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
Packit 4a16fb
	0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
Packit 4a16fb
	0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
Packit 4a16fb
	0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
Packit 4a16fb
	0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
Packit 4a16fb
	0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
Packit 4a16fb
	0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
Packit 4a16fb
	0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
Packit 4a16fb
	0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
Packit 4a16fb
	0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
Packit 4a16fb
	0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
Packit 4a16fb
	0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
Packit 4a16fb
	0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
Packit 4a16fb
	0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
Packit 4a16fb
	0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
Packit 4a16fb
	0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
Packit 4a16fb
	0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
Packit 4a16fb
	0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
Packit 4a16fb
	0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
Packit 4a16fb
	0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
/* (32bit x 16bit) >> 16 */
Packit 4a16fb
typedef union {
Packit 4a16fb
	int i;
Packit 4a16fb
	short s[2];
Packit 4a16fb
} val_t;
Packit 4a16fb
static inline int MULTI_DIV_32x16(int a, unsigned short b)
Packit 4a16fb
{
Packit 4a16fb
	val_t v, x, y;
Packit 4a16fb
	v.i = a;
Packit 4a16fb
	y.i = 0;
Packit 4a16fb
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit 4a16fb
	x.i = (unsigned short)v.s[0];
Packit 4a16fb
	x.i *= b;
Packit 4a16fb
	y.s[0] = x.s[1];
Packit 4a16fb
	y.i += (int)v.s[1] * b;
Packit 4a16fb
#else
Packit 4a16fb
	x.i = (unsigned int)v.s[1] * b;
Packit 4a16fb
	y.s[1] = x.s[0];
Packit 4a16fb
	y.i += (int)v.s[0] * b;
Packit 4a16fb
#endif
Packit 4a16fb
	return y.i;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int gain = (b >> VOL_SCALE_SHIFT);
Packit 4a16fb
	int fraction;
Packit 4a16fb
	a = swap ? (int)bswap_32(a) : a;
Packit 4a16fb
	fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
Packit 4a16fb
	if (gain) {
Packit 4a16fb
		long long amp = (long long)a * gain + fraction;
Packit 4a16fb
		if (amp > (int)0x7fffffff)
Packit 4a16fb
			amp = (int)0x7fffffff;
Packit 4a16fb
		else if (amp < (int)0x80000000)
Packit 4a16fb
			amp = (int)0x80000000;
Packit 4a16fb
		return swap ? (int)bswap_32((int)amp) : (int)amp;
Packit 4a16fb
	}
Packit 4a16fb
	return swap ? (int)bswap_32(fraction) : fraction;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* always little endian */
Packit 4a16fb
static inline int MULTI_DIV_24(int a, unsigned int b)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int gain = b >> VOL_SCALE_SHIFT;
Packit 4a16fb
	int fraction;
Packit 4a16fb
	fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
Packit 4a16fb
	if (gain) {
Packit 4a16fb
		long long amp = (long long)a * gain + fraction;
Packit 4a16fb
		if (amp > (int)0x7fffff)
Packit 4a16fb
			amp = (int)0x7fffff;
Packit 4a16fb
		else if (amp < (int)0x800000)
Packit 4a16fb
			amp = (int)0x800000;
Packit 4a16fb
		return (int)amp;
Packit 4a16fb
	}
Packit 4a16fb
	return fraction;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int gain = b >> VOL_SCALE_SHIFT;
Packit 4a16fb
	int fraction;
Packit 4a16fb
	a = swap ? (short)bswap_16(a) : a;
Packit 4a16fb
	fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
Packit 4a16fb
	if (gain) {
Packit 4a16fb
		int amp = a * gain + fraction;
Packit 4a16fb
		if (abs(amp) > 0x7fff)
Packit 4a16fb
			amp = (a<0) ? (short)0x8000 : (short)0x7fff;
Packit 4a16fb
		return swap ? (short)bswap_16((short)amp) : (short)amp;
Packit 4a16fb
	}
Packit 4a16fb
	return swap ? (short)bswap_16((short)fraction) : (short)fraction;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#endif /* DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * apply volumue attenuation
Packit 4a16fb
 *
Packit 4a16fb
 * TODO: use SIMD operations
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
#define CONVERT_AREA(TYPE, swap) do {	\
Packit 4a16fb
	unsigned int ch, fr; \
Packit 4a16fb
	TYPE *src, *dst; \
Packit 4a16fb
	for (ch = 0; ch < channels; ch++) { \
Packit 4a16fb
		src_area = &src_areas[ch]; \
Packit 4a16fb
		dst_area = &dst_areas[ch]; \
Packit 4a16fb
		src = snd_pcm_channel_area_addr(src_area, src_offset); \
Packit 4a16fb
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
Packit 4a16fb
		src_step = snd_pcm_channel_area_step(src_area) / sizeof(TYPE); \
Packit 4a16fb
		dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
Packit 4a16fb
		GET_VOL_SCALE; \
Packit 4a16fb
		fr = frames; \
Packit 4a16fb
		if (! vol_scale) { \
Packit 4a16fb
			while (fr--) { \
Packit 4a16fb
				*dst = 0; \
Packit 4a16fb
				dst += dst_step; \
Packit 4a16fb
			} \
Packit 4a16fb
		} else if (vol_scale == 0xffff) { \
Packit 4a16fb
			while (fr--) { \
Packit 4a16fb
				*dst = *src; \
Packit 4a16fb
				src += src_step; \
Packit 4a16fb
				dst += dst_step; \
Packit 4a16fb
			} \
Packit 4a16fb
		} else { \
Packit 4a16fb
			while (fr--) { \
Packit 4a16fb
				*dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
Packit 4a16fb
				src += src_step; \
Packit 4a16fb
				dst += dst_step; \
Packit 4a16fb
			} \
Packit 4a16fb
		} \
Packit 4a16fb
	} \
Packit 4a16fb
} while (0)
Packit 4a16fb
Packit 4a16fb
#define CONVERT_AREA_S24_3LE() do {					\
Packit 4a16fb
	unsigned int ch, fr;						\
Packit 4a16fb
	unsigned char *src, *dst;					\
Packit 4a16fb
	int tmp;							\
Packit 4a16fb
	for (ch = 0; ch < channels; ch++) {				\
Packit 4a16fb
		src_area = &src_areas[ch];				\
Packit 4a16fb
		dst_area = &dst_areas[ch];				\
Packit 4a16fb
		src = snd_pcm_channel_area_addr(src_area, src_offset);	\
Packit 4a16fb
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);	\
Packit 4a16fb
		src_step = snd_pcm_channel_area_step(src_area);		\
Packit 4a16fb
		dst_step = snd_pcm_channel_area_step(dst_area);		\
Packit 4a16fb
		GET_VOL_SCALE;						\
Packit 4a16fb
		fr = frames;						\
Packit 4a16fb
		if (! vol_scale) {					\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				dst[0] = dst[1] = dst[2] = 0;		\
Packit 4a16fb
				dst += dst_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		} else if (vol_scale == 0xffff) {			\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				dst[0] = src[0];			\
Packit 4a16fb
				dst[1] = src[1];			\
Packit 4a16fb
				dst[2] = src[2];			\
Packit 4a16fb
				src += dst_step;			\
Packit 4a16fb
				dst += src_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		} else {						\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				tmp = src[0] |				\
Packit 4a16fb
				      (src[1] << 8) |			\
Packit 4a16fb
				      (((signed char *) src)[2] << 16);	\
Packit 4a16fb
				tmp = MULTI_DIV_24(tmp, vol_scale);	\
Packit 4a16fb
				dst[0] = tmp;				\
Packit 4a16fb
				dst[1] = tmp >> 8;			\
Packit 4a16fb
				dst[2] = tmp >> 16;			\
Packit 4a16fb
				src += dst_step;			\
Packit 4a16fb
				dst += src_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		}							\
Packit 4a16fb
	}								\
Packit 4a16fb
} while (0)
Packit 4a16fb
Packit 4a16fb
#define CONVERT_AREA_S24_LE() do {					\
Packit 4a16fb
	unsigned int ch, fr;						\
Packit 4a16fb
	int *src, *dst;							\
Packit 4a16fb
	int tmp;							\
Packit 4a16fb
	for (ch = 0; ch < channels; ch++) {				\
Packit 4a16fb
		src_area = &src_areas[ch];				\
Packit 4a16fb
		dst_area = &dst_areas[ch];				\
Packit 4a16fb
		src = snd_pcm_channel_area_addr(src_area, src_offset);	\
Packit 4a16fb
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);	\
Packit 4a16fb
		src_step = snd_pcm_channel_area_step(src_area)		\
Packit 4a16fb
				/ sizeof(int);				\
Packit 4a16fb
		dst_step = snd_pcm_channel_area_step(dst_area)		\
Packit 4a16fb
				/ sizeof(int);				\
Packit 4a16fb
		GET_VOL_SCALE;						\
Packit 4a16fb
		fr = frames;						\
Packit 4a16fb
		if (! vol_scale) {					\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				*dst = 0;				\
Packit 4a16fb
				dst += dst_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		} else if (vol_scale == 0xffff) {			\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				*dst = *src;				\
Packit 4a16fb
				src += dst_step;			\
Packit 4a16fb
				dst += src_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		} else {						\
Packit 4a16fb
			while (fr--) {					\
Packit 4a16fb
				tmp = *src << 8;			\
Packit 4a16fb
				tmp = (signed int) tmp >> 8;		\
Packit 4a16fb
				*dst = MULTI_DIV_24(tmp, vol_scale);	\
Packit 4a16fb
				src += dst_step;			\
Packit 4a16fb
				dst += src_step;			\
Packit 4a16fb
			}						\
Packit 4a16fb
		}							\
Packit 4a16fb
	}								\
Packit 4a16fb
} while (0)
Packit 4a16fb
		
Packit 4a16fb
#define GET_VOL_SCALE \
Packit 4a16fb
	switch (ch) { \
Packit 4a16fb
	case 0: \
Packit 4a16fb
	case 2: \
Packit 4a16fb
		vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
Packit 4a16fb
		break; \
Packit 4a16fb
	case 4: \
Packit 4a16fb
	case 5: \
Packit 4a16fb
		vol_scale = vol_c; \
Packit 4a16fb
		break; \
Packit 4a16fb
	default: \
Packit 4a16fb
		vol_scale = vol[ch & 1]; \
Packit 4a16fb
		break; \
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
#endif /* DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
/* 2-channel stereo control */
Packit 4a16fb
static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
Packit 4a16fb
				       const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
				       snd_pcm_uframes_t dst_offset,
Packit 4a16fb
				       const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
				       snd_pcm_uframes_t src_offset,
Packit 4a16fb
				       unsigned int channels,
Packit 4a16fb
				       snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	const snd_pcm_channel_area_t *dst_area, *src_area;
Packit 4a16fb
	unsigned int src_step, dst_step;
Packit 4a16fb
	unsigned int vol_scale, vol[2], vol_c;
Packit 4a16fb
Packit 4a16fb
	if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
Packit 4a16fb
		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
Packit 4a16fb
				      svol->sformat);
Packit 4a16fb
		return;
Packit 4a16fb
	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
Packit 4a16fb
		   svol->cur_vol[1] == svol->zero_dB_val) {
Packit 4a16fb
		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
Packit 4a16fb
				   channels, frames, svol->sformat);
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (svol->max_val == 1) {
Packit 4a16fb
		vol[0] = svol->cur_vol[0] ? 0xffff : 0;
Packit 4a16fb
		vol[1] = svol->cur_vol[1] ? 0xffff : 0;
Packit 4a16fb
		vol_c = vol[0] | vol[1];
Packit 4a16fb
	} else {
Packit 4a16fb
		vol[0] = svol->dB_value[svol->cur_vol[0]];
Packit 4a16fb
		vol[1] = svol->dB_value[svol->cur_vol[1]];
Packit 4a16fb
		vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
Packit 4a16fb
	}
Packit 4a16fb
	switch (svol->sformat) {
Packit 4a16fb
	case SND_PCM_FORMAT_S16_LE:
Packit 4a16fb
	case SND_PCM_FORMAT_S16_BE:
Packit 4a16fb
		/* 16bit samples */
Packit 4a16fb
		CONVERT_AREA(short, 
Packit 4a16fb
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S32_LE:
Packit 4a16fb
	case SND_PCM_FORMAT_S32_BE:
Packit 4a16fb
		/* 32bit samples */
Packit 4a16fb
		CONVERT_AREA(int,
Packit 4a16fb
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S24_LE:
Packit 4a16fb
		/* 24bit samples */
Packit 4a16fb
		CONVERT_AREA_S24_LE();
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S24_3LE:
Packit 4a16fb
		CONVERT_AREA_S24_3LE();
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#undef GET_VOL_SCALE
Packit 4a16fb
#define GET_VOL_SCALE
Packit 4a16fb
Packit 4a16fb
/* mono control */
Packit 4a16fb
static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
Packit 4a16fb
				     const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
				     snd_pcm_uframes_t dst_offset,
Packit 4a16fb
				     const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
				     snd_pcm_uframes_t src_offset,
Packit 4a16fb
				     unsigned int channels,
Packit 4a16fb
				     snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	const snd_pcm_channel_area_t *dst_area, *src_area;
Packit 4a16fb
	unsigned int src_step, dst_step;
Packit 4a16fb
	unsigned int vol_scale;
Packit 4a16fb
Packit 4a16fb
	if (svol->cur_vol[0] == 0) {
Packit 4a16fb
		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
Packit 4a16fb
				      svol->sformat);
Packit 4a16fb
		return;
Packit 4a16fb
	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
Packit 4a16fb
		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
Packit 4a16fb
				   channels, frames, svol->sformat);
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (svol->max_val == 1)
Packit 4a16fb
		vol_scale = svol->cur_vol[0] ? 0xffff : 0;
Packit 4a16fb
	else
Packit 4a16fb
		vol_scale = svol->dB_value[svol->cur_vol[0]];
Packit 4a16fb
	switch (svol->sformat) {
Packit 4a16fb
	case SND_PCM_FORMAT_S16_LE:
Packit 4a16fb
	case SND_PCM_FORMAT_S16_BE:
Packit 4a16fb
		/* 16bit samples */
Packit 4a16fb
		CONVERT_AREA(short, 
Packit 4a16fb
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S32_LE:
Packit 4a16fb
	case SND_PCM_FORMAT_S32_BE:
Packit 4a16fb
		/* 32bit samples */
Packit 4a16fb
		CONVERT_AREA(int,
Packit 4a16fb
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S24_LE:
Packit 4a16fb
		/* 24bit samples */
Packit 4a16fb
		CONVERT_AREA_S24_LE();
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_FORMAT_S24_3LE:
Packit 4a16fb
		CONVERT_AREA_S24_3LE();
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * get the current volume value from driver
Packit 4a16fb
 *
Packit 4a16fb
 * TODO: mmap support?
Packit 4a16fb
 */
Packit 4a16fb
static void get_current_volume(snd_pcm_softvol_t *svol)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int val;
Packit 4a16fb
	unsigned int i;
Packit 4a16fb
Packit 4a16fb
	if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
Packit 4a16fb
		return;
Packit 4a16fb
	for (i = 0; i < svol->cchannels; i++) {
Packit 4a16fb
		val = svol->elem.value.integer.value[i];
Packit 4a16fb
		if (val > svol->max_val)
Packit 4a16fb
			val = svol->max_val;
Packit 4a16fb
		svol->cur_vol[i] = val;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void softvol_free(snd_pcm_softvol_t *svol)
Packit 4a16fb
{
Packit 4a16fb
	if (svol->plug.gen.close_slave)
Packit 4a16fb
		snd_pcm_close(svol->plug.gen.slave);
Packit 4a16fb
	if (svol->ctl)
Packit 4a16fb
		snd_ctl_close(svol->ctl);
Packit 4a16fb
	if (svol->dB_value && svol->dB_value != preset_dB_value)
Packit 4a16fb
		free(svol->dB_value);
Packit 4a16fb
	free(svol);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_softvol_close(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	softvol_free(svol);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
Packit 4a16fb
					      snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
Packit 4a16fb
	snd_pcm_format_mask_t format_mask = {
Packit 4a16fb
		{
Packit 4a16fb
			(1ULL << SND_PCM_FORMAT_S16_LE) |
Packit 4a16fb
			(1ULL << SND_PCM_FORMAT_S16_BE) |
Packit 4a16fb
			(1ULL << SND_PCM_FORMAT_S24_LE) |
Packit 4a16fb
			(1ULL << SND_PCM_FORMAT_S32_LE) |
Packit 4a16fb
 			(1ULL << SND_PCM_FORMAT_S32_BE),
Packit 4a16fb
			(1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
Packit 4a16fb
		}
Packit 4a16fb
	};
Packit 4a16fb
	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
Packit 4a16fb
		snd_pcm_format_mask_none(&format_mask);
Packit 4a16fb
		snd_pcm_format_mask_set(&format_mask, svol->sformat);
Packit 4a16fb
	}
Packit 4a16fb
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
					 &access_mask);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit 4a16fb
					 &format_mask);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
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_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
Packit 4a16fb
	_snd_pcm_hw_params_any(sparams);
Packit 4a16fb
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
				   &saccess_mask);
Packit 4a16fb
	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
Packit 4a16fb
		_snd_pcm_hw_params_set_format(sparams, svol->sformat);
Packit 4a16fb
		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * refine the access mask
Packit 4a16fb
 */
Packit 4a16fb
static int check_access_mask(snd_pcm_hw_params_t *src,
Packit 4a16fb
			     snd_pcm_hw_params_t *dst)
Packit 4a16fb
{
Packit 4a16fb
	const snd_pcm_access_mask_t *mask;
Packit 4a16fb
	snd_pcm_access_mask_t smask;
Packit 4a16fb
Packit 4a16fb
	mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
Packit 4a16fb
	snd_mask_none(&smask;;
Packit 4a16fb
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
Packit 4a16fb
		snd_pcm_access_mask_set(&smask,
Packit 4a16fb
					SND_PCM_ACCESS_RW_INTERLEAVED);
Packit 4a16fb
		snd_pcm_access_mask_set(&smask,
Packit 4a16fb
					SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit 4a16fb
	}
Packit 4a16fb
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
Packit 4a16fb
	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))  {
Packit 4a16fb
		snd_pcm_access_mask_set(&smask,
Packit 4a16fb
					SND_PCM_ACCESS_RW_NONINTERLEAVED);
Packit 4a16fb
		snd_pcm_access_mask_set(&smask,
Packit 4a16fb
					SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
Packit 4a16fb
	}
Packit 4a16fb
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
Packit 4a16fb
		snd_pcm_access_mask_set(&smask,
Packit 4a16fb
					SND_PCM_ACCESS_MMAP_COMPLEX);
Packit 4a16fb
Packit 4a16fb
	return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask;;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
Packit 4a16fb
					     snd_pcm_hw_params_t *params,
Packit 4a16fb
					     snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	err = check_access_mask(params, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
	
Packit 4a16fb
static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
Packit 4a16fb
					     snd_pcm_hw_params_t *params,
Packit 4a16fb
					    snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit 4a16fb
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit 4a16fb
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	err = check_access_mask(sparams, params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_softvol_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_softvol_hw_refine_cprepare,
Packit 4a16fb
				       snd_pcm_softvol_hw_refine_cchange,
Packit 4a16fb
				       snd_pcm_softvol_hw_refine_sprepare,
Packit 4a16fb
				       snd_pcm_softvol_hw_refine_schange,
Packit 4a16fb
				       snd_pcm_generic_hw_refine);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = svol->plug.gen.slave;
Packit 4a16fb
	int err = snd_pcm_hw_params_slave(pcm, params,
Packit 4a16fb
					  snd_pcm_softvol_hw_refine_cchange,
Packit 4a16fb
					  snd_pcm_softvol_hw_refine_sprepare,
Packit 4a16fb
					  snd_pcm_softvol_hw_refine_schange,
Packit 4a16fb
					  snd_pcm_generic_hw_params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (slave->format != SND_PCM_FORMAT_S16_LE &&
Packit 4a16fb
	    slave->format != SND_PCM_FORMAT_S16_BE &&
Packit 4a16fb
	    slave->format != SND_PCM_FORMAT_S24_3LE && 
Packit 4a16fb
	    slave->format != SND_PCM_FORMAT_S24_LE &&
Packit 4a16fb
	    slave->format != SND_PCM_FORMAT_S32_LE &&
Packit 4a16fb
	    slave->format != SND_PCM_FORMAT_S32_BE) {
Packit 4a16fb
		SNDERR("softvol supports only S16_LE, S16_BE, S24_LE, S24_3LE, "
Packit 4a16fb
		       "S32_LE or S32_BE");
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	svol->sformat = slave->format;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
Packit 4a16fb
			    const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			    snd_pcm_uframes_t offset,
Packit 4a16fb
			    snd_pcm_uframes_t size,
Packit 4a16fb
			    const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			    snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			    snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	get_current_volume(svol);
Packit 4a16fb
	if (svol->cchannels == 1)
Packit 4a16fb
		softvol_convert_mono_vol(svol, slave_areas, slave_offset,
Packit 4a16fb
					 areas, offset, pcm->channels, size);
Packit 4a16fb
	else
Packit 4a16fb
		softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
Packit 4a16fb
					   areas, offset, pcm->channels, size);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
Packit 4a16fb
			   const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			   snd_pcm_uframes_t offset,
Packit 4a16fb
			   snd_pcm_uframes_t size,
Packit 4a16fb
			   const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			   snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			   snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	get_current_volume(svol);
Packit 4a16fb
	if (svol->cchannels == 1)
Packit 4a16fb
		softvol_convert_mono_vol(svol, areas, offset, slave_areas,
Packit 4a16fb
					 slave_offset, pcm->channels, size);
Packit 4a16fb
	else
Packit 4a16fb
		softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
Packit 4a16fb
					   slave_offset, pcm->channels, size);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit 4a16fb
	snd_output_printf(out, "Soft volume PCM\n");
Packit 4a16fb
	snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
Packit 4a16fb
	if (svol->max_val == 1)
Packit 4a16fb
		snd_output_printf(out, "boolean\n");
Packit 4a16fb
	else {
Packit 4a16fb
		snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
Packit 4a16fb
		snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
Packit 4a16fb
		snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
Packit 4a16fb
	}
Packit 4a16fb
	if (pcm->setup) {
Packit 4a16fb
		snd_output_printf(out, "Its setup is:\n");
Packit 4a16fb
		snd_pcm_dump_setup(pcm, out);
Packit 4a16fb
	}
Packit 4a16fb
	snd_output_printf(out, "Slave: ");
Packit 4a16fb
	snd_pcm_dump(svol->plug.gen.slave, out);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
Packit 4a16fb
{
Packit 4a16fb
	unsigned int tlv[4];
Packit 4a16fb
	tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
Packit 4a16fb
	tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
Packit 4a16fb
	tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
Packit 4a16fb
	tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
Packit 4a16fb
		(int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
Packit 4a16fb
	return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
Packit 4a16fb
			int count)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	int i;
Packit 4a16fb
	unsigned int def_val;
Packit 4a16fb
	
Packit 4a16fb
	if (svol->max_val == 1)
Packit 4a16fb
		err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
Packit 4a16fb
	else
Packit 4a16fb
		err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
Packit 4a16fb
						   0, svol->max_val, 0);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (svol->max_val == 1)
Packit 4a16fb
		def_val = 1;
Packit 4a16fb
	else {
Packit 4a16fb
		add_tlv_info(svol, cinfo);
Packit 4a16fb
		/* set zero dB value as default, or max_val if
Packit 4a16fb
		   there is no 0 dB setting */
Packit 4a16fb
		def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
Packit 4a16fb
	}
Packit 4a16fb
	for (i = 0; i < count; i++)
Packit 4a16fb
		svol->elem.value.integer.value[i] = def_val;
Packit 4a16fb
	return snd_ctl_elem_write(svol->ctl, &svol->elem);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * load and set up user-control
Packit 4a16fb
 * returns 0 if the user-control is found or created,
Packit 4a16fb
 * returns 1 if the control is a hw control,
Packit 4a16fb
 * or a negative error code
Packit 4a16fb
 */
Packit 4a16fb
static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
Packit 4a16fb
				int ctl_card, snd_ctl_elem_id_t *ctl_id,
Packit 4a16fb
				int cchannels, double min_dB, double max_dB,
Packit 4a16fb
				int resolution)
Packit 4a16fb
{
Packit 4a16fb
	char tmp_name[32];
Packit 4a16fb
	snd_pcm_info_t info = {0};
Packit 4a16fb
	snd_ctl_elem_info_t cinfo = {0};
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int i;
Packit 4a16fb
Packit 4a16fb
	if (ctl_card < 0) {
Packit 4a16fb
		err = snd_pcm_info(pcm, &info;;
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		ctl_card = snd_pcm_info_get_card(&info;;
Packit 4a16fb
		if (ctl_card < 0) {
Packit 4a16fb
			SNDERR("No card defined for softvol control");
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	sprintf(tmp_name, "hw:%d", ctl_card);
Packit 4a16fb
	err = snd_ctl_open(&svol->ctl, tmp_name, 0);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		SNDERR("Cannot open CTL %s", tmp_name);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	svol->elem.id = *ctl_id;
Packit 4a16fb
	svol->max_val = resolution - 1;
Packit 4a16fb
	svol->min_dB = min_dB;
Packit 4a16fb
	svol->max_dB = max_dB;
Packit 4a16fb
	if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
Packit 4a16fb
		svol->zero_dB_val = svol->max_val;
Packit 4a16fb
	else if (svol->max_dB < 0)
Packit 4a16fb
		svol->zero_dB_val = 0; /* there is no 0 dB setting */
Packit 4a16fb
	else
Packit 4a16fb
		svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
Packit 4a16fb
								svol->max_val;
Packit 4a16fb
		
Packit 4a16fb
	snd_ctl_elem_info_set_id(&cinfo, ctl_id);
Packit 4a16fb
	if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
Packit 4a16fb
		if (err != -ENOENT) {
Packit 4a16fb
			SNDERR("Cannot get info for CTL %s", tmp_name);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		err = add_user_ctl(svol, &cinfo, cchannels);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			SNDERR("Cannot add a control");
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
Packit 4a16fb
			/* hardware control exists */
Packit 4a16fb
			return 1; /* notify */
Packit 4a16fb
Packit 4a16fb
		} else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
Packit 4a16fb
			    cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
Packit 4a16fb
			   cinfo.count != (unsigned int)cchannels ||
Packit 4a16fb
			   cinfo.value.integer.min != 0 ||
Packit 4a16fb
			   cinfo.value.integer.max != resolution - 1) {
Packit 4a16fb
			err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SNDERR("Control %s mismatch", tmp_name);
Packit 4a16fb
				return err;
Packit 4a16fb
			}
Packit 4a16fb
			/* reset numid */
Packit 4a16fb
			snd_ctl_elem_info_set_id(&cinfo, ctl_id);
Packit 4a16fb
			if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
Packit 4a16fb
				SNDERR("Cannot add a control");
Packit 4a16fb
				return err;
Packit 4a16fb
			}
Packit 4a16fb
		} else if (svol->max_val > 1) {
Packit 4a16fb
			/* check TLV availability */
Packit 4a16fb
			unsigned int tlv[4];
Packit 4a16fb
			err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
Packit 4a16fb
						    sizeof(tlv));
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				add_tlv_info(svol, &cinfo);
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (svol->max_val == 1)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/* set up dB table */
Packit 4a16fb
	if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
Packit 4a16fb
						resolution == PRESET_RESOLUTION)
Packit 4a16fb
		svol->dB_value = (unsigned int*)preset_dB_value;
Packit 4a16fb
	else {
Packit 4a16fb
#ifndef HAVE_SOFT_FLOAT
Packit 4a16fb
		svol->dB_value = calloc(resolution, sizeof(unsigned int));
Packit 4a16fb
		if (! svol->dB_value) {
Packit 4a16fb
			SNDERR("cannot allocate dB table");
Packit 4a16fb
			return -ENOMEM;
Packit 4a16fb
		}
Packit 4a16fb
		svol->min_dB = min_dB;
Packit 4a16fb
		svol->max_dB = max_dB;
Packit 4a16fb
		for (i = 0; i <= svol->max_val; i++) {
Packit 4a16fb
			double db = svol->min_dB +
Packit 4a16fb
				(i * (svol->max_dB - svol->min_dB)) /
Packit 4a16fb
					svol->max_val;
Packit 4a16fb
			double v = (pow(10.0, db / 20.0) *
Packit 4a16fb
					(double)(1 << VOL_SCALE_SHIFT));
Packit 4a16fb
			svol->dB_value[i] = (unsigned int)v;
Packit 4a16fb
		}
Packit 4a16fb
		if (svol->zero_dB_val)
Packit 4a16fb
			svol->dB_value[svol->zero_dB_val] = 65535;
Packit 4a16fb
#else
Packit 4a16fb
		SNDERR("Cannot handle the given dB range and resolution");
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
#endif
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_ops_t snd_pcm_softvol_ops = {
Packit 4a16fb
	.close = snd_pcm_softvol_close,
Packit 4a16fb
	.info = snd_pcm_generic_info,
Packit 4a16fb
	.hw_refine = snd_pcm_softvol_hw_refine,
Packit 4a16fb
	.hw_params = snd_pcm_softvol_hw_params,
Packit 4a16fb
	.hw_free = snd_pcm_generic_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_softvol_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 SoftVolume PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param sformat Slave format
Packit 4a16fb
 * \param ctl_card card index of the control
Packit 4a16fb
 * \param ctl_id The control element
Packit 4a16fb
 * \param cchannels PCM channels
Packit 4a16fb
 * \param min_dB minimal dB value
Packit 4a16fb
 * \param max_dB maximal dB value
Packit 4a16fb
 * \param resolution resolution of control
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_softvol_open(snd_pcm_t **pcmp, const char *name,
Packit 4a16fb
			 snd_pcm_format_t sformat,
Packit 4a16fb
			 int ctl_card, snd_ctl_elem_id_t *ctl_id,
Packit 4a16fb
			 int cchannels,
Packit 4a16fb
			 double min_dB, double max_dB, int resolution,
Packit 4a16fb
			 snd_pcm_t *slave, int close_slave)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_t *pcm;
Packit 4a16fb
	snd_pcm_softvol_t *svol;
Packit 4a16fb
	int err;
Packit 4a16fb
	assert(pcmp && slave);
Packit 4a16fb
	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S16_LE &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S16_BE &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S24_3LE && 
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S24_LE &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S32_LE &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_S32_BE)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	svol = calloc(1, sizeof(*svol));
Packit 4a16fb
	if (! svol)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
Packit 4a16fb
				   min_dB, max_dB, resolution);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		softvol_free(svol);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (err > 0) { /* hardware control - no need for softvol! */
Packit 4a16fb
		softvol_free(svol);
Packit 4a16fb
		*pcmp = slave; /* just pass the slave */
Packit 4a16fb
		if (!slave->name && name)
Packit 4a16fb
			slave->name = strdup(name);
Packit 4a16fb
		return 0;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* do softvol */
Packit 4a16fb
	snd_pcm_plugin_init(&svol->plug);
Packit 4a16fb
	svol->sformat = sformat;
Packit 4a16fb
	svol->cchannels = cchannels;
Packit 4a16fb
	svol->plug.read = snd_pcm_softvol_read_areas;
Packit 4a16fb
	svol->plug.write = snd_pcm_softvol_write_areas;
Packit 4a16fb
	svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
Packit 4a16fb
	svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
Packit 4a16fb
	svol->plug.gen.slave = slave;
Packit 4a16fb
	svol->plug.gen.close_slave = close_slave;
Packit 4a16fb
Packit 4a16fb
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		softvol_free(svol);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	pcm->ops = &snd_pcm_softvol_ops;
Packit 4a16fb
	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
Packit 4a16fb
	pcm->private_data = svol;
Packit 4a16fb
	pcm->poll_fd = slave->poll_fd;
Packit 4a16fb
	pcm->poll_events = slave->poll_events;
Packit 4a16fb
	/*
Packit 4a16fb
	 * Since the softvol converts on the place, and the format/channels
Packit 4a16fb
	 * must be identical between source and destination, we don't need
Packit 4a16fb
	 * an extra buffer.
Packit 4a16fb
	 */
Packit 4a16fb
	pcm->mmap_shadow = 1;
Packit 4a16fb
	pcm->tstamp_type = slave->tstamp_type;
Packit 4a16fb
	snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
Packit 4a16fb
	snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
Packit 4a16fb
	*pcmp = pcm;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* in pcm_misc.c */
Packit 4a16fb
int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
Packit 4a16fb
			     int *cchannelsp, int *hwctlp);
Packit 4a16fb
Packit 4a16fb
/*! \page pcm_plugins
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_softvol Plugin: Soft Volume
Packit 4a16fb
Packit 4a16fb
This plugin applies the software volume attenuation.
Packit 4a16fb
The format, rate and channels must match for both of source and destination.
Packit 4a16fb
Packit 4a16fb
When the control is stereo (count=2), the channels are assumed to be either
Packit 4a16fb
mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
Packit 4a16fb
Packit 4a16fb
If the control already exists and it's a system control (i.e. no
Packit 4a16fb
user-defined control), the plugin simply passes its slave without
Packit 4a16fb
any changes.
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.name {
Packit 4a16fb
        type softvol            # Soft Volume 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
Packit 4a16fb
        }
Packit 4a16fb
        control {
Packit 4a16fb
	        name STR        # control element id string
Packit 4a16fb
		[card STR]      # control card index
Packit 4a16fb
		[iface STR]     # interface of the element
Packit 4a16fb
		[index INT]     # index of the element
Packit 4a16fb
		[device INT]    # device number of the element
Packit 4a16fb
		[subdevice INT] # subdevice number of the element
Packit 4a16fb
		[count INT]     # control channels 1 or 2 (default: 2)
Packit 4a16fb
	}
Packit 4a16fb
	[min_dB REAL]           # minimal dB value (default: -51.0)
Packit 4a16fb
	[max_dB REAL]           # maximal dB value (default:   0.0)
Packit 4a16fb
	[resolution INT]        # resolution (default: 256)
Packit 4a16fb
				# resolution = 2 means a mute switch
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
\subsection pcm_plugins_softvol_funcref Function reference
Packit 4a16fb
Packit 4a16fb
    Packit 4a16fb
      
  • snd_pcm_softvol_open()
  • Packit 4a16fb
      
  • _snd_pcm_softvol_open()
  • Packit 4a16fb
    Packit 4a16fb
    Packit 4a16fb
    */
    Packit 4a16fb
    Packit 4a16fb
    /**
    Packit 4a16fb
     * \brief Creates a new Soft Volume 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 Soft Volume 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_softvol_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 *control = NULL;
    Packit 4a16fb
    	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    Packit 4a16fb
    	snd_ctl_elem_id_t ctl_id = {0};
    Packit 4a16fb
    	int resolution = PRESET_RESOLUTION;
    Packit 4a16fb
    	double min_dB = PRESET_MIN_DB;
    Packit 4a16fb
    	double max_dB = ZERO_DB;
    Packit 4a16fb
    	int card = -1, cchannels = 2;
    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
    		if (strcmp(id, "control") == 0) {
    Packit 4a16fb
    			control = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "resolution") == 0) {
    Packit 4a16fb
    			long v;
    Packit 4a16fb
    			err = snd_config_get_integer(n, &v);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid resolution value");
    Packit 4a16fb
    				return err;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			resolution = v;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "min_dB") == 0) {
    Packit 4a16fb
    			err = snd_config_get_real(n, &min_dB);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid min_dB value");
    Packit 4a16fb
    				return err;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "max_dB") == 0) {
    Packit 4a16fb
    			err = snd_config_get_real(n, &max_dB);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid max_dB value");
    Packit 4a16fb
    				return err;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    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
    	if (!control) {
    Packit 4a16fb
    		SNDERR("control is not defined");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (min_dB >= 0) {
    Packit 4a16fb
    		SNDERR("min_dB must be a negative value");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
    Packit 4a16fb
    		SNDERR("max_dB must be larger than min_dB and less than %d dB",
    Packit 4a16fb
    		       MAX_DB_UPPER_LIMIT);
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (resolution <= 1 || resolution > 1024) {
    Packit 4a16fb
    		SNDERR("Invalid resolution value %d", resolution);
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (mode & SND_PCM_NO_SOFTVOL) {
    Packit 4a16fb
    		err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    Packit 4a16fb
    		if (err < 0)
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
    Packit 4a16fb
    					       mode, conf);
    Packit 4a16fb
    		snd_config_delete(sconf);
    Packit 4a16fb
    	} else {
    Packit 4a16fb
    		err = snd_pcm_slave_conf(root, slave, &sconf, 1,
    Packit 4a16fb
    					 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
    Packit 4a16fb
    		if (err < 0)
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		if (sformat != SND_PCM_FORMAT_UNKNOWN &&
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S16_LE &&
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S16_BE &&
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S24_3LE && 
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S24_LE &&
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S32_LE &&
    Packit 4a16fb
    		    sformat != SND_PCM_FORMAT_S32_BE) {
    Packit 4a16fb
    			SNDERR("only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
    Packit 4a16fb
    			snd_config_delete(sconf);
    Packit 4a16fb
    			return -EINVAL;
    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_parse_control_id(control, &ctl_id, &card,
    Packit 4a16fb
    					       &cchannels, NULL);
    Packit 4a16fb
    		if (err < 0) {
    Packit 4a16fb
    			snd_pcm_close(spcm);
    Packit 4a16fb
    			return err;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
    Packit 4a16fb
    					   cchannels, min_dB, max_dB,
    Packit 4a16fb
    					   resolution, spcm, 1);
    Packit 4a16fb
    		if (err < 0)
    Packit 4a16fb
    			snd_pcm_close(spcm);
    Packit 4a16fb
    	}
    Packit 4a16fb
    	return err;
    Packit 4a16fb
    }
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
    Packit 4a16fb
    #endif