diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index f6558e687..c9598adc5 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -49,7 +49,6 @@ #include "fmt-ops.h" #include "channelmix-ops.h" #include "resample.h" -#include "dither-ops.h" #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT log_topic @@ -210,7 +209,6 @@ struct impl { struct channelmix mix; struct resample resample; struct volume volume; - struct dither dither; double rate_scale; uint32_t in_offset; @@ -634,7 +632,7 @@ static int impl_node_enum_params(void *object, int seq, SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_name, SPA_POD_String("dither.noise"), SPA_PROP_INFO_description, SPA_POD_String("Add dithering noise"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dither.noise, 0, 16), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise, 0, 16), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; default: @@ -704,7 +702,7 @@ static int impl_node_enum_params(void *object, int seq, spa_pod_builder_string(&b, "resample.disable"); spa_pod_builder_bool(&b, p->resample_disabled); spa_pod_builder_string(&b, "dither.noise"); - spa_pod_builder_int(&b, this->dither.noise); + spa_pod_builder_int(&b, this->dir[1].conv.noise); spa_pod_builder_pop(&b, &f[1]); param = spa_pod_builder_pop(&b, &f[0]); break; @@ -775,7 +773,7 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char * else if (spa_streq(k, "resample.disable")) this->props.resample_disabled = spa_atob(s); else if (spa_streq(k, "dither.noise")) - spa_atou32(s, &this->dither.noise, 0); + spa_atou32(s, &this->dir[1].conv.noise, 0); else return 0; return 1; @@ -1408,6 +1406,7 @@ static int setup_out_convert(struct impl *this) break; } } + out->conv.quantize = calc_width(&dst_info) * 8; out->conv.src_fmt = src_info.info.raw.format; out->conv.dst_fmt = dst_info.info.raw.format; out->conv.n_channels = dst_info.info.raw.channels; @@ -1416,29 +1415,11 @@ static int setup_out_convert(struct impl *this) if ((res = convert_init(&out->conv)) < 0) return res; - spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d", this, - this->cpu_flags, out->conv.cpu_flags, out->conv.is_passthrough); + spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d passthrough:%d %s", this, + this->cpu_flags, out->conv.cpu_flags, + out->conv.quantize, out->conv.noise, + out->conv.is_passthrough, out->conv.func_name); - - return 0; -} - -static int setup_dither(struct impl *this) -{ - struct dir *out = &this->dir[SPA_DIRECTION_OUTPUT]; - int res; - - this->dither.quantize = calc_width(&out->format) * 8; - this->dither.n_channels = out->format.info.raw.channels; - this->dither.cpu_flags = this->cpu_flags; - - if ((res = dither_init(&this->dither)) < 0) - return res; - - spa_log_info(this->log, "%p: got dither %08x:%08x quantize:%d:%d passthrough:%d", this, - this->cpu_flags, this->dither.cpu_flags, - this->dither.quantize, this->dither.noise, - this->dither.is_passthrough); return 0; } @@ -1484,8 +1465,6 @@ static int setup_convert(struct impl *this) return res; if ((res = setup_out_convert(this)) < 0) return res; - if ((res = setup_dither(this)) < 0) - return res; for (i = 0; i < MAX_PORTS; i++) { this->tmp_datas[0][i] = SPA_PTROFF(this->tmp, this->empty_size * i, void); @@ -2512,15 +2491,6 @@ static int impl_node_process(void *object) } this->out_offset += n_samples; - if (!this->dither.is_passthrough) { - in_datas = (const void**)out_datas; - if (out_passthrough) - out_datas = dst_datas; - else - out_datas = (void **)this->tmp_datas[(tmp++) & 1]; - dither_process(&this->dither, out_datas, in_datas, n_samples); - in_empty = false; - } if (!out_passthrough) { in_datas = (const void**)out_datas; spa_log_trace_fp(this->log, "%p: convert %d", this, n_samples); @@ -2652,8 +2622,6 @@ static int impl_clear(struct spa_handle *handle) if (this->resample.free) resample_free(&this->resample); - if (this->dither.free) - dither_free(&this->dither); return 0; } @@ -2702,8 +2670,6 @@ impl_init(const struct spa_handle_factory *factory, this->mix.rear_delay = 12.0f; this->mix.widen = 0.0f; - this->dither.log = this->log; - for (i = 0; info && i < info->n_items; i++) { const char *k = info->items[i].key; const char *s = info->items[i].value; diff --git a/spa/plugins/audioconvert/dither-ops-c.c b/spa/plugins/audioconvert/dither-ops-c.c deleted file mode 100644 index 03f5fd941..000000000 --- a/spa/plugins/audioconvert/dither-ops-c.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Spa - * - * Copyright © 2022 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "dither-ops.h" - - -/* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */ -static inline uint32_t -xorshift(uint32_t *state) -{ - uint32_t x = *state; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - return (*state = x); -} - -static inline void update_dither_c(struct dither *dt, uint32_t n_samples) -{ - uint32_t n; - for (n = 0; n < n_samples; n++) - dt->dither[n] = ((int32_t)xorshift(&dt->random[0])) * dt->scale; -} - -void dither_f32_c(struct dither *dt, void * SPA_RESTRICT dst[], - const void * SPA_RESTRICT src[], uint32_t n_samples) -{ - uint32_t i, n, m, chunk; - const float **s = (const float**)src; - float **d = (float**)dst; - float *dither = dt->dither; - - chunk = SPA_MIN(n_samples, dt->dither_size); - update_dither_c(dt, chunk); - - for (n = 0; n < n_samples; n += chunk) { - chunk = SPA_MIN(n_samples - n, dt->dither_size); - - for (i = 0; i < dt->n_channels; i++) { - float *di = &d[i][n]; - const float *si = &s[i][n]; - - for (m = 0; m < chunk; m++) - di[m] = si[m] + dither[m]; - } - } -} diff --git a/spa/plugins/audioconvert/dither-ops-sse2.c b/spa/plugins/audioconvert/dither-ops-sse2.c deleted file mode 100644 index 8e58a35d6..000000000 --- a/spa/plugins/audioconvert/dither-ops-sse2.c +++ /dev/null @@ -1,97 +0,0 @@ -/* Spa - * - * Copyright © 2022 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "dither-ops.h" - -#include - -static inline void update_dither_sse2(struct dither *dt, uint32_t n_samples) -{ - uint32_t n; - const uint32_t *r = SPA_PTR_ALIGN(dt->random, 16, uint32_t); - float *dither = SPA_PTR_ALIGN(dt->dither, 16, float); - __m128 scale = _mm_set1_ps(dt->scale), out[1]; - __m128i in[1], t[1]; - - for (n = 0; n < n_samples; n += 4) { - /* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */ - in[0] = _mm_load_si128((__m128i*)r); - t[0] = _mm_slli_epi32(in[0], 13); - in[0] = _mm_xor_si128(in[0], t[0]); - t[0] = _mm_srli_epi32(in[0], 17); - in[0] = _mm_xor_si128(in[0], t[0]); - t[0] = _mm_slli_epi32(in[0], 5); - in[0] = _mm_xor_si128(in[0], t[0]); - _mm_store_si128((__m128i*)r, in[0]); - - out[0] = _mm_cvtepi32_ps(in[0]); - out[0] = _mm_mul_ps(out[0], scale); - _mm_store_ps(&dither[n], out[0]); - } -} - -void dither_f32_sse2(struct dither *dt, void * SPA_RESTRICT dst[], - const void * SPA_RESTRICT src[], uint32_t n_samples) -{ - uint32_t i, n, m, chunk, unrolled; - const float **s = (const float**)src; - float **d = (float**)dst; - float *dither = SPA_PTR_ALIGN(dt->dither, 16, float); - __m128 in[4]; - - chunk = SPA_MIN(n_samples, dt->dither_size); - update_dither_sse2(dt, chunk); - - for (n = 0; n < n_samples; n += chunk) { - chunk = SPA_MIN(n_samples - n, dt->dither_size); - - for (i = 0; i < dt->n_channels; i++) { - float *di = &d[i][n]; - const float *si = &s[i][n]; - - if (SPA_IS_ALIGNED(di, 16) && - SPA_IS_ALIGNED(si, 16)) - unrolled = chunk & ~15; - else - unrolled = 0; - - for (m = 0; m < unrolled; m += 16) { - in[0] = _mm_load_ps(&si[m ]); - in[1] = _mm_load_ps(&si[m + 4]); - in[2] = _mm_load_ps(&si[m + 8]); - in[3] = _mm_load_ps(&si[m + 12]); - in[0] = _mm_add_ps(in[0], _mm_load_ps(&dither[m ])); - in[1] = _mm_add_ps(in[1], _mm_load_ps(&dither[m + 4])); - in[2] = _mm_add_ps(in[2], _mm_load_ps(&dither[m + 8])); - in[3] = _mm_add_ps(in[3], _mm_load_ps(&dither[m + 12])); - _mm_store_ps(&di[m ], in[0]); - _mm_store_ps(&di[m + 4], in[1]); - _mm_store_ps(&di[m + 8], in[2]); - _mm_store_ps(&di[m + 12], in[3]); - } - for (; m < chunk; m++) - di[m] = si[m] + dither[m]; - } - } -} diff --git a/spa/plugins/audioconvert/dither-ops.c b/spa/plugins/audioconvert/dither-ops.c deleted file mode 100644 index 1ff6428e0..000000000 --- a/spa/plugins/audioconvert/dither-ops.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Spa - * - * Copyright © 2022 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "dither-ops.h" - -#define DITHER_SIZE (1<<10) - -typedef void (*dither_func_t) (struct dither *d, void * SPA_RESTRICT dst[], - const void * SPA_RESTRICT src[], uint32_t n_samples); - -static const struct dither_info { - dither_func_t process; - uint32_t cpu_flags; -} dither_table[] = -{ -#if defined (HAVE_SSE2) - { dither_f32_sse2, SPA_CPU_FLAG_SSE, }, -#endif - { dither_f32_c, 0 }, -}; - -#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a) - -static const struct dither_info *find_dither_info(uint32_t cpu_flags) -{ - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(dither_table); i++) { - if (!MATCH_CPU_FLAGS(dither_table[i].cpu_flags, cpu_flags)) - continue; - return &dither_table[i]; - } - return NULL; -} - -static void impl_dither_free(struct dither *d) -{ - d->process = NULL; - free(d->dither); - d->dither = NULL; -} - -int dither_init(struct dither *d) -{ - const struct dither_info *info; - size_t i; - uint32_t scale; - - info = find_dither_info(d->cpu_flags); - if (info == NULL) - return -ENOTSUP; - - scale = d->quantize; - scale -= SPA_MIN(scale, d->noise); - - d->scale = 1.0f / powf(2.0f, 31 + scale); - - d->dither_size = DITHER_SIZE; - d->dither = calloc(d->dither_size + DITHER_OPS_MAX_OVERREAD + - DITHER_OPS_MAX_ALIGN / sizeof(float), sizeof(float)); - if (d->dither == NULL) - return -errno; - - for (i = 0; i < SPA_N_ELEMENTS(d->random); i++) - d->random[i] = random(); - - d->cpu_flags = info->cpu_flags; - - d->free = impl_dither_free; - d->process = info->process; - - d->is_passthrough = d->noise == 0 && d->method == DITHER_METHOD_NONE; - - return 0; -} diff --git a/spa/plugins/audioconvert/dither-ops.h b/spa/plugins/audioconvert/dither-ops.h deleted file mode 100644 index 780d191f0..000000000 --- a/spa/plugins/audioconvert/dither-ops.h +++ /dev/null @@ -1,96 +0,0 @@ -/* Spa - * - * Copyright © 2022 Wim Taymans - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include -#include -#include - -#define DITHER_OPS_MAX_ALIGN 16 -#define DITHER_OPS_MAX_OVERREAD 16 - -struct dither { - uint32_t quantize; - uint32_t noise; -#define DITHER_METHOD_NONE 0 -#define DITHER_METHOD_RECTANGULAR 2 -#define DITHER_METHOD_TRIANGULAR 3 -#define DITHER_METHOD_SHAPED_5 4 - uint32_t method; - uint32_t n_channels; - uint32_t cpu_flags; - unsigned int is_passthrough:1; - - struct spa_log *log; - - void (*process) (struct dither *d, void * SPA_RESTRICT dst[], - const void * SPA_RESTRICT src[], uint32_t n_samples); - void (*free) (struct dither *d); - - uint32_t random[16 + DITHER_OPS_MAX_ALIGN/sizeof(uint32_t)]; - float *dither; - uint32_t dither_size; - float scale; -}; - -int dither_init(struct dither *d); - -static const struct dither_method_info { - const char *label; - const char *description; - uint32_t method; -} dither_method_info[] = { - [DITHER_METHOD_NONE] = { "none", "Disabled", DITHER_METHOD_NONE }, - [DITHER_METHOD_RECTANGULAR] = { "rectangular", "Rectangular dithering", DITHER_METHOD_RECTANGULAR }, - [DITHER_METHOD_TRIANGULAR] = { "triangular", "Triangular dithering", DITHER_METHOD_TRIANGULAR }, - [DITHER_METHOD_SHAPED_5] = { "shaped5", "Shaped 5 dithering", DITHER_METHOD_SHAPED_5 } -}; - -static inline uint32_t dither_method_from_label(const char *label) -{ - uint32_t i; - for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) { - if (spa_streq(dither_method_info[i].label, label)) - return dither_method_info[i].method; - } - return DITHER_METHOD_NONE; -} - -#define dither_process(d,...) (d)->process(d, __VA_ARGS__) -#define dither_free(d) (d)->free(d) - -#define DEFINE_FUNCTION(name,arch) \ -void dither_##name##_##arch(struct dither *d, \ - void * SPA_RESTRICT dst[], \ - const void * SPA_RESTRICT src[], \ - uint32_t n_samples); - -DEFINE_FUNCTION(f32, c); -#if defined(HAVE_SSE2) -DEFINE_FUNCTION(f32, sse2); -#endif - -#undef DEFINE_FUNCTION diff --git a/spa/plugins/audioconvert/fmt-ops-c.c b/spa/plugins/audioconvert/fmt-ops-c.c index 95dfec7c2..3ea58279d 100644 --- a/spa/plugins/audioconvert/fmt-ops-c.c +++ b/spa/plugins/audioconvert/fmt-ops-c.c @@ -736,6 +736,26 @@ conv_f64d_to_f32_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * } } +/* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */ +static inline uint32_t +xorshift(uint32_t *state) +{ + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return (*state = x); +} + +static inline void update_dither_c(struct convert *conv, uint32_t n_samples) +{ + uint32_t n, mask = conv->mask; + int32_t offset = conv->offset + conv->bias; + + for (n = 0; n < n_samples; n++) + conv->dither[n] = offset + (int32_t)(xorshift(&conv->random[0]) & mask); +} + void conv_f32d_to_u8d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -920,6 +940,29 @@ conv_f32d_to_s16d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s16d_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (i = 0; i < n_channels; i++) { + const float *s = src[i]; + int16_t *d = dst[i]; + int32_t v; + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + v = F32_TO_S32(s[j]) + conv->dither[k]; + d[j] = v >> 16; + } + } + } +} + void conv_f32_to_s16_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -962,6 +1005,28 @@ conv_f32d_to_s16_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * } } +void +conv_f32d_to_s16_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int16_t *d = dst[0]; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + *d++ = v >> 16; + } + } + } +} + void conv_f32d_to_s16s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -976,6 +1041,28 @@ conv_f32d_to_s16s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s16s_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int16_t *d = dst[0]; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + *d++ = bswap_16(v >> 16); + } + } + } +} + void conv_f32_to_u32_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1019,6 +1106,26 @@ conv_f32d_to_s32d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s32d_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (i = 0; i < n_channels; i++) { + const float *s = src[i]; + int32_t *d = dst[i]; + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) + d[j] = F32_TO_S32(s[j]) + conv->dither[k]; + } + } +} + void conv_f32_to_s32_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1061,6 +1168,25 @@ conv_f32d_to_s32_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * } } +void +conv_f32d_to_s32_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int32_t *d = dst[0]; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) + *d++ = F32_TO_S32(s[i][j]) + conv->dither[k]; + } + } +} + void conv_f32d_to_s32s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1075,6 +1201,27 @@ conv_f32d_to_s32s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s32s_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int32_t *d = dst[0], v; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + *d++ = bswap_32(v); + } + } + } +} + void conv_f32d_to_f64d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1195,6 +1342,30 @@ conv_f32d_to_s24d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s24d_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (i = 0; i < n_channels; i++) { + const float *s = src[i]; + uint8_t *d = dst[i]; + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + v = F32_TO_S32(s[j]) + conv->dither[k]; + write_s24(d, v >> 8); + d += 3; + } + } + } +} + void conv_f32_to_s24_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1242,6 +1413,30 @@ conv_f32d_to_s24_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * } } +void +conv_f32d_to_s24_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + uint8_t *d = dst[0]; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + write_s24(d, v >> 8); + d += 3; + } + } + } +} + + void conv_f32d_to_s24s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1258,6 +1453,28 @@ conv_f32d_to_s24s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void } } +void +conv_f32d_to_s24s_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + uint8_t *d = dst[0]; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + write_s24s(d, v >> 8); + d += 3; + } + } + } +} void conv_f32d_to_s24_32d_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], @@ -1274,6 +1491,29 @@ conv_f32d_to_s24_32d_c(struct convert *conv, void * SPA_RESTRICT dst[], const vo } } +void +conv_f32d_to_s24_32d_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + int32_t v; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (i = 0; i < n_channels; i++) { + const float *s = src[i]; + int32_t *d = dst[i]; + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + v = F32_TO_S32(s[j]) + conv->dither[k]; + d[j] = v >> 8; + } + } + } +} + void conv_f32_to_u24_32_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1344,6 +1584,27 @@ conv_f32d_to_s24_32_c(struct convert *conv, void * SPA_RESTRICT dst[], const voi } } +void +conv_f32d_to_s24_32_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int32_t *d = dst[0], v; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + *d++ = v >> 8; + } + } + } +} + void conv_f32d_to_s24_32s_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) @@ -1358,6 +1619,27 @@ conv_f32d_to_s24_32s_c(struct convert *conv, void * SPA_RESTRICT dst[], const vo } } +void +conv_f32d_to_s24_32s_dither_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], + uint32_t n_samples) +{ + const float **s = (const float **) src; + int32_t *d = dst[0], v; + uint32_t i, j, k, chunk, n_channels = conv->n_channels; + + update_dither_c(conv, SPA_MIN(n_samples, conv->dither_size)); + + for (j = 0; j < n_samples;) { + chunk = SPA_MIN(n_samples - j, conv->dither_size); + for (k = 0; k < chunk; k++, j++) { + for (i = 0; i < n_channels; i++) { + v = F32_TO_S32(s[i][j]) + conv->dither[k]; + *d++ = bswap_32(v >> 8); + } + } + } +} + void conv_deinterleave_8_c(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples) diff --git a/spa/plugins/audioconvert/fmt-ops.c b/spa/plugins/audioconvert/fmt-ops.c index 617793971..f9bb915b1 100644 --- a/spa/plugins/audioconvert/fmt-ops.c +++ b/spa/plugins/audioconvert/fmt-ops.c @@ -32,6 +32,8 @@ #include "fmt-ops.h" +#define DITHER_SIZE (1<<10) + typedef void (*convert_func_t) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], uint32_t n_samples); @@ -39,277 +41,301 @@ struct conv_info { uint32_t src_fmt; uint32_t dst_fmt; uint32_t n_channels; - uint32_t cpu_flags; convert_func_t process; + const char *name; + + uint32_t cpu_flags; +#define CONV_DITHER (1<<0) +#define CONV_SHAPE (1<<1) + uint32_t dither_flags; }; +#define MAKE(fmt1,fmt2,chan,func,...) \ + { SPA_AUDIO_FORMAT_ ##fmt1, SPA_AUDIO_FORMAT_ ##fmt2, chan, func, #func , __VA_ARGS__ } + static struct conv_info conv_table[] = { /* to f32 */ - { SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u8_to_f32_c }, - { SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u8d_to_f32d_c }, - { SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u8_to_f32d_c }, - { SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u8d_to_f32_c }, + MAKE(U8, F32, 0, conv_u8_to_f32_c), + MAKE(U8, F32, 0, conv_u8_to_f32_c), + MAKE(U8P, F32P, 0, conv_u8d_to_f32d_c), + MAKE(U8, F32P, 0, conv_u8_to_f32d_c), + MAKE(U8P, F32, 0, conv_u8d_to_f32_c), - { SPA_AUDIO_FORMAT_S8, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s8_to_f32_c }, - { SPA_AUDIO_FORMAT_S8P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s8d_to_f32d_c }, - { SPA_AUDIO_FORMAT_S8, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s8_to_f32d_c }, - { SPA_AUDIO_FORMAT_S8P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s8d_to_f32_c }, + MAKE(S8, F32, 0, conv_s8_to_f32_c), + MAKE(S8P, F32P, 0, conv_s8d_to_f32d_c), + MAKE(S8, F32P, 0, conv_s8_to_f32d_c), + MAKE(S8P, F32, 0, conv_s8d_to_f32_c), - { SPA_AUDIO_FORMAT_ALAW, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_alaw_to_f32d_c }, - { SPA_AUDIO_FORMAT_ULAW, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_ulaw_to_f32d_c }, + MAKE(ALAW, F32P, 0, conv_alaw_to_f32d_c), + MAKE(ULAW, F32P, 0, conv_ulaw_to_f32d_c), - { SPA_AUDIO_FORMAT_U16, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u16_to_f32_c }, - { SPA_AUDIO_FORMAT_U16, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u16_to_f32d_c }, + MAKE(U16, F32, 0, conv_u16_to_f32_c), + MAKE(U16, F32P, 0, conv_u16_to_f32d_c), - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s16_to_f32_c }, - { SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s16d_to_f32d_c }, + MAKE(S16, F32, 0, conv_s16_to_f32_c), + MAKE(S16P, F32P, 0, conv_s16d_to_f32d_c), #if defined (HAVE_NEON) - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 2, SPA_CPU_FLAG_NEON, conv_s16_to_f32d_2_neon }, - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_NEON, conv_s16_to_f32d_neon }, + MAKE(S16, F32P, 2, conv_s16_to_f32d_2_neon, SPA_CPU_FLAG_NEON), + MAKE(S16, F32P, 0, conv_s16_to_f32d_neon, SPA_CPU_FLAG_NEON), #endif #if defined (HAVE_AVX2) - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 2, SPA_CPU_FLAG_AVX2, conv_s16_to_f32d_2_avx2 }, - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX2, conv_s16_to_f32d_avx2 }, + MAKE(S16, F32P, 2, conv_s16_to_f32d_2_avx2, SPA_CPU_FLAG_AVX2), + MAKE(S16, F32P, 0, conv_s16_to_f32d_avx2, SPA_CPU_FLAG_AVX2), #endif #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 2, SPA_CPU_FLAG_SSE2, conv_s16_to_f32d_2_sse2 }, - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSE2, conv_s16_to_f32d_sse2 }, + MAKE(S16, F32P, 2, conv_s16_to_f32d_2_sse2, SPA_CPU_FLAG_SSE2), + MAKE(S16, F32P, 0, conv_s16_to_f32d_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s16_to_f32d_c }, - { SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s16d_to_f32_c }, + MAKE(S16, F32P, 0, conv_s16_to_f32d_c), + MAKE(S16P, F32, 0, conv_s16d_to_f32_c), - { SPA_AUDIO_FORMAT_S16_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s16s_to_f32d_c }, + MAKE(S16_OE, F32P, 0, conv_s16s_to_f32d_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_copy32_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_copy32d_c }, + MAKE(F32, F32, 0, conv_copy32_c), + MAKE(F32P, F32P, 0, conv_copy32d_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_sse2 }, + MAKE(F32, F32P, 0, conv_deinterleave_32_sse2), #endif - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32_c }, + MAKE(F32, F32P, 0, conv_deinterleave_32_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_sse2 }, + MAKE(F32P, F32, 0, conv_interleave_32_sse2), #endif - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_interleave_32_c }, + MAKE(F32P, F32, 0, conv_interleave_32_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_sse2 }, + MAKE(F32_OE, F32P, 0, conv_deinterleave_32s_sse2), #endif - { SPA_AUDIO_FORMAT_F32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_deinterleave_32s_c }, + MAKE(F32_OE, F32P, 0, conv_deinterleave_32s_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_sse2 }, + MAKE(F32P, F32_OE, 0, conv_interleave_32s_sse2), #endif - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32_OE, 0, 0, conv_interleave_32s_c }, + MAKE(F32P, F32_OE, 0, conv_interleave_32s_c), - { SPA_AUDIO_FORMAT_U32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u32_to_f32_c }, - { SPA_AUDIO_FORMAT_U32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u32_to_f32d_c }, + MAKE(U32, F32, 0, conv_u32_to_f32_c), + MAKE(U32, F32P, 0, conv_u32_to_f32d_c), #if defined (HAVE_AVX2) - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX2, conv_s32_to_f32d_avx2 }, + MAKE(S32, F32P, 0, conv_s32_to_f32d_avx2, SPA_CPU_FLAG_AVX2), #endif #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSE2, conv_s32_to_f32d_sse2 }, + MAKE(S32, F32P, 0, conv_s32_to_f32d_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s32_to_f32_c }, - { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s32d_to_f32d_c }, - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s32_to_f32d_c }, - { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s32d_to_f32_c }, + MAKE(S32, F32, 0, conv_s32_to_f32_c), + MAKE(S32P, F32P, 0, conv_s32d_to_f32d_c), + MAKE(S32, F32P, 0, conv_s32_to_f32d_c), + MAKE(S32P, F32, 0, conv_s32d_to_f32_c), - { SPA_AUDIO_FORMAT_S32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s32s_to_f32d_c }, + MAKE(S32_OE, F32P, 0, conv_s32s_to_f32d_c), - { SPA_AUDIO_FORMAT_U24, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u24_to_f32_c }, - { SPA_AUDIO_FORMAT_U24, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u24_to_f32d_c }, + MAKE(U24, F32, 0, conv_u24_to_f32_c), + MAKE(U24, F32P, 0, conv_u24_to_f32d_c), - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s24_to_f32_c }, - { SPA_AUDIO_FORMAT_S24P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24d_to_f32d_c }, + MAKE(S24, F32, 0, conv_s24_to_f32_c), + MAKE(S24P, F32P, 0, conv_s24d_to_f32d_c), #if defined (HAVE_AVX2) - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX2, conv_s24_to_f32d_avx2 }, + MAKE(S24, F32P, 0, conv_s24_to_f32d_avx2, SPA_CPU_FLAG_AVX2), #endif #if defined (HAVE_SSSE3) -// { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSSE3, conv_s24_to_f32d_ssse3 }, +// MAKE(S24, F32P, 0, conv_s24_to_f32d_ssse3, SPA_CPU_FLAG_SSSE3), #endif #if defined (HAVE_SSE41) - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSE41, conv_s24_to_f32d_sse41 }, + MAKE(S24, F32P, 0, conv_s24_to_f32d_sse41, SPA_CPU_FLAG_SSE41), #endif #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSE2, conv_s24_to_f32d_sse2 }, + MAKE(S24, F32P, 0, conv_s24_to_f32d_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24_to_f32d_c }, - { SPA_AUDIO_FORMAT_S24P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s24d_to_f32_c }, + MAKE(S24, F32P, 0, conv_s24_to_f32d_c), + MAKE(S24P, F32, 0, conv_s24d_to_f32_c), - { SPA_AUDIO_FORMAT_S24_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24s_to_f32d_c }, + MAKE(S24_OE, F32P, 0, conv_s24s_to_f32d_c), - { SPA_AUDIO_FORMAT_U24_32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_u24_32_to_f32_c }, - { SPA_AUDIO_FORMAT_U24_32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_u24_32_to_f32d_c }, + MAKE(U24_32, F32, 0, conv_u24_32_to_f32_c), + MAKE(U24_32, F32P, 0, conv_u24_32_to_f32d_c), - { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s24_32_to_f32_c }, - { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24_32d_to_f32d_c }, - { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24_32_to_f32d_c }, - { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_s24_32d_to_f32_c }, + MAKE(S24_32, F32, 0, conv_s24_32_to_f32_c), + MAKE(S24_32P, F32P, 0, conv_s24_32d_to_f32d_c), + MAKE(S24_32, F32P, 0, conv_s24_32_to_f32d_c), + MAKE(S24_32P, F32, 0, conv_s24_32d_to_f32_c), - { SPA_AUDIO_FORMAT_S24_32_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_s24_32s_to_f32d_c }, + MAKE(S24_32_OE, F32P, 0, conv_s24_32s_to_f32d_c), - { SPA_AUDIO_FORMAT_F64, SPA_AUDIO_FORMAT_F32, 0, 0, conv_f64_to_f32_c }, - { SPA_AUDIO_FORMAT_F64P, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_f64d_to_f32d_c }, - { SPA_AUDIO_FORMAT_F64, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_f64_to_f32d_c }, - { SPA_AUDIO_FORMAT_F64P, SPA_AUDIO_FORMAT_F32, 0, 0, conv_f64d_to_f32_c }, + MAKE(F64, F32, 0, conv_f64_to_f32_c), + MAKE(F64P, F32P, 0, conv_f64d_to_f32d_c), + MAKE(F64, F32P, 0, conv_f64_to_f32d_c), + MAKE(F64P, F32, 0, conv_f64d_to_f32_c), - { SPA_AUDIO_FORMAT_F64_OE, SPA_AUDIO_FORMAT_F32P, 0, 0, conv_f64s_to_f32d_c }, + MAKE(F64_OE, F32P, 0, conv_f64s_to_f32d_c), /* from f32 */ - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U8, 0, 0, conv_f32_to_u8_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U8P, 0, 0, conv_f32d_to_u8d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U8P, 0, 0, conv_f32_to_u8d_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U8, 0, 0, conv_f32d_to_u8_c }, + MAKE(F32, U8, 0, conv_f32_to_u8_c), + MAKE(F32P, U8P, 0, conv_f32d_to_u8d_c), + MAKE(F32, U8P, 0, conv_f32_to_u8d_c), + MAKE(F32P, U8, 0, conv_f32d_to_u8_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S8, 0, 0, conv_f32_to_s8_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S8P, 0, 0, conv_f32d_to_s8d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S8P, 0, 0, conv_f32_to_s8d_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S8, 0, 0, conv_f32d_to_s8_c }, + MAKE(F32, S8, 0, conv_f32_to_s8_c), + MAKE(F32P, S8P, 0, conv_f32d_to_s8d_c), + MAKE(F32, S8P, 0, conv_f32_to_s8d_c), + MAKE(F32P, S8, 0, conv_f32d_to_s8_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_ALAW, 0, 0, conv_f32d_to_alaw_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_ULAW, 0, 0, conv_f32d_to_ulaw_c }, + MAKE(F32P, ALAW, 0, conv_f32d_to_alaw_c), + MAKE(F32P, ULAW, 0, conv_f32d_to_ulaw_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U16, 0, 0, conv_f32_to_u16_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U16, 0, 0, conv_f32d_to_u16_c }, + MAKE(F32, U16, 0, conv_f32_to_u16_c), + MAKE(F32P, U16, 0, conv_f32d_to_u16_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S16, 0, SPA_CPU_FLAG_SSE2, conv_f32_to_s16_sse2 }, + MAKE(F32, S16, 0, conv_f32_to_s16_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S16, 0, 0, conv_f32_to_s16_c }, + MAKE(F32, S16, 0, conv_f32_to_s16_c), + MAKE(F32P, S16P, 0, conv_f32d_to_s16d_dither_c, 0, CONV_DITHER), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16P, 0, SPA_CPU_FLAG_SSE2, conv_f32d_to_s16d_sse2 }, + MAKE(F32P, S16P, 0, conv_f32d_to_s16d_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16P, 0, 0, conv_f32d_to_s16d_c }, + MAKE(F32P, S16P, 0, conv_f32d_to_s16d_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S16P, 0, 0, conv_f32_to_s16d_c }, + MAKE(F32, S16P, 0, conv_f32_to_s16d_c), + MAKE(F32P, S16, 0, conv_f32d_to_s16_dither_c, 0, CONV_DITHER), #if defined (HAVE_NEON) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 0, SPA_CPU_FLAG_NEON, conv_f32d_to_s16_neon }, + MAKE(F32P, S16, 0, conv_f32d_to_s16_neon, SPA_CPU_FLAG_NEON), #endif #if defined (HAVE_AVX2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 4, SPA_CPU_FLAG_AVX2, conv_f32d_to_s16_4_avx2 }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 2, SPA_CPU_FLAG_AVX2, conv_f32d_to_s16_2_avx2 }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 0, SPA_CPU_FLAG_AVX2, conv_f32d_to_s16_avx2 }, + MAKE(F32P, S16, 4, conv_f32d_to_s16_4_avx2, SPA_CPU_FLAG_AVX2), + MAKE(F32P, S16, 2, conv_f32d_to_s16_2_avx2, SPA_CPU_FLAG_AVX2), + MAKE(F32P, S16, 0, conv_f32d_to_s16_avx2, SPA_CPU_FLAG_AVX2), #endif #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 2, SPA_CPU_FLAG_SSE2, conv_f32d_to_s16_2_sse2 }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 0, SPA_CPU_FLAG_SSE2, conv_f32d_to_s16_sse2 }, + MAKE(F32P, S16, 2, conv_f32d_to_s16_2_sse2, SPA_CPU_FLAG_SSE2), + MAKE(F32P, S16, 0, conv_f32d_to_s16_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16, 0, 0, conv_f32d_to_s16_c }, + MAKE(F32P, S16, 0, conv_f32d_to_s16_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S16_OE, 0, 0, conv_f32d_to_s16s_c }, + MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U32, 0, 0, conv_f32_to_u32_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U32, 0, 0, conv_f32d_to_u32_c }, + MAKE(F32, U32, 0, conv_f32_to_u32_c), + MAKE(F32P, U32, 0, conv_f32d_to_u32_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S32, 0, 0, conv_f32_to_s32_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_f32d_to_s32d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_f32_to_s32d_c }, + MAKE(F32, S32, 0, conv_f32_to_s32_c), + MAKE(F32P, S32P, 0, conv_f32d_to_s32d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S32P, 0, conv_f32d_to_s32d_c), + MAKE(F32, S32P, 0, conv_f32_to_s32d_c), + + MAKE(F32P, S32, 0, conv_f32d_to_s32_dither_c, 0, CONV_DITHER), #if defined (HAVE_AVX2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S32, 0, SPA_CPU_FLAG_AVX2, conv_f32d_to_s32_avx2 }, + MAKE(F32P, S32, 0, conv_f32d_to_s32_avx2, SPA_CPU_FLAG_AVX2), #endif #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S32, 0, SPA_CPU_FLAG_SSE2, conv_f32d_to_s32_sse2 }, + MAKE(F32P, S32, 0, conv_f32d_to_s32_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_f32d_to_s32_c }, + MAKE(F32P, S32, 0, conv_f32d_to_s32_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S32_OE, 0, 0, conv_f32d_to_s32s_c }, + MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U24, 0, 0, conv_f32_to_u24_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U24, 0, 0, conv_f32d_to_u24_c }, + MAKE(F32, U24, 0, conv_f32_to_u24_c), + MAKE(F32P, U24, 0, conv_f32d_to_u24_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S24, 0, 0, conv_f32_to_s24_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24P, 0, 0, conv_f32d_to_s24d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S24P, 0, 0, conv_f32_to_s24d_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24, 0, 0, conv_f32d_to_s24_c }, + MAKE(F32, S24, 0, conv_f32_to_s24_c), + MAKE(F32P, S24P, 0, conv_f32d_to_s24d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24P, 0, conv_f32d_to_s24d_c), + MAKE(F32, S24P, 0, conv_f32_to_s24d_c), + MAKE(F32P, S24, 0, conv_f32d_to_s24_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24, 0, conv_f32d_to_s24_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24_OE, 0, 0, conv_f32d_to_s24s_c }, + MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_U24_32, 0, 0, conv_f32_to_u24_32_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U24_32, 0, 0, conv_f32d_to_u24_32_c }, + MAKE(F32, U24_32, 0, conv_f32_to_u24_32_c), + MAKE(F32P, U24_32, 0, conv_f32d_to_u24_32_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_f32_to_s24_32_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_f32d_to_s24_32d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_f32_to_s24_32d_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_f32d_to_s24_32_c }, + MAKE(F32, S24_32, 0, conv_f32_to_s24_32_c), + MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_c), + MAKE(F32, S24_32P, 0, conv_f32_to_s24_32d_c), + MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_S24_32_OE, 0, 0, conv_f32d_to_s24_32s_c }, + MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_c), - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F64, 0, 0, conv_f32_to_f64_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F64P, 0, 0, conv_f32d_to_f64d_c }, - { SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F64P, 0, 0, conv_f32_to_f64d_c }, - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F64, 0, 0, conv_f32d_to_f64_c }, + MAKE(F32, F64, 0, conv_f32_to_f64_c), + MAKE(F32P, F64P, 0, conv_f32d_to_f64d_c), + MAKE(F32, F64P, 0, conv_f32_to_f64d_c), + MAKE(F32P, F64, 0, conv_f32d_to_f64_c), - { SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F64_OE, 0, 0, conv_f32d_to_f64s_c }, + MAKE(F32P, F64_OE, 0, conv_f32d_to_f64s_c), /* u8 */ - { SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_U8, 0, 0, conv_copy8_c }, - { SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_U8P, 0, 0, conv_copy8d_c }, - { SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_U8P, 0, 0, conv_deinterleave_8_c }, - { SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_U8, 0, 0, conv_interleave_8_c }, + MAKE(U8, U8, 0, conv_copy8_c), + MAKE(U8P, U8P, 0, conv_copy8d_c), + MAKE(U8, U8P, 0, conv_deinterleave_8_c), + MAKE(U8P, U8, 0, conv_interleave_8_c), /* s8 */ - { SPA_AUDIO_FORMAT_S8, SPA_AUDIO_FORMAT_S8, 0, 0, conv_copy8_c }, - { SPA_AUDIO_FORMAT_S8P, SPA_AUDIO_FORMAT_S8P, 0, 0, conv_copy8d_c }, - { SPA_AUDIO_FORMAT_S8, SPA_AUDIO_FORMAT_S8P, 0, 0, conv_deinterleave_8_c }, - { SPA_AUDIO_FORMAT_S8P, SPA_AUDIO_FORMAT_S8, 0, 0, conv_interleave_8_c }, + MAKE(S8, S8, 0, conv_copy8_c), + MAKE(S8P, S8P, 0, conv_copy8d_c), + MAKE(S8, S8P, 0, conv_deinterleave_8_c), + MAKE(S8P, S8, 0, conv_interleave_8_c), /* alaw */ - { SPA_AUDIO_FORMAT_ALAW, SPA_AUDIO_FORMAT_ALAW, 0, 0, conv_copy8_c }, + MAKE(ALAW, ALAW, 0, conv_copy8_c), /* ulaw */ - { SPA_AUDIO_FORMAT_ULAW, SPA_AUDIO_FORMAT_ULAW, 0, 0, conv_copy8_c }, + MAKE(ULAW, ULAW, 0, conv_copy8_c), /* s16 */ - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_S16, 0, 0, conv_copy16_c }, - { SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_S16P, 0, 0, conv_copy16d_c }, - { SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FORMAT_S16P, 0, 0, conv_deinterleave_16_c }, - { SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_S16, 0, 0, conv_interleave_16_c }, + MAKE(S16, S16, 0, conv_copy16_c), + MAKE(S16P, S16P, 0, conv_copy16d_c), + MAKE(S16, S16P, 0, conv_deinterleave_16_c), + MAKE(S16P, S16, 0, conv_interleave_16_c), /* s32 */ - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32, 0, 0, conv_copy32_c }, - { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_copy32d_c }, + MAKE(S32, S32, 0, conv_copy32_c), + MAKE(S32P, S32P, 0, conv_copy32d_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_sse2 }, + MAKE(S32, S32P, 0, conv_deinterleave_32_sse2), #endif - { SPA_AUDIO_FORMAT_S32, SPA_AUDIO_FORMAT_S32P, 0, 0, conv_deinterleave_32_c }, + MAKE(S32, S32P, 0, conv_deinterleave_32_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_sse2 }, + MAKE(S32P, S32, 0, conv_interleave_32_sse2), #endif - { SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_S32, 0, 0, conv_interleave_32_c }, + MAKE(S32P, S32, 0, conv_interleave_32_c), /* s24 */ - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_S24, 0, 0, conv_copy24_c }, - { SPA_AUDIO_FORMAT_S24P, SPA_AUDIO_FORMAT_S24P, 0, 0, conv_copy24d_c }, - { SPA_AUDIO_FORMAT_S24, SPA_AUDIO_FORMAT_S24P, 0, 0, conv_deinterleave_24_c }, - { SPA_AUDIO_FORMAT_S24P, SPA_AUDIO_FORMAT_S24, 0, 0, conv_interleave_24_c }, + MAKE(S24, S24, 0, conv_copy24_c), + MAKE(S24P, S24P, 0, conv_copy24d_c), + MAKE(S24, S24P, 0, conv_deinterleave_24_c), + MAKE(S24P, S24, 0, conv_interleave_24_c), /* s24_32 */ - { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_copy32_c }, - { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_copy32d_c }, + MAKE(S24_32, S24_32, 0, conv_copy32_c), + MAKE(S24_32P, S24_32P, 0, conv_copy32d_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_sse2 }, + MAKE(S24_32, S24_32P, 0, conv_deinterleave_32_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_S24_32, SPA_AUDIO_FORMAT_S24_32P, 0, 0, conv_deinterleave_32_c }, + MAKE(S24_32, S24_32P, 0, conv_deinterleave_32_c), #if defined (HAVE_SSE2) - { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_sse2 }, + MAKE(S24_32P, S24_32, 0, conv_interleave_32_sse2, SPA_CPU_FLAG_SSE2), #endif - { SPA_AUDIO_FORMAT_S24_32P, SPA_AUDIO_FORMAT_S24_32, 0, 0, conv_interleave_32_c }, + MAKE(S24_32P, S24_32, 0, conv_interleave_32_c), /* F64 */ - { SPA_AUDIO_FORMAT_F64, SPA_AUDIO_FORMAT_F64, 0, 0, conv_copy64_c }, - { SPA_AUDIO_FORMAT_F64P, SPA_AUDIO_FORMAT_F64P, 0, 0, conv_copy64d_c }, - { SPA_AUDIO_FORMAT_F64, SPA_AUDIO_FORMAT_F64P, 0, 0, conv_deinterleave_64_c }, - { SPA_AUDIO_FORMAT_F64P, SPA_AUDIO_FORMAT_F64, 0, 0, conv_interleave_64_c }, + MAKE(F64, F64, 0, conv_copy64_c), + MAKE(F64P, F64P, 0, conv_copy64d_c), + MAKE(F64, F64P, 0, conv_deinterleave_64_c), + MAKE(F64P, F64, 0, conv_interleave_64_c), }; +#undef MAKE #define MATCH_CHAN(a,b) ((a) == 0 || (a) == (b)) #define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a) +#define MATCH_DITHER(a,b) ((a) == 0 || ((a) & (b)) == a) static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt, - uint32_t n_channels, uint32_t cpu_flags) + uint32_t n_channels, uint32_t cpu_flags, uint32_t dither_flags) { size_t i; @@ -317,7 +343,8 @@ static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt if (conv_table[i].src_fmt == src_fmt && conv_table[i].dst_fmt == dst_fmt && MATCH_CHAN(conv_table[i].n_channels, n_channels) && - MATCH_CPU_FLAGS(conv_table[i].cpu_flags, cpu_flags)) + MATCH_CPU_FLAGS(conv_table[i].cpu_flags, cpu_flags) && + MATCH_DITHER(conv_table[i].dither_flags, dither_flags)) return &conv_table[i]; } return NULL; @@ -326,20 +353,47 @@ static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt static void impl_convert_free(struct convert *conv) { conv->process = NULL; + free(conv->dither); + conv->dither = NULL; } int convert_init(struct convert *conv) { const struct conv_info *info; + uint32_t i, shift, dither_flags; - info = find_conv_info(conv->src_fmt, conv->dst_fmt, conv->n_channels, conv->cpu_flags); + shift = 32u - SPA_MIN(conv->quantize, 32u); + shift += conv->noise; + + conv->mask = (1ULL << (shift + 1)) - 1; + conv->offset = shift < 32 ? -(1ULL << shift) : 0; + conv->bias = shift > 0 ? 1 << (shift - 1) : 0; + + dither_flags = 0; + if (conv->method != DITHER_METHOD_NONE || conv->noise) + dither_flags |= CONV_DITHER; + if (conv->method == DITHER_METHOD_SHAPED_5) + dither_flags |= CONV_SHAPE; + + info = find_conv_info(conv->src_fmt, conv->dst_fmt, conv->n_channels, + conv->cpu_flags, dither_flags); if (info == NULL) return -ENOTSUP; + conv->dither_size = DITHER_SIZE; + conv->dither = calloc(conv->dither_size + 16 + + FMT_OPS_MAX_ALIGN / sizeof(float), sizeof(float)); + if (conv->dither == NULL) + return -errno; + + for (i = 0; i < SPA_N_ELEMENTS(conv->random); i++) + conv->random[i] = random(); + conv->is_passthrough = conv->src_fmt == conv->dst_fmt; conv->cpu_flags = info->cpu_flags; conv->process = info->process; conv->free = impl_convert_free; + conv->func_name = info->name; return 0; } diff --git a/spa/plugins/audioconvert/fmt-ops.h b/spa/plugins/audioconvert/fmt-ops.h index 3016c6752..f4373a122 100644 --- a/spa/plugins/audioconvert/fmt-ops.h +++ b/spa/plugins/audioconvert/fmt-ops.h @@ -33,6 +33,9 @@ #endif #include +#include + +#define FMT_OPS_MAX_ALIGN 32 #define U8_MIN 0 #define U8_MAX 255 @@ -176,12 +179,29 @@ static inline void write_s24s(void *dst, int32_t val) #define MAX_NS 64 struct convert { + uint32_t quantize; + uint32_t noise; +#define DITHER_METHOD_NONE 0 +#define DITHER_METHOD_RECTANGULAR 1 +#define DITHER_METHOD_TRIANGULAR 2 +#define DITHER_METHOD_SHAPED_5 3 + uint32_t method; + uint32_t src_fmt; uint32_t dst_fmt; uint32_t n_channels; uint32_t cpu_flags; + const char *func_name; unsigned int is_passthrough:1; + + int32_t bias; + int32_t offset; + uint32_t mask; + uint32_t random[16 + FMT_OPS_MAX_ALIGN/4]; + int32_t *dither; + uint32_t dither_size; + float ns_data[MAX_NS]; uint32_t ns_idx; uint32_t ns_size; @@ -193,6 +213,27 @@ struct convert { int convert_init(struct convert *conv); +static const struct dither_method_info { + const char *label; + const char *description; + uint32_t method; +} dither_method_info[] = { + [DITHER_METHOD_NONE] = { "none", "Disabled", DITHER_METHOD_NONE }, + [DITHER_METHOD_RECTANGULAR] = { "rectangular", "Rectangular dithering", DITHER_METHOD_RECTANGULAR }, + [DITHER_METHOD_TRIANGULAR] = { "triangular", "Triangular dithering", DITHER_METHOD_TRIANGULAR }, + [DITHER_METHOD_SHAPED_5] = { "shaped5", "Shaped 5 dithering", DITHER_METHOD_SHAPED_5 } +}; + +static inline uint32_t dither_method_from_label(const char *label) +{ + uint32_t i; + for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) { + if (spa_streq(dither_method_info[i].label, label)) + return dither_method_info[i].method; + } + return DITHER_METHOD_NONE; +} + #define convert_process(conv,...) (conv)->process(conv, __VA_ARGS__) #define convert_free(conv) (conv)->free(conv) @@ -200,8 +241,6 @@ int convert_init(struct convert *conv); void conv_##name##_##arch(struct convert *conv, void * SPA_RESTRICT dst[], \ const void * SPA_RESTRICT src[], uint32_t n_samples) \ -#define FMT_OPS_MAX_ALIGN 32 - DEFINE_FUNCTION(copy8d, c); DEFINE_FUNCTION(copy8, c); DEFINE_FUNCTION(copy16d, c); @@ -268,31 +307,43 @@ DEFINE_FUNCTION(f32d_to_ulaw, c); DEFINE_FUNCTION(f32_to_u16, c); DEFINE_FUNCTION(f32d_to_u16, c); DEFINE_FUNCTION(f32d_to_s16d, c); +DEFINE_FUNCTION(f32d_to_s16d_dither, c); DEFINE_FUNCTION(f32_to_s16, c); DEFINE_FUNCTION(f32_to_s16d, c); DEFINE_FUNCTION(f32d_to_s16, c); +DEFINE_FUNCTION(f32d_to_s16_dither, c); DEFINE_FUNCTION(f32d_to_s16s, c); +DEFINE_FUNCTION(f32d_to_s16s_dither, c); DEFINE_FUNCTION(f32_to_u32, c); DEFINE_FUNCTION(f32d_to_u32, c); DEFINE_FUNCTION(f32d_to_s32d, c); +DEFINE_FUNCTION(f32d_to_s32d_dither, c); DEFINE_FUNCTION(f32_to_s32, c); DEFINE_FUNCTION(f32_to_s32d, c); DEFINE_FUNCTION(f32d_to_s32, c); +DEFINE_FUNCTION(f32d_to_s32_dither, c); DEFINE_FUNCTION(f32d_to_s32s, c); +DEFINE_FUNCTION(f32d_to_s32s_dither, c); DEFINE_FUNCTION(f32_to_u24, c); DEFINE_FUNCTION(f32d_to_u24, c); DEFINE_FUNCTION(f32d_to_s24d, c); +DEFINE_FUNCTION(f32d_to_s24d_dither, c); DEFINE_FUNCTION(f32_to_s24, c); DEFINE_FUNCTION(f32_to_s24d, c); DEFINE_FUNCTION(f32d_to_s24, c); +DEFINE_FUNCTION(f32d_to_s24_dither, c); DEFINE_FUNCTION(f32d_to_s24s, c); +DEFINE_FUNCTION(f32d_to_s24s_dither, c); DEFINE_FUNCTION(f32_to_u24_32, c); DEFINE_FUNCTION(f32d_to_u24_32, c); DEFINE_FUNCTION(f32d_to_s24_32d, c); +DEFINE_FUNCTION(f32d_to_s24_32d_dither, c); DEFINE_FUNCTION(f32_to_s24_32, c); DEFINE_FUNCTION(f32_to_s24_32d, c); DEFINE_FUNCTION(f32d_to_s24_32, c); +DEFINE_FUNCTION(f32d_to_s24_32_dither, c); DEFINE_FUNCTION(f32d_to_s24_32s, c); +DEFINE_FUNCTION(f32d_to_s24_32s_dither, c); DEFINE_FUNCTION(f32d_to_f64d, c); DEFINE_FUNCTION(f32_to_f64, c); DEFINE_FUNCTION(f32_to_f64d, c); diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build index 97b1962a6..3eb007f9c 100644 --- a/spa/plugins/audioconvert/meson.build +++ b/spa/plugins/audioconvert/meson.build @@ -22,8 +22,7 @@ if have_sse endif if have_sse2 audioconvert_sse2 = static_library('audioconvert_sse2', - ['fmt-ops-sse2.c', - 'dither-ops-sse2.c' ], + ['fmt-ops-sse2.c' ], c_args : [sse2_args, '-O3', '-DHAVE_SSE2'], dependencies : [ spa_dep ], install : false @@ -95,9 +94,7 @@ audioconvert_lib = static_library('audioconvert', 'resample-peaks.c', 'fmt-ops-c.c', 'volume-ops.c', - 'volume-ops-c.c', - 'dither-ops.c', - 'dither-ops-c.c' ], + 'volume-ops-c.c' ], c_args : [ simd_cargs, '-O3'], link_with : simd_dependencies, include_directories : [configinc],