From: Juergen Kreileder Date: Tue, 21 Mar 2006 16:55:25 +0000 (+0000) Subject: Add support for S32_3LE and byte-swapped S16/S32 to softvol X-Git-Tag: v1.0.11rc4~3 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=5ba4634ab5543e86131c5a9f32087d9f5538465a;p=alsa-lib.git 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 --- diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index d59d0315..588d347d 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -96,10 +96,10 @@ typedef union { int i; short s[2]; } 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; - v.i = a; + v.i = swap ? bswap_32(a) : a; y.i = 0; #if __BYTE_ORDER == __LITTLE_ENDIAN 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.i += (int)v.s[0] * b; #endif - return y.i; + return swap ? bswap_32(y.i) : y.i; } /* (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 */ @@ -125,7 +128,7 @@ static inline int MULTI_DIV_int(int a, unsigned short b) */ #ifndef DOC_HIDDEN -#define CONVERT_AREA(TYPE) do { \ +#define CONVERT_AREA(TYPE, swap) do { \ unsigned int ch, fr; \ TYPE *src, *dst; \ for (ch = 0; ch < channels; ch++) { \ @@ -150,15 +153,55 @@ static inline int MULTI_DIV_int(int a, unsigned short b) } \ } else { \ while (fr--) { \ - *dst = MULTI_DIV_##TYPE(*src, vol_scale);\ + *dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \ src += src_step; \ dst += dst_step; \ } \ } \ } \ } 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 \ 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[1] = svol->dB_value[svol->cur_vol[1]]; 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 */ - CONVERT_AREA(short); - } else { + CONVERT_AREA(short, + !snd_pcm_format_cpu_endian(svol->sformat)); + break; + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: /* 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]]; - if (svol->sformat == SND_PCM_FORMAT_S16) { + switch (svol->sformat) { + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: /* 16bit samples */ - CONVERT_AREA(short); - } else { + CONVERT_AREA(short, + !snd_pcm_format_cpu_endian(svol->sformat)); + break; + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: /* 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; } -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) { int err; + snd_pcm_softvol_t *svol = pcm->private_data; snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 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, &access_mask); 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); if (err < 0) return err; - if (slave->format != SND_PCM_FORMAT_S16 && - slave->format != SND_PCM_FORMAT_S32) { - SNDERR("softvol supports only S16 or S32"); + if (slave->format != SND_PCM_FORMAT_S16_LE && + slave->format != SND_PCM_FORMAT_S16_BE && + 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; } svol->sformat = slave->format; @@ -619,8 +701,11 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, int err; assert(pcmp && slave); if (sformat != SND_PCM_FORMAT_UNKNOWN && - sformat != SND_PCM_FORMAT_S16 && - sformat != SND_PCM_FORMAT_S32) + sformat != SND_PCM_FORMAT_S16_LE && + 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; svol = calloc(1, sizeof(*svol)); if (! svol) @@ -808,9 +893,13 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, if (err < 0) return err; if (sformat != SND_PCM_FORMAT_UNKNOWN && - sformat != SND_PCM_FORMAT_S16 && - sformat != SND_PCM_FORMAT_S32) { - SNDERR("only S16 or S32 format is supported"); + sformat != SND_PCM_FORMAT_S16_LE && + sformat != SND_PCM_FORMAT_S16_BE && + 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); return -EINVAL; }