From d8b7e48d323f7c8cbb3fcf1dd78b538652ca88fc Mon Sep 17 00:00:00 2001 From: joq Date: Wed, 31 Dec 2003 21:28:55 +0000 Subject: [PATCH] [0.93.6] ALSA support for 24-bit cards git-svn-id: svn+ssh://jackaudio.org/trunk/jack@602 0c269be4-1314-0410-8aa9-9f06e86f4224 --- configure.in | 2 +- drivers/alsa/alsa_driver.c | 92 +++++++++++------- drivers/alsa/alsa_driver.h | 8 ++ drivers/alsa/memops.c | 190 +++++++++++++++++++++++++++++++++++++ drivers/alsa/memops.h | 7 ++ 5 files changed, 264 insertions(+), 35 deletions(-) diff --git a/configure.in b/configure.in index 63d1b8b..c350392 100644 --- a/configure.in +++ b/configure.in @@ -15,7 +15,7 @@ dnl changes are made dnl --- JACK_MAJOR_VERSION=0 JACK_MINOR_VERSION=93 -JACK_MICRO_VERSION=5 +JACK_MICRO_VERSION=6 dnl --- dnl HOWTO: updating the jack protocal version diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c index 1973af6..c8cb7ab 100644 --- a/drivers/alsa/alsa_driver.c +++ b/drivers/alsa/alsa_driver.c @@ -243,6 +243,35 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) } break; + case 3: + if (driver->playback_interleaved) { + driver->channel_copy = memcpy_interleave_d24_s24; + } else { + driver->channel_copy = memcpy_fake; + } + + switch (driver->dither) { + case Rectangular: + printf("Rectangular dithering at 16 bits\n"); + driver->write_via_copy = sample_move_dither_rect_d24u24_sS; + break; + + case Triangular: + printf("Triangular dithering at 16 bits\n"); + driver->write_via_copy = sample_move_dither_tri_d24u24_sS; + break; + + case Shaped: + printf("Noise-shaped dithering at 16 bits\n"); + driver->write_via_copy = sample_move_dither_shaped_d24u24_sS; + break; + + default: + driver->write_via_copy = sample_move_d24u24_sS; + break; + } + break; + case 4: if (driver->playback_interleaved) { driver->channel_copy = memcpy_interleave_d32_s32; @@ -280,6 +309,9 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) case 2: driver->read_via_copy = sample_move_dS_s16; break; + case 3: + driver->read_via_copy = sample_move_dS_s24u24; + break; case 4: driver->read_via_copy = sample_move_dS_s32u24; break; @@ -294,8 +326,17 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, snd_pcm_sw_params_t *sw_params, unsigned long *nchns,unsigned long sample_width) { - int err; + int err, format; snd_pcm_uframes_t stop_th; + static struct { + char Name[16]; + snd_pcm_format_t format; + } formats[] = { + {"32bit", SND_PCM_FORMAT_S32}, + {"24bit", SND_PCM_FORMAT_S24_3}, + {"16bit", SND_PCM_FORMAT_S16}, + }; +#define NOFORMATS (sizeof(formats)/sizeof(formats[0])) if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { jack_error ("ALSA: no playback configurations available (%s)", @@ -323,44 +364,25 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return -1; } } - - - if (sample_width == 4) { - - if ((err = snd_pcm_hw_params_set_format ( - handle, hw_params, SND_PCM_FORMAT_S32)) < 0) { - jack_error("Couldn't open %s for 32bit samples " - "trying 16bit instead", device_name); - if ((err = snd_pcm_hw_params_set_format ( - handle, hw_params, SND_PCM_FORMAT_S16)) - < 0) { - jack_error ("Sorry. The audio interface \"%s\"" - "doesn't support either of the two" - " hardware sample formats that " - "JACK can use.", - device_name); - return -1; - } - } - - } else { - if ((err = snd_pcm_hw_params_set_format ( - handle, hw_params, SND_PCM_FORMAT_S16)) < 0) { - jack_error("Couldn't open %s for 16bit samples trying" - " 32bit instead",device_name); - if ((err = snd_pcm_hw_params_set_format ( - handle, hw_params, SND_PCM_FORMAT_S32)) - < 0) { + format = sample_width == 4 ? 0 : NOFORMATS - 1; + while (1) { + if ((err = snd_pcm_hw_params_set_format (handle, hw_params, formats[format].format)) < 0) { + int failed_format = format; + if (sample_width == 4 ? format++ < NOFORMATS - 1 : format-- > 0) { + jack_error ("Couldn't open %s for %s samples" + " trying %s instead", device_name, formats[failed_format].Name, formats[format].Name); + } else { jack_error ("Sorry. The audio interface \"%s\"" - "doesn't support either of the two" - " hardware sample formats that " - "JACK can use.", + " doesn't support any of the" + " hardware sample formats that" + " JACK's alsa-driver can use.", device_name); return -1; } - } - } + } else + break; + } if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, driver->frame_rate, 0)) @@ -674,6 +696,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, if (driver->playback_handle) { switch (driver->playback_sample_format) { case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S24_3: case SND_PCM_FORMAT_S16_LE: case SND_PCM_FORMAT_S32_BE: case SND_PCM_FORMAT_S16_BE: @@ -689,6 +712,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, if (driver->capture_handle) { switch (driver->capture_sample_format) { case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S24_3: case SND_PCM_FORMAT_S16_LE: case SND_PCM_FORMAT_S32_BE: case SND_PCM_FORMAT_S16_BE: diff --git a/drivers/alsa/alsa_driver.h b/drivers/alsa/alsa_driver.h index ac8b504..e0f911c 100644 --- a/drivers/alsa/alsa_driver.h +++ b/drivers/alsa/alsa_driver.h @@ -25,6 +25,14 @@ #define ALSA_PCM_OLD_SW_PARAMS_API #include +#if __BYTE_ORDER == __LITTLE_ENDIAN + #define SND_PCM_FORMAT_S24_3 SND_PCM_FORMAT_S24_3LE +#elif __BYTE_ORDER == __BIG_ENDIAN + #define SND_PCM_FORMAT_S24_3 SND_PCM_FORMAT_S24_3BE +#endif + + + #include #include #include diff --git a/drivers/alsa/memops.c b/drivers/alsa/memops.c index 810821d..b3689c7 100644 --- a/drivers/alsa/memops.c +++ b/drivers/alsa/memops.c @@ -181,6 +181,163 @@ void sample_move_dither_shaped_d32u24_sS (char *dst, jack_default_audio_sample_ state->idx = idx; } +void sample_move_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) + +{ + long long y; + + while (nsamples--) { + y = (long long)(*src * SAMPLE_MAX_24BIT); + + if (y > (INT_MAX >> 8 )) { + y = (INT_MAX >> 8); + } else if (y < (INT_MIN >> 8 )) { + y = (INT_MIN >> 8 ); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy (dst, &y, 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + memcpy (dst, (char *)&y + 5, 3); +#endif + dst += dst_skip; + src++; + } +} + +void sample_move_dS_s24u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) +{ + /* ALERT: signed sign-extension portability !!! */ + + while (nsamples--) { + int x; +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy((char*)&x + 1, src, 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + memcpy(&x, src, 3); +#endif + x >>= 8; + *dst = x / SAMPLE_MAX_24BIT; + dst++; + src += src_skip; + } +} + +void sample_move_dither_rect_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) + +{ + /* ALERT: signed sign-extension portability !!! */ + jack_default_audio_sample_t x; + long long y; + + while (nsamples--) { + x = *src * SAMPLE_MAX_16BIT; + x -= (float)fast_rand() / (float)INT_MAX; + y = (long long)f_round(x); + + y <<= 8; + + if (y > (INT_MAX >> 8)) { + y = (INT_MAX >> 8); + } else if (y < (INT_MIN >> 8)) { + y = (INT_MIN >> 8); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy (dst, &y, 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + memcpy (dst, (char *)&y + 5, 3); +#endif + + dst += dst_skip; + src++; + } +} + +void sample_move_dither_tri_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) + +{ + jack_default_audio_sample_t x; + float r; + float rm1 = state->rm1; + long long y; + + while (nsamples--) { + x = *src * (float)SAMPLE_MAX_16BIT; + r = 2.0f * (float)fast_rand() / (float)INT_MAX - 1.0f; + x += r - rm1; + rm1 = r; + y = (long long)f_round(x); + + y <<= 8; + + if (y > (INT_MAX >> 8)) { + y = (INT_MAX >> 8); + } else if (y < (INT_MIN >> 8)) { + y = (INT_MIN >> 8); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy (dst, &y, 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + memcpy (dst, (char *)&y + 5, 3); +#endif + + dst += dst_skip; + src++; + } + state->rm1 = rm1; +} + +void sample_move_dither_shaped_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) + +{ + jack_default_audio_sample_t x; + jack_default_audio_sample_t xe; /* the innput sample - filtered error */ + jack_default_audio_sample_t xp; /* x' */ + float r; + float rm1 = state->rm1; + unsigned int idx = state->idx; + long long y; + + while (nsamples--) { + x = *src * (float)SAMPLE_MAX_16BIT; + r = 2.0f * (float)fast_rand() / (float)INT_MAX - 1.0f; + /* Filter the error with Lipshitz's minimally audible FIR: + [2.033 -2.165 1.959 -1.590 0.6149] */ + xe = x + - state->e[idx] * 2.033f + + state->e[(idx - 1) & DITHER_BUF_MASK] * 2.165f + - state->e[(idx - 2) & DITHER_BUF_MASK] * 1.959f + + state->e[(idx - 3) & DITHER_BUF_MASK] * 1.590f + - state->e[(idx - 4) & DITHER_BUF_MASK] * 0.6149f; + xp = xe + r - rm1; + rm1 = r; + + /* This could be some inline asm on x86 */ + y = (long long)f_round(xp); + + /* Intrinsic z^-1 delay */ + idx = (idx + 1) & DITHER_BUF_MASK; + state->e[idx] = y - xe; + + y <<= 8; + + if (y > (INT_MAX >> 8)) { + y = (INT_MAX >> 8); + } else if (y < (INT_MIN >> 8)) { + y = (INT_MIN >> 8); + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + memcpy (dst, &y, 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + memcpy (dst, (char *)&y + 5, 3); +#endif + + dst += dst_skip; + src++; + } + state->rm1 = rm1; + state->idx = idx; +} + void sample_move_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { @@ -368,6 +525,13 @@ void memset_interleave (char *dst, char val, unsigned long bytes, bytes -= 4; } break; + default: + while (bytes) { + memset(dst, val, unit_bytes); + dst += skip_bytes; + bytes -= unit_bytes; + } + break; } } @@ -434,6 +598,19 @@ merge_memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, } } +void +merge_memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, + unsigned long dst_skip_bytes, unsigned long src_skip_bytes) +{ + while (src_bytes) { + int acc = (*(int *)dst & 0xFFFFFF) + (*(int *)src & 0xFFFFFF); + memcpy(dst, &acc, 3); + dst += dst_skip_bytes; + src += src_skip_bytes; + src_bytes -= 3; + } +} + void memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes) @@ -446,6 +623,19 @@ memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, } } +void +memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, + unsigned long dst_skip_bytes, unsigned long src_skip_bytes) + +{ + while (src_bytes) { + memcpy(dst, src, 3); + dst += dst_skip_bytes; + src += src_skip_bytes; + src_bytes -= 3; + } +} + void memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes) diff --git a/drivers/alsa/memops.h b/drivers/alsa/memops.h index 1718fde..f36cc0b 100644 --- a/drivers/alsa/memops.h +++ b/drivers/alsa/memops.h @@ -42,16 +42,21 @@ typedef struct { 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_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_shaped_d32u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); +void sample_move_dither_rect_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); +void sample_move_dither_tri_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); +void sample_move_dither_shaped_d24u24_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_rect_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); void sample_move_dither_tri_d16_sS (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_s32u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); +void sample_move_dS_s24u24 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_move_dS_s16 (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); void sample_merge_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); @@ -79,9 +84,11 @@ void memset_interleave (char *dst, char val, unsigned long bytes, void memcpy_fake (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar); void memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); +void memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); +void merge_memcpy_interleave_d24_s24 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes); void merge_memcpy_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar);