Browse Source

Add 32 bit support for ALSA driver (#811)

* apply changes without whitespace

* remove neon intrinsics and fix indentation

* update float_32 macro and fix misspellings

* check msbits to determine number of bits in alsa driver

* add better error messages and support for SND_PCM_FORMAT_S32_BE

* log when sample format is not equal to bits

Co-authored-by: Claudio Cabral <clca@bang-olufsen.dk>
Co-authored-by: Claudio Cabral <cl@udio.co>
tags/v1.9.20
Claudio Cabral falkTX <falktx@falktx.com> 5 months ago
parent
commit
719fa1d545
3 changed files with 171 additions and 4 deletions
  1. +97
    -4
      common/memops.c
  2. +4
    -0
      common/memops.h
  3. +70
    -0
      linux/alsa/alsa_driver.c

+ 97
- 4
common/memops.c View File

@@ -73,6 +73,7 @@
So, for now (October 2008) we use 2^(N-1)-1 as the scaling factor.
*/

#define SAMPLE_32BIT_SCALING 2147483647.0
#define SAMPLE_24BIT_SCALING 8388607.0f
#define SAMPLE_16BIT_SCALING 32767.0f

@@ -81,6 +82,11 @@
advice from Fons Adriaensen: make the limits symmetrical
*/

#define SAMPLE_32BIT_MAX 2147483647
#define SAMPLE_32BIT_MIN -2147483647
#define SAMPLE_32BIT_MAX_D 2147483647.0
#define SAMPLE_32BIT_MIN_D -2147483647.0

#define SAMPLE_24BIT_MAX 8388607
#define SAMPLE_24BIT_MIN -8388607
#define SAMPLE_24BIT_MAX_F 8388607.0f
@@ -106,6 +112,7 @@
*/

#define f_round(f) lrintf(f)
#define d_round(f) lrint(f)

#define float_16(s, d)\
if ((s) <= NORMALIZED_FLOAT_MIN) {\
@@ -146,6 +153,15 @@
(d) = f_round ((s) * SAMPLE_24BIT_SCALING); \
}

#define float_32(s, d) \
do { \
double clipped = fmin(NORMALIZED_FLOAT_MAX, \
fmax((double)(s), NORMALIZED_FLOAT_MIN)); \
double scaled = clipped * SAMPLE_32BIT_MAX_D; \
(d) = d_round(scaled); \
} \
while (0)

/* call this when "s" has already been scaled (e.g. when dithering)
*/

@@ -195,6 +211,11 @@ static inline __m128 clip(__m128 s, __m128 min, __m128 max)
return _mm_min_ps(max, _mm_max_ps(s, min));
}

static inline __m128d clip_double(__m128d s, __m128d min, __m128d max)
{
return _mm_min_pd(max, _mm_max_pd(s, min));
}

static inline __m128i float_24_sse(__m128 s)
{
const __m128 upper_bound = gen_one(); /* NORMALIZED_FLOAT_MAX */
@@ -274,13 +295,14 @@ void sample_move_dS_floatLE (char *dst, jack_default_audio_sample_t *src, unsign
S - sample is a jack_default_audio_sample_t, currently (October 2008) a 32 bit floating point value
Ss - like S but reverse endian from the host CPU
32u24 - sample is an signed 32 bit integer value, but data is in upper 24 bits only
32 - sample is a signed 32 bit integer value
32u24 - sample is a signed 32 bit integer value, but data is in upper 24 bits only
32u24s - like 32u24 but reverse endian from the host CPU
32l24 - sample is an signed 32 bit integer value, but data is in lower 24 bits only
32l24 - sample is a signed 32 bit integer value, but data is in lower 24 bits only
32l24s - like 32l24 but reverse endian from the host CPU
24 - sample is an signed 24 bit integer value
24 - sample is a signed 24 bit integer value
24s - like 24 but reverse endian from the host CPU
16 - sample is an signed 16 bit integer value
16 - sample is a signed 16 bit integer value
16s - like 16 but reverse endian from the host CPU

For obvious reasons, the reverse endian versions only show as source types.
@@ -290,6 +312,36 @@ void sample_move_dS_floatLE (char *dst, jack_default_audio_sample_t *src, unsign

/* functions for native integer sample data */

void sample_move_d32_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state)
{
while (nsamples--) {
int32_t z;
float_32(*src, z);
#if __BYTE_ORDER == __LITTLE_ENDIAN
dst[0]=(char)(z>>24);
dst[1]=(char)(z>>16);
dst[2]=(char)(z>>8);
dst[3]=(char)(z);
#elif __BYTE_ORDER == __BIG_ENDIAN
dst[0]=(char)(z);
dst[1]=(char)(z>>8);
dst[2]=(char)(z>>16);
dst[3]=(char)(z>>24);
#endif
dst += dst_skip;
src++;
}
}

void sample_move_d32_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state)
{
while (nsamples--) {
float_32(*src, *(int32_t *)dst);
dst += dst_skip;
src++;
}
}

void sample_move_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state)
{
#if defined (__ARM_NEON__) || defined (__ARM_NEON)
@@ -689,6 +741,35 @@ void sample_move_d32l24_sS (char *dst, jack_default_audio_sample_t *src, unsigne
#endif
}

void sample_move_dS_s32s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
{
const jack_default_audio_sample_t scaling = 1.0/SAMPLE_32BIT_SCALING;
while (nsamples--) {
int32_t x;
#if __BYTE_ORDER == __LITTLE_ENDIAN
x = (unsigned char)(src[0]);
x <<= 8;
x |= (unsigned char)(src[1]);
x <<= 8;
x |= (unsigned char)(src[2]);
x <<= 8;
x |= (unsigned char)(src[3]);
#elif __BYTE_ORDER == __BIG_ENDIAN
x = (unsigned char)(src[3]);
x <<= 8;
x |= (unsigned char)(src[2]);
x <<= 8;
x |= (unsigned char)(src[1]);
x <<= 8;
x |= (unsigned char)(src[0]);
#endif
double extended = x * scaling;
*dst = (float)extended;
dst++;
src += src_skip;
}
}

void sample_move_dS_s32l24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
{
#if defined (__ARM_NEON__) || defined (__ARM_NEON)
@@ -753,6 +834,18 @@ void sample_move_dS_s32l24s (jack_default_audio_sample_t *dst, char *src, unsign
}
}

void sample_move_dS_s32 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
{
const double scaling = 1.0 / SAMPLE_32BIT_SCALING;
while (nsamples--) {
int32_t val=(*((int32_t*)src));
double extended = val * scaling;
*dst = (float)extended;
dst++;
src += src_skip;
}
}

void sample_move_dS_s32l24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
{
#if defined (__SSE2__) && !defined (__sun__)


+ 4
- 0
common/memops.h View File

@@ -53,6 +53,8 @@ void sample_move_floatLE_sSs (jack_default_audio_sample_t *dst, char *src, unsig
void sample_move_dS_floatLE (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);

/* integer functions */
void sample_move_d32_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void sample_move_d32_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void sample_move_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void sample_move_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void sample_move_d32l24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
@@ -81,6 +83,8 @@ void sample_move_dither_tri_d16_sS (char *dst, jack_default_audio_sample_
void sample_move_dither_shaped_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);
void sample_move_dither_shaped_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state);

void sample_move_dS_s32s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
void sample_move_dS_s32 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
void sample_move_dS_s32u24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
void sample_move_dS_s32u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
void sample_move_dS_s32l24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);


+ 70
- 0
linux/alsa/alsa_driver.c View File

@@ -314,10 +314,45 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver)
break;

case 4: /* NO DITHER */
switch (driver->playback_sample_format)
{
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
driver->write_via_copy = driver->quirk_bswap?
sample_move_d32l24_sSs:
sample_move_d32l24_sS;
break;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
{
int bits = snd_pcm_hw_params_get_sbits(driver->playback_hw_params);
if (bits == 32)
{
driver->write_via_copy = driver->quirk_bswap?
sample_move_d32_sSs:
sample_move_d32_sS;
}
else if (bits == 24)
{
jack_log("sample format is SND_PCM_FORMAT_S32 but only 24 bits available");
driver->write_via_copy = driver->quirk_bswap?
sample_move_d32u24_sSs:
sample_move_d32u24_sS;
}
else
{
jack_error("unsupported sample format for playback: "
"SND_PCM_FORMAT_S32 with %d bits",
bits);
exit (1);
}
break;
}
default:
jack_error("unsupported 4 byte sample_format");
exit (1);
}
break;

default:
jack_error ("impossible sample width (%d) discovered!",
@@ -343,10 +378,45 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver)
sample_move_dS_s24;
break;
case 4:
switch (driver->capture_sample_format)
{
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
driver->read_via_copy = driver->quirk_bswap?
sample_move_dS_s32l24s:
sample_move_dS_s32l24;
break;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
{
int bits = snd_pcm_hw_params_get_sbits(driver->capture_hw_params);
if (bits == 32)
{
driver->read_via_copy = driver->quirk_bswap?
sample_move_dS_s32s:
sample_move_dS_s32;
}
else if(bits == 24)
{
jack_log("sample format is SND_PCM_FORMAT_S32 but only 24 bits available");
driver->read_via_copy = driver->quirk_bswap?
sample_move_dS_s32u24s:
sample_move_dS_s32u24;
}
else
{
jack_error("unsupported sample format for capture: "
"SND_PCM_FORMAT_S32 with %d bits",
bits);
exit (1);
}
break;
}
default:
jack_error("unsupported 4 byte sample_format");
exit (1);
}
break;
}
}
}


Loading…
Cancel
Save