Add support for S32_3LE and byte-swapped S16/S32 to softvol

Here's a patch which adds support for S24_3LE and byte-swapped S16 and
S32 to softvol.  I've tested S24_3LE and byte-swapped S16 on powerpc
with snd-usb-audio.  All other cases are untested so far.
(Config at http://blog.blackdown.de/static/alsa/USB-Audio.conf)

Signed-off-by: Juergen Kreileder <jk@blackdown.de>
This commit is contained in:
Juergen Kreileder 2006-03-21 16:55:25 +00:00 committed by Takashi Iwai
parent 91066a98b9
commit 5ba4634ab5

View file

@ -96,10 +96,10 @@ typedef union {
int i; int i;
short s[2]; short s[2];
} val_t; } val_t;
static inline int MULTI_DIV_int(int a, unsigned short b) static inline int MULTI_DIV_int(int a, unsigned short b, int swap)
{ {
val_t v, x, y; val_t v, x, y;
v.i = a; v.i = swap ? bswap_32(a) : a;
y.i = 0; y.i = 0;
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
x.i = (unsigned int)v.s[0] * b; x.i = (unsigned int)v.s[0] * b;
@ -110,11 +110,14 @@ static inline int MULTI_DIV_int(int a, unsigned short b)
y.s[1] = x.s[0]; y.s[1] = x.s[0];
y.i += (int)v.s[0] * b; y.i += (int)v.s[0] * b;
#endif #endif
return y.i; return swap ? bswap_32(y.i) : y.i;
} }
/* (16bit x 16bit) >> 16 */ /* (16bit x 16bit) >> 16 */
#define MULTI_DIV_short(src,scale) (((int)(src) * (scale)) >> VOL_SCALE_SHIFT) #define MULTI_DIV_short(src, scale, swap) \
(swap \
? bswap_16(((short) bswap_16(src) * (scale)) >> VOL_SCALE_SHIFT) \
: (((int) (src) * (scale)) >> VOL_SCALE_SHIFT))
#endif /* DOC_HIDDEN */ #endif /* DOC_HIDDEN */
@ -125,7 +128,7 @@ static inline int MULTI_DIV_int(int a, unsigned short b)
*/ */
#ifndef DOC_HIDDEN #ifndef DOC_HIDDEN
#define CONVERT_AREA(TYPE) do { \ #define CONVERT_AREA(TYPE, swap) do { \
unsigned int ch, fr; \ unsigned int ch, fr; \
TYPE *src, *dst; \ TYPE *src, *dst; \
for (ch = 0; ch < channels; ch++) { \ for (ch = 0; ch < channels; ch++) { \
@ -150,15 +153,55 @@ static inline int MULTI_DIV_int(int a, unsigned short b)
} \ } \
} else { \ } else { \
while (fr--) { \ while (fr--) { \
*dst = MULTI_DIV_##TYPE(*src, vol_scale);\ *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
src += src_step; \ src += src_step; \
dst += dst_step; \ dst += dst_step; \
} \ } \
} \ } \
} \ } \
} while (0) } while (0)
#define CONVERT_AREA_S24_3LE() do { \
unsigned int ch, fr; \
unsigned char *src, *dst; \
int tmp; \
for (ch = 0; ch < channels; ch++) { \
src_area = &src_areas[ch]; \
dst_area = &dst_areas[ch]; \
src = snd_pcm_channel_area_addr(src_area, src_offset); \
dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
src_step = snd_pcm_channel_area_step(src_area); \
dst_step = snd_pcm_channel_area_step(dst_area); \
GET_VOL_SCALE; \
fr = frames; \
if (! vol_scale) { \
while (fr--) { \
dst[0] = dst[1] = dst[2] = 0; \
dst += dst_step; \
} \
} else if (vol_scale == 0xffff) { \
while (fr--) { \
dst[0] = src[0]; \
dst[1] = src[1]; \
dst[2] = src[2]; \
src += dst_step; \
dst += src_step; \
} \
} else { \
while (fr--) { \
tmp = src[0] | \
(src[1] << 8) | \
(((signed char *) src)[2] << 16); \
tmp = MULTI_DIV_int(tmp, vol_scale, 0); \
dst[0] = tmp; \
dst[1] = tmp >> 8; \
dst[2] = tmp >> 16; \
src += dst_step; \
dst += src_step; \
} \
} \
} \
} while (0)
#define GET_VOL_SCALE \ #define GET_VOL_SCALE \
switch (ch) { \ switch (ch) { \
@ -204,12 +247,24 @@ static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
vol[0] = svol->dB_value[svol->cur_vol[0]]; vol[0] = svol->dB_value[svol->cur_vol[0]];
vol[1] = svol->dB_value[svol->cur_vol[1]]; vol[1] = svol->dB_value[svol->cur_vol[1]];
vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2]; vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
if (svol->sformat == SND_PCM_FORMAT_S16) { switch (svol->sformat) {
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
/* 16bit samples */ /* 16bit samples */
CONVERT_AREA(short); CONVERT_AREA(short,
} else { !snd_pcm_format_cpu_endian(svol->sformat));
break;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
/* 32bit samples */ /* 32bit samples */
CONVERT_AREA(int); CONVERT_AREA(int,
!snd_pcm_format_cpu_endian(svol->sformat));
break;
case SND_PCM_FORMAT_S24_3LE:
CONVERT_AREA_S24_3LE();
break;
default:
break;
} }
} }
@ -240,12 +295,24 @@ static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
} }
vol_scale = svol->dB_value[svol->cur_vol[0]]; vol_scale = svol->dB_value[svol->cur_vol[0]];
if (svol->sformat == SND_PCM_FORMAT_S16) { switch (svol->sformat) {
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
/* 16bit samples */ /* 16bit samples */
CONVERT_AREA(short); CONVERT_AREA(short,
} else { !snd_pcm_format_cpu_endian(svol->sformat));
break;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
/* 32bit samples */ /* 32bit samples */
CONVERT_AREA(int); CONVERT_AREA(int,
!snd_pcm_format_cpu_endian(svol->sformat));
break;
case SND_PCM_FORMAT_S24_3LE:
CONVERT_AREA_S24_3LE();
break;
default:
break;
} }
} }
@ -287,14 +354,25 @@ static int snd_pcm_softvol_close(snd_pcm_t *pcm)
return 0; return 0;
} }
static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
snd_pcm_hw_params_t *params) snd_pcm_hw_params_t *params)
{ {
int err; int err;
snd_pcm_softvol_t *svol = pcm->private_data;
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
snd_pcm_format_mask_t format_mask = { snd_pcm_format_mask_t format_mask = {
{ (1U << SND_PCM_FORMAT_S16) | (1U << SND_PCM_FORMAT_S32) } {
(1ULL << SND_PCM_FORMAT_S16_LE) |
(1ULL << SND_PCM_FORMAT_S16_BE) |
(1ULL << SND_PCM_FORMAT_S32_LE) |
(1ULL << SND_PCM_FORMAT_S32_BE),
(1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
}
}; };
if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
snd_pcm_format_mask_none(&format_mask);
snd_pcm_format_mask_set(&format_mask, svol->sformat);
}
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask); &access_mask);
if (err < 0) if (err < 0)
@ -396,9 +474,13 @@ static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * param
snd_pcm_generic_hw_params); snd_pcm_generic_hw_params);
if (err < 0) if (err < 0)
return err; return err;
if (slave->format != SND_PCM_FORMAT_S16 && if (slave->format != SND_PCM_FORMAT_S16_LE &&
slave->format != SND_PCM_FORMAT_S32) { slave->format != SND_PCM_FORMAT_S16_BE &&
SNDERR("softvol supports only S16 or S32"); slave->format != SND_PCM_FORMAT_S24_3LE &&
slave->format != SND_PCM_FORMAT_S32_LE &&
slave->format != SND_PCM_FORMAT_S32_BE) {
SNDERR("softvol supports only S16_LE, S16_BE, S24_3LE, S32_LE "
" or S32_BE");
return -EINVAL; return -EINVAL;
} }
svol->sformat = slave->format; svol->sformat = slave->format;
@ -619,8 +701,11 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
int err; int err;
assert(pcmp && slave); assert(pcmp && slave);
if (sformat != SND_PCM_FORMAT_UNKNOWN && if (sformat != SND_PCM_FORMAT_UNKNOWN &&
sformat != SND_PCM_FORMAT_S16 && sformat != SND_PCM_FORMAT_S16_LE &&
sformat != SND_PCM_FORMAT_S32) sformat != SND_PCM_FORMAT_S16_BE &&
sformat != SND_PCM_FORMAT_S24_3LE &&
sformat != SND_PCM_FORMAT_S32_LE &&
sformat != SND_PCM_FORMAT_S32_BE)
return -EINVAL; return -EINVAL;
svol = calloc(1, sizeof(*svol)); svol = calloc(1, sizeof(*svol));
if (! svol) if (! svol)
@ -808,9 +893,13 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
if (err < 0) if (err < 0)
return err; return err;
if (sformat != SND_PCM_FORMAT_UNKNOWN && if (sformat != SND_PCM_FORMAT_UNKNOWN &&
sformat != SND_PCM_FORMAT_S16 && sformat != SND_PCM_FORMAT_S16_LE &&
sformat != SND_PCM_FORMAT_S32) { sformat != SND_PCM_FORMAT_S16_BE &&
SNDERR("only S16 or S32 format is supported"); sformat != SND_PCM_FORMAT_S24_3LE &&
sformat != SND_PCM_FORMAT_S32_LE &&
sformat != SND_PCM_FORMAT_S32_BE) {
SNDERR("only S16_LE, S16_BE, S24_3LE, S32_LE or S32_BE format "
"is supported");
snd_config_delete(sconf); snd_config_delete(sconf);
return -EINVAL; return -EINVAL;
} }