audioconvert: improve noise shaping

Reorganize things a little so we can add more noise shapers.
Add sloped triangular noise.
Add wanamaker3 noise shaping.
This commit is contained in:
Wim Taymans 2022-07-15 12:36:15 +02:00
parent 7151150802
commit a4db745a7e
6 changed files with 272 additions and 171 deletions

View file

@ -628,8 +628,8 @@ static int impl_node_enum_params(void *object, int seq,
param = spa_pod_builder_add_object(&b, param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_PropInfo, id, SPA_TYPE_OBJECT_PropInfo, id,
SPA_PROP_INFO_name, SPA_POD_String("dither.noise"), SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
SPA_PROP_INFO_description, SPA_POD_String("Add dithering noise"), SPA_PROP_INFO_description, SPA_POD_String("Add noise bits"),
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise, 0, 16), SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16),
SPA_PROP_INFO_params, SPA_POD_Bool(true)); SPA_PROP_INFO_params, SPA_POD_Bool(true));
break; break;
case 23: case 23:

View file

@ -230,95 +230,104 @@ lcnoise(uint32_t *state)
return (int32_t)(*state); return (int32_t)(*state);
} }
static inline void update_dither_c(struct convert *conv, uint32_t n_samples) static inline void update_noise_c(struct convert *conv, uint32_t n_samples)
{ {
uint32_t n; uint32_t n;
float *dither = conv->dither, scale = conv->scale; float *noise = conv->noise, scale = conv->scale;
uint32_t *state = &conv->random[0]; uint32_t *state = &conv->random[0];
int32_t *prev = &conv->prev[0], old, new;
if (conv->method < DITHER_METHOD_TRIANGULAR) { switch (conv->noise_method) {
case NOISE_METHOD_RECTANGULAR:
for (n = 0; n < n_samples; n++) for (n = 0; n < n_samples; n++)
dither[n] = lcnoise(state) * scale; noise[n] = lcnoise(state) * scale;
} else { break;
case NOISE_METHOD_TRIANGULAR:
for (n = 0; n < n_samples; n++) for (n = 0; n < n_samples; n++)
dither[n] = (lcnoise(state) + lcnoise(state)) * scale; noise[n] = (lcnoise(state) - lcnoise(state)) * scale;
break;
case NOISE_METHOD_TRIANGULAR_HF:
old = *prev;
for (n = 0; n < n_samples; n++) {
new = lcnoise(state);
noise[n] = (new - old) * scale;
old = new;
}
*prev = old;
break;
} }
} }
#define MAKE_D_dither(dname,dtype,func) \ #define MAKE_D_noise(dname,dtype,func) \
void conv_f32d_to_ ##dname## d_dither_c(struct convert *conv, \ void conv_f32d_to_ ##dname## d_noise_c(struct convert *conv, \
void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \ void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \
uint32_t n_samples) \ uint32_t n_samples) \
{ \ { \
uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \
float *dither = conv->dither; \ float *noise = conv->noise; \
update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \
for (i = 0; i < n_channels; i++) { \ for (i = 0; i < n_channels; i++) { \
const float *s = src[i]; \ const float *s = src[i]; \
dtype *d = dst[i]; \ dtype *d = dst[i]; \
for (j = 0; j < n_samples;) { \ for (j = 0; j < n_samples;) { \
chunk = SPA_MIN(n_samples - j, dither_size); \ chunk = SPA_MIN(n_samples - j, noise_size); \
for (k = 0; k < chunk; k++, j++) \ for (k = 0; k < chunk; k++, j++) \
d[j] = func (s[j], dither[k]); \ d[j] = func (s[j], noise[k]); \
} \ } \
} \ } \
} }
#define MAKE_I_dither(dname,dtype,func) \ #define MAKE_I_noise(dname,dtype,func) \
void conv_f32d_to_ ##dname## _dither_c(struct convert *conv, \ void conv_f32d_to_ ##dname## _noise_c(struct convert *conv, \
void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \ void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \
uint32_t n_samples) \ uint32_t n_samples) \
{ \ { \
const float **s = (const float **) src; \ const float **s = (const float **) src; \
dtype *d = dst[0]; \ dtype *d = dst[0]; \
uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \
float *dither = conv->dither; \ float *noise = conv->noise; \
update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \
for (j = 0; j < n_samples;) { \ for (j = 0; j < n_samples;) { \
chunk = SPA_MIN(n_samples - j, dither_size); \ chunk = SPA_MIN(n_samples - j, noise_size); \
for (k = 0; k < chunk; k++, j++) { \ for (k = 0; k < chunk; k++, j++) { \
for (i = 0; i < n_channels; i++) \ for (i = 0; i < n_channels; i++) \
*d++ = func (s[i][j], dither[k]); \ *d++ = func (s[i][j], noise[k]); \
} \ } \
} \ } \
} }
MAKE_D_dither(u8, uint8_t, F32_TO_U8_D); MAKE_D_noise(u8, uint8_t, F32_TO_U8_D);
MAKE_I_dither(u8, uint8_t, F32_TO_U8_D); MAKE_I_noise(u8, uint8_t, F32_TO_U8_D);
MAKE_D_dither(s8, int8_t, F32_TO_S8_D); MAKE_D_noise(s8, int8_t, F32_TO_S8_D);
MAKE_I_dither(s8, int8_t, F32_TO_S8_D); MAKE_I_noise(s8, int8_t, F32_TO_S8_D);
MAKE_D_dither(s16, int16_t, F32_TO_S16_D); MAKE_D_noise(s16, int16_t, F32_TO_S16_D);
MAKE_I_dither(s16, int16_t, F32_TO_S16_D); MAKE_I_noise(s16, int16_t, F32_TO_S16_D);
MAKE_I_dither(s16s, uint16_t, F32_TO_S16S_D); MAKE_I_noise(s16s, uint16_t, F32_TO_S16S_D);
MAKE_D_dither(s32, int32_t, F32_TO_S32_D); MAKE_D_noise(s32, int32_t, F32_TO_S32_D);
MAKE_I_dither(s32, int32_t, F32_TO_S32_D); MAKE_I_noise(s32, int32_t, F32_TO_S32_D);
MAKE_I_dither(s32s, uint32_t, F32_TO_S32S_D); MAKE_I_noise(s32s, uint32_t, F32_TO_S32S_D);
MAKE_D_dither(s24, int24_t, F32_TO_S24_D); MAKE_D_noise(s24, int24_t, F32_TO_S24_D);
MAKE_I_dither(s24, int24_t, F32_TO_S24_D); MAKE_I_noise(s24, int24_t, F32_TO_S24_D);
MAKE_I_dither(s24s, int24_t, F32_TO_S24_D); MAKE_I_noise(s24s, int24_t, F32_TO_S24_D);
MAKE_D_dither(s24_32, int32_t, F32_TO_S24_32_D); MAKE_D_noise(s24_32, int32_t, F32_TO_S24_32_D);
MAKE_I_dither(s24_32, int32_t, F32_TO_S24_32_D); MAKE_I_noise(s24_32, int32_t, F32_TO_S24_32_D);
MAKE_I_dither(s24_32s, int32_t, F32_TO_S24_32S_D); MAKE_I_noise(s24_32s, int32_t, F32_TO_S24_32S_D);
#define SHAPER(type,s,scale,offs,sh,min,max,d) \
#define SHAPER5(type,s,scale,offs,sh,min,max,d) \
({ \ ({ \
type t; \ type t; \
float v = s * scale + offs + \ float v = s * scale + offs; \
- sh->e[idx] * 2.033f \ for (n = 0; n < n_ns; n++) \
+ sh->e[(idx - 1) & NS_MASK] * 2.165f \ v += sh->e[idx + n] * ns[n]; \
- sh->e[(idx - 2) & NS_MASK] * 1.959f \ t = FTOI(type, v, 1.0f, 0.0f, d, min, max); \
+ sh->e[(idx - 3) & NS_MASK] * 1.590f \ idx = (idx - 1) & NS_MASK; \
- sh->e[(idx - 4) & NS_MASK] * 0.6149f; \ sh->e[idx] = sh->e[idx + NS_MAX] = v - t; \
t = (type)SPA_CLAMP(v + d, min, max); \
idx = (idx + 1) & NS_MASK; \
sh->e[idx] = t - v; \
t; \ t; \
}) })
#define F32_TO_U8_SH(s,sh,d) SHAPER5(uint8_t, s, U8_SCALE, U8_OFFS, sh, U8_MIN, U8_MAX, d) #define F32_TO_U8_SH(s,sh,d) SHAPER(uint8_t, s, U8_SCALE, U8_OFFS, sh, U8_MIN, U8_MAX, d)
#define F32_TO_S8_SH(s,sh,d) SHAPER5(int8_t, s, S8_SCALE, 0, sh, S8_MIN, S8_MAX, d) #define F32_TO_S8_SH(s,sh,d) SHAPER(int8_t, s, S8_SCALE, 0, sh, S8_MIN, S8_MAX, d)
#define F32_TO_S16_SH(s,sh,d) SHAPER5(int16_t, s, S16_SCALE, 0, sh, S16_MIN, S16_MAX, d) #define F32_TO_S16_SH(s,sh,d) SHAPER(int16_t, s, S16_SCALE, 0, sh, S16_MIN, S16_MAX, d)
#define F32_TO_S16S_SH(s,sh,d) bswap_16(F32_TO_S16_SH(s,sh,d)) #define F32_TO_S16S_SH(s,sh,d) bswap_16(F32_TO_S16_SH(s,sh,d))
#define MAKE_D_shaped(dname,dtype,func) \ #define MAKE_D_shaped(dname,dtype,func) \
@ -326,18 +335,19 @@ void conv_f32d_to_ ##dname## d_shaped_c(struct convert *conv, \
void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \ void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], \
uint32_t n_samples) \ uint32_t n_samples) \
{ \ { \
uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \
float *dither = conv->dither; \ const float *noise = conv->noise, *ns = conv->ns; \
update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ uint32_t n, n_ns = conv->n_ns; \
update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \
for (i = 0; i < n_channels; i++) { \ for (i = 0; i < n_channels; i++) { \
const float *s = src[i]; \ const float *s = src[i]; \
dtype *d = dst[i]; \ dtype *d = dst[i]; \
struct shaper *sh = &conv->shaper[i]; \ struct shaper *sh = &conv->shaper[i]; \
uint32_t idx = sh->idx; \ uint32_t idx = sh->idx; \
for (j = 0; j < n_samples;) { \ for (j = 0; j < n_samples;) { \
chunk = SPA_MIN(n_samples - j, dither_size); \ chunk = SPA_MIN(n_samples - j, noise_size); \
for (k = 0; k < chunk; k++, j++) \ for (k = 0; k < chunk; k++, j++) \
d[j] = func (s[j], sh, dither[k]); \ d[j] = func (s[j], sh, noise[k]); \
} \ } \
sh->idx = idx; \ sh->idx = idx; \
} \ } \
@ -349,18 +359,19 @@ void conv_f32d_to_ ##dname## _shaped_c(struct convert *conv, \
uint32_t n_samples) \ uint32_t n_samples) \
{ \ { \
dtype *d0 = dst[0]; \ dtype *d0 = dst[0]; \
uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \
float *dither = conv->dither; \ const float *noise = conv->noise, *ns = conv->ns; \
update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ uint32_t n, n_ns = conv->n_ns; \
update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \
for (i = 0; i < n_channels; i++) { \ for (i = 0; i < n_channels; i++) { \
const float *s = src[i]; \ const float *s = src[i]; \
dtype *d = &d0[i]; \ dtype *d = &d0[i]; \
struct shaper *sh = &conv->shaper[i]; \ struct shaper *sh = &conv->shaper[i]; \
uint32_t idx = sh->idx; \ uint32_t idx = sh->idx; \
for (j = 0; j < n_samples;) { \ for (j = 0; j < n_samples;) { \
chunk = SPA_MIN(n_samples - j, dither_size); \ chunk = SPA_MIN(n_samples - j, noise_size); \
for (k = 0; k < chunk; k++, j++) \ for (k = 0; k < chunk; k++, j++) \
d[j*n_channels] = func (s[j], sh, dither[k]); \ d[j*n_channels] = func (s[j], sh, noise[k]); \
} \ } \
sh->idx = idx; \ sh->idx = idx; \
} \ } \

View file

@ -577,38 +577,54 @@ conv_f32d_to_s32_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
}) })
static inline void update_dither_sse2(struct convert *conv, uint32_t n_samples) static inline void update_noise_sse2(struct convert *conv, uint32_t n_samples)
{ {
uint32_t n; uint32_t n;
const uint32_t *r = SPA_PTR_ALIGN(conv->random, 16, uint32_t); const uint32_t *r = SPA_PTR_ALIGN(conv->random, 16, uint32_t);
const int32_t *p = SPA_PTR_ALIGN(conv->prev, 16, int32_t);
__m128 scale = _mm_set1_ps(conv->scale); __m128 scale = _mm_set1_ps(conv->scale);
__m128 out[1]; __m128 out[1];
float *dither = SPA_PTR_ALIGN(conv->dither, 16, float); float *noise = SPA_PTR_ALIGN(conv->noise, 16, float);
__m128i in[2]; __m128i in[1], old[1], new[1];
if (conv->method < DITHER_METHOD_TRIANGULAR) { switch (conv->noise_method) {
case DITHER_METHOD_RECTANGULAR:
for (n = 0; n < n_samples; n += 4) { for (n = 0; n < n_samples; n += 4) {
in[0] = _MM_XORSHIFT_EPI32(r); in[0] = _MM_XORSHIFT_EPI32(r);
out[0] = _mm_cvtepi32_ps(_MM_XORSHIFT_EPI32(r)); out[0] = _mm_cvtepi32_ps(_MM_XORSHIFT_EPI32(r));
out[0] = _mm_mul_ps(out[0], scale); out[0] = _mm_mul_ps(out[0], scale);
_mm_store_ps(&dither[n], out[0]); _mm_store_ps(&noise[n], out[0]);
} }
} else { break;
case DITHER_METHOD_TRIANGULAR:
for (n = 0; n < n_samples; n += 4) { for (n = 0; n < n_samples; n += 4) {
in[0] = _mm_add_epi32( _MM_XORSHIFT_EPI32(r), _MM_XORSHIFT_EPI32(r)); in[0] = _mm_sub_epi32( _MM_XORSHIFT_EPI32(r), _MM_XORSHIFT_EPI32(r));
out[0] = _mm_cvtepi32_ps(in[0]); out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], scale); out[0] = _mm_mul_ps(out[0], scale);
_mm_store_ps(&dither[n], out[0]); _mm_store_ps(&noise[n], out[0]);
} }
break;
case DITHER_METHOD_TRIANGULAR_HF:
old[0] = _mm_load_si128((__m128i*)p);
for (n = 0; n < n_samples; n += 4) {
new[0] = _MM_XORSHIFT_EPI32(r);
in[0] = _mm_sub_epi32(old[0], new[0]);
old[0] = new[0];
out[0] = _mm_cvtepi32_ps(in[0]);
out[0] = _mm_mul_ps(out[0], scale);
_mm_store_ps(&noise[n], out[0]);
}
_mm_store_si128((__m128i*)p, old[0]);
break;
} }
} }
static void static void
conv_f32d_to_s32_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, conv_f32d_to_s32_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples) uint32_t n_channels, uint32_t n_samples)
{ {
const float *s = src; const float *s = src;
float *dither = SPA_PTR_ALIGN(conv->dither, 16, float); float *noise = SPA_PTR_ALIGN(conv->noise, 16, float);
int32_t *d = dst; int32_t *d = dst;
uint32_t n, unrolled; uint32_t n, unrolled;
__m128 in[1]; __m128 in[1];
@ -624,7 +640,7 @@ conv_f32d_to_s32_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
for(n = 0; n < unrolled; n += 4) { for(n = 0; n < unrolled; n += 4) {
in[0] = _mm_mul_ps(_mm_load_ps(&s[n]), scale); in[0] = _mm_mul_ps(_mm_load_ps(&s[n]), scale);
in[0] = _mm_add_ps(in[0], _mm_load_ps(&dither[n])); in[0] = _mm_add_ps(in[0], _mm_load_ps(&noise[n]));
in[0] = _MM_CLAMP_PS(in[0], int_min, int_max); in[0] = _MM_CLAMP_PS(in[0], int_min, int_max);
out[0] = _mm_cvtps_epi32(in[0]); out[0] = _mm_cvtps_epi32(in[0]);
out[0] = _mm_slli_epi32(out[0], 8); out[0] = _mm_slli_epi32(out[0], 8);
@ -641,7 +657,7 @@ conv_f32d_to_s32_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
for(; n < n_samples; n++) { for(; n < n_samples; n++) {
in[0] = _mm_load_ss(&s[n]); in[0] = _mm_load_ss(&s[n]);
in[0] = _mm_mul_ss(in[0], scale); in[0] = _mm_mul_ss(in[0], scale);
in[0] = _mm_add_ss(in[0], _mm_load_ss(&dither[n])); in[0] = _mm_add_ss(in[0], _mm_load_ss(&noise[n]));
in[0] = _MM_CLAMP_SS(in[0], int_min, int_max); in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
*d = _mm_cvtss_si32(in[0]) << 8; *d = _mm_cvtss_si32(in[0]) << 8;
d += n_channels; d += n_channels;
@ -649,19 +665,19 @@ conv_f32d_to_s32_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
} }
void void
conv_f32d_to_s32_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], conv_f32d_to_s32_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
uint32_t n_samples) uint32_t n_samples)
{ {
int32_t *d = dst[0]; int32_t *d = dst[0];
uint32_t i, k, chunk, n_channels = conv->n_channels; uint32_t i, k, chunk, n_channels = conv->n_channels;
update_dither_sse2(conv, SPA_MIN(n_samples, conv->dither_size)); update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size));
for(i = 0; i < n_channels; i++) { for(i = 0; i < n_channels; i++) {
const float *s = src[i]; const float *s = src[i];
for(k = 0; k < n_samples; k += chunk) { for(k = 0; k < n_samples; k += chunk) {
chunk = SPA_MIN(n_samples - k, conv->dither_size); chunk = SPA_MIN(n_samples - k, conv->noise_size);
conv_f32d_to_s32_1s_dither_sse2(conv, &d[i + k*n_channels], &s[k], n_channels, chunk); conv_f32d_to_s32_1s_noise_sse2(conv, &d[i + k*n_channels], &s[k], n_channels, chunk);
} }
} }
} }
@ -1238,12 +1254,12 @@ conv_f32d_to_s16_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const voi
} }
static void static void
conv_f32d_to_s16_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, conv_f32d_to_s16_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src,
uint32_t n_channels, uint32_t n_samples) uint32_t n_channels, uint32_t n_samples)
{ {
const float *s0 = src; const float *s0 = src;
int16_t *d = dst; int16_t *d = dst;
float *dither = SPA_PTR_ALIGN(conv->dither, 16, float); float *noise = SPA_PTR_ALIGN(conv->noise, 16, float);
uint32_t n, unrolled; uint32_t n, unrolled;
__m128 in[2]; __m128 in[2];
__m128i out[2]; __m128i out[2];
@ -1259,8 +1275,8 @@ conv_f32d_to_s16_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
for(n = 0; n < unrolled; n += 8) { for(n = 0; n < unrolled; n += 8) {
in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), int_scale); in[0] = _mm_mul_ps(_mm_load_ps(&s0[n]), int_scale);
in[1] = _mm_mul_ps(_mm_load_ps(&s0[n+4]), int_scale); in[1] = _mm_mul_ps(_mm_load_ps(&s0[n+4]), int_scale);
in[0] = _mm_add_ps(in[0], _mm_load_ps(&dither[n])); in[0] = _mm_add_ps(in[0], _mm_load_ps(&noise[n]));
in[1] = _mm_add_ps(in[1], _mm_load_ps(&dither[n+4])); in[1] = _mm_add_ps(in[1], _mm_load_ps(&noise[n+4]));
out[0] = _mm_cvtps_epi32(in[0]); out[0] = _mm_cvtps_epi32(in[0]);
out[1] = _mm_cvtps_epi32(in[1]); out[1] = _mm_cvtps_epi32(in[1]);
out[0] = _mm_packs_epi32(out[0], out[1]); out[0] = _mm_packs_epi32(out[0], out[1]);
@ -1277,7 +1293,7 @@ conv_f32d_to_s16_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
} }
for(; n < n_samples; n++) { for(; n < n_samples; n++) {
in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale); in[0] = _mm_mul_ss(_mm_load_ss(&s0[n]), int_scale);
in[0] = _mm_add_ss(in[0], _mm_load_ss(&dither[n])); in[0] = _mm_add_ss(in[0], _mm_load_ss(&noise[n]));
in[0] = _MM_CLAMP_SS(in[0], int_min, int_max); in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
*d = _mm_cvtss_si32(in[0]); *d = _mm_cvtss_si32(in[0]);
d += n_channels; d += n_channels;
@ -1285,30 +1301,30 @@ conv_f32d_to_s16_1s_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, c
} }
void void
conv_f32d_to_s16_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], conv_f32d_to_s16_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
uint32_t n_samples) uint32_t n_samples)
{ {
int16_t *d = dst[0]; int16_t *d = dst[0];
uint32_t i, k, chunk, n_channels = conv->n_channels; uint32_t i, k, chunk, n_channels = conv->n_channels;
update_dither_sse2(conv, SPA_MIN(n_samples, conv->dither_size)); update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size));
for(i = 0; i < n_channels; i++) { for(i = 0; i < n_channels; i++) {
const float *s = src[i]; const float *s = src[i];
for(k = 0; k < n_samples; k += chunk) { for(k = 0; k < n_samples; k += chunk) {
chunk = SPA_MIN(n_samples - k, conv->dither_size); chunk = SPA_MIN(n_samples - k, conv->noise_size);
conv_f32d_to_s16_1s_dither_sse2(conv, &d[i + k*n_channels], &s[k], n_channels, chunk); conv_f32d_to_s16_1s_noise_sse2(conv, &d[i + k*n_channels], &s[k], n_channels, chunk);
} }
} }
} }
static void static void
conv_f32_to_s16_1_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, conv_f32_to_s16_1_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src,
uint32_t n_samples) uint32_t n_samples)
{ {
const float *s = src; const float *s = src;
int16_t *d = dst; int16_t *d = dst;
float *dither = SPA_PTR_ALIGN(conv->dither, 16, float); float *noise = SPA_PTR_ALIGN(conv->noise, 16, float);
uint32_t n, unrolled; uint32_t n, unrolled;
__m128 in[2]; __m128 in[2];
__m128i out[2]; __m128i out[2];
@ -1324,8 +1340,8 @@ conv_f32_to_s16_1_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, con
for(n = 0; n < unrolled; n += 8) { for(n = 0; n < unrolled; n += 8) {
in[0] = _mm_mul_ps(_mm_load_ps(&s[n]), int_scale); in[0] = _mm_mul_ps(_mm_load_ps(&s[n]), int_scale);
in[1] = _mm_mul_ps(_mm_load_ps(&s[n+4]), int_scale); in[1] = _mm_mul_ps(_mm_load_ps(&s[n+4]), int_scale);
in[0] = _mm_add_ps(in[0], _mm_load_ps(&dither[n])); in[0] = _mm_add_ps(in[0], _mm_load_ps(&noise[n]));
in[1] = _mm_add_ps(in[1], _mm_load_ps(&dither[n+4])); in[1] = _mm_add_ps(in[1], _mm_load_ps(&noise[n+4]));
out[0] = _mm_cvtps_epi32(in[0]); out[0] = _mm_cvtps_epi32(in[0]);
out[1] = _mm_cvtps_epi32(in[1]); out[1] = _mm_cvtps_epi32(in[1]);
out[0] = _mm_packs_epi32(out[0], out[1]); out[0] = _mm_packs_epi32(out[0], out[1]);
@ -1333,26 +1349,26 @@ conv_f32_to_s16_1_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst, con
} }
for(; n < n_samples; n++) { for(; n < n_samples; n++) {
in[0] = _mm_mul_ss(_mm_load_ss(&s[n]), int_scale); in[0] = _mm_mul_ss(_mm_load_ss(&s[n]), int_scale);
in[0] = _mm_add_ss(in[0], _mm_load_ss(&dither[n])); in[0] = _mm_add_ss(in[0], _mm_load_ss(&noise[n]));
in[0] = _MM_CLAMP_SS(in[0], int_min, int_max); in[0] = _MM_CLAMP_SS(in[0], int_min, int_max);
d[n] = _mm_cvtss_si32(in[0]); d[n] = _mm_cvtss_si32(in[0]);
} }
} }
void void
conv_f32d_to_s16d_dither_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], conv_f32d_to_s16d_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
uint32_t n_samples) uint32_t n_samples)
{ {
uint32_t i, k, chunk, n_channels = conv->n_channels; uint32_t i, k, chunk, n_channels = conv->n_channels;
update_dither_sse2(conv, SPA_MIN(n_samples, conv->dither_size)); update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size));
for(i = 0; i < n_channels; i++) { for(i = 0; i < n_channels; i++) {
const float *s = src[i]; const float *s = src[i];
int16_t *d = dst[i]; int16_t *d = dst[i];
for(k = 0; k < n_samples; k += chunk) { for(k = 0; k < n_samples; k += chunk) {
chunk = SPA_MIN(n_samples - k, conv->dither_size); chunk = SPA_MIN(n_samples - k, conv->noise_size);
conv_f32_to_s16_1_dither_sse2(conv, &d[k], &s[k], chunk); conv_f32_to_s16_1_noise_sse2(conv, &d[k], &s[k], chunk);
} }
} }
} }

View file

@ -46,9 +46,9 @@ struct conv_info {
const char *name; const char *name;
uint32_t cpu_flags; uint32_t cpu_flags;
#define CONV_DITHER (1<<0) #define CONV_NOISE (1<<0)
#define CONV_SHAPE (1<<1) #define CONV_SHAPE (1<<1)
uint32_t dither_flags; uint32_t conv_flags;
}; };
#define MAKE(fmt1,fmt2,chan,func,...) \ #define MAKE(fmt1,fmt2,chan,func,...) \
@ -171,20 +171,20 @@ static struct conv_info conv_table[] =
/* from f32 */ /* from f32 */
MAKE(F32, U8, 0, conv_f32_to_u8_c), MAKE(F32, U8, 0, conv_f32_to_u8_c),
MAKE(F32P, U8P, 0, conv_f32d_to_u8d_shaped_c, 0, CONV_SHAPE), MAKE(F32P, U8P, 0, conv_f32d_to_u8d_shaped_c, 0, CONV_SHAPE),
MAKE(F32P, U8P, 0, conv_f32d_to_u8d_dither_c, 0, CONV_DITHER), MAKE(F32P, U8P, 0, conv_f32d_to_u8d_noise_c, 0, CONV_NOISE),
MAKE(F32P, U8P, 0, conv_f32d_to_u8d_c), MAKE(F32P, U8P, 0, conv_f32d_to_u8d_c),
MAKE(F32, U8P, 0, conv_f32_to_u8d_c), MAKE(F32, U8P, 0, conv_f32_to_u8d_c),
MAKE(F32P, U8, 0, conv_f32d_to_u8_shaped_c, 0, CONV_SHAPE), MAKE(F32P, U8, 0, conv_f32d_to_u8_shaped_c, 0, CONV_SHAPE),
MAKE(F32P, U8, 0, conv_f32d_to_u8_dither_c, 0, CONV_DITHER), MAKE(F32P, U8, 0, conv_f32d_to_u8_noise_c, 0, CONV_NOISE),
MAKE(F32P, U8, 0, conv_f32d_to_u8_c), MAKE(F32P, U8, 0, conv_f32d_to_u8_c),
MAKE(F32, S8, 0, conv_f32_to_s8_c), MAKE(F32, S8, 0, conv_f32_to_s8_c),
MAKE(F32P, S8P, 0, conv_f32d_to_s8d_shaped_c, 0, CONV_SHAPE), MAKE(F32P, S8P, 0, conv_f32d_to_s8d_shaped_c, 0, CONV_SHAPE),
MAKE(F32P, S8P, 0, conv_f32d_to_s8d_dither_c, 0, CONV_DITHER), MAKE(F32P, S8P, 0, conv_f32d_to_s8d_noise_c, 0, CONV_NOISE),
MAKE(F32P, S8P, 0, conv_f32d_to_s8d_c), MAKE(F32P, S8P, 0, conv_f32d_to_s8d_c),
MAKE(F32, S8P, 0, conv_f32_to_s8d_c), MAKE(F32, S8P, 0, conv_f32_to_s8d_c),
MAKE(F32P, S8, 0, conv_f32d_to_s8_shaped_c, 0, CONV_SHAPE), MAKE(F32P, S8, 0, conv_f32d_to_s8_shaped_c, 0, CONV_SHAPE),
MAKE(F32P, S8, 0, conv_f32d_to_s8_dither_c, 0, CONV_DITHER), MAKE(F32P, S8, 0, conv_f32d_to_s8_noise_c, 0, CONV_NOISE),
MAKE(F32P, S8, 0, conv_f32d_to_s8_c), MAKE(F32P, S8, 0, conv_f32d_to_s8_c),
MAKE(F32P, ALAW, 0, conv_f32d_to_alaw_c), MAKE(F32P, ALAW, 0, conv_f32d_to_alaw_c),
@ -200,9 +200,9 @@ static struct conv_info conv_table[] =
MAKE(F32P, S16P, 0, conv_f32d_to_s16d_shaped_c, 0, CONV_SHAPE), MAKE(F32P, S16P, 0, conv_f32d_to_s16d_shaped_c, 0, CONV_SHAPE),
#if defined (HAVE_SSE2) #if defined (HAVE_SSE2)
MAKE(F32P, S16P, 0, conv_f32d_to_s16d_dither_sse2, SPA_CPU_FLAG_SSE2, CONV_DITHER), MAKE(F32P, S16P, 0, conv_f32d_to_s16d_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE),
#endif #endif
MAKE(F32P, S16P, 0, conv_f32d_to_s16d_dither_c, 0, CONV_DITHER), MAKE(F32P, S16P, 0, conv_f32d_to_s16d_noise_c, 0, CONV_NOISE),
#if defined (HAVE_SSE2) #if defined (HAVE_SSE2)
MAKE(F32P, S16P, 0, conv_f32d_to_s16d_sse2, SPA_CPU_FLAG_SSE2), MAKE(F32P, S16P, 0, conv_f32d_to_s16d_sse2, SPA_CPU_FLAG_SSE2),
#endif #endif
@ -212,9 +212,9 @@ static struct conv_info conv_table[] =
MAKE(F32P, S16, 0, conv_f32d_to_s16_shaped_c, 0, CONV_SHAPE), MAKE(F32P, S16, 0, conv_f32d_to_s16_shaped_c, 0, CONV_SHAPE),
#if defined (HAVE_SSE2) #if defined (HAVE_SSE2)
MAKE(F32P, S16, 0, conv_f32d_to_s16_dither_sse2, SPA_CPU_FLAG_SSE2, CONV_DITHER), MAKE(F32P, S16, 0, conv_f32d_to_s16_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE),
#endif #endif
MAKE(F32P, S16, 0, conv_f32d_to_s16_dither_c, 0, CONV_DITHER), MAKE(F32P, S16, 0, conv_f32d_to_s16_noise_c, 0, CONV_NOISE),
#if defined (HAVE_NEON) #if defined (HAVE_NEON)
MAKE(F32P, S16, 0, conv_f32d_to_s16_neon, SPA_CPU_FLAG_NEON), MAKE(F32P, S16, 0, conv_f32d_to_s16_neon, SPA_CPU_FLAG_NEON),
#endif #endif
@ -230,21 +230,21 @@ static struct conv_info conv_table[] =
MAKE(F32P, S16, 0, conv_f32d_to_s16_c), MAKE(F32P, S16, 0, conv_f32d_to_s16_c),
MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_shaped_c, 0, CONV_SHAPE), MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_shaped_c, 0, CONV_SHAPE),
MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_dither_c, 0, CONV_DITHER), MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_noise_c, 0, CONV_NOISE),
MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_c), MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_c),
MAKE(F32, U32, 0, conv_f32_to_u32_c), MAKE(F32, U32, 0, conv_f32_to_u32_c),
MAKE(F32P, U32, 0, conv_f32d_to_u32_c), MAKE(F32P, U32, 0, conv_f32d_to_u32_c),
MAKE(F32, S32, 0, conv_f32_to_s32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S32P, 0, conv_f32d_to_s32d_c), MAKE(F32P, S32P, 0, conv_f32d_to_s32d_c),
MAKE(F32, S32P, 0, conv_f32_to_s32d_c), MAKE(F32, S32P, 0, conv_f32_to_s32d_c),
#if defined (HAVE_SSE2) #if defined (HAVE_SSE2)
MAKE(F32P, S32, 0, conv_f32d_to_s32_dither_sse2, SPA_CPU_FLAG_SSE2, CONV_DITHER), MAKE(F32P, S32, 0, conv_f32d_to_s32_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE),
#endif #endif
MAKE(F32P, S32, 0, conv_f32d_to_s32_dither_c, 0, CONV_DITHER), MAKE(F32P, S32, 0, conv_f32d_to_s32_noise_c, 0, CONV_NOISE),
#if defined (HAVE_AVX2) #if defined (HAVE_AVX2)
MAKE(F32P, S32, 0, conv_f32d_to_s32_avx2, SPA_CPU_FLAG_AVX2), MAKE(F32P, S32, 0, conv_f32d_to_s32_avx2, SPA_CPU_FLAG_AVX2),
@ -254,33 +254,33 @@ static struct conv_info conv_table[] =
#endif #endif
MAKE(F32P, S32, 0, conv_f32d_to_s32_c), MAKE(F32P, S32, 0, conv_f32d_to_s32_c),
MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_dither_c, 0, CONV_DITHER), MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_noise_c, 0, CONV_NOISE),
MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_c), MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_c),
MAKE(F32, U24, 0, conv_f32_to_u24_c), MAKE(F32, U24, 0, conv_f32_to_u24_c),
MAKE(F32P, U24, 0, conv_f32d_to_u24_c), MAKE(F32P, U24, 0, conv_f32d_to_u24_c),
MAKE(F32, S24, 0, conv_f32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24P, 0, conv_f32d_to_s24d_c), MAKE(F32P, S24P, 0, conv_f32d_to_s24d_c),
MAKE(F32, S24P, 0, conv_f32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24, 0, conv_f32d_to_s24_c), MAKE(F32P, S24, 0, conv_f32d_to_s24_c),
MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_dither_c, 0, CONV_DITHER), MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_c), MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_c),
MAKE(F32, U24_32, 0, conv_f32_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), MAKE(F32P, U24_32, 0, conv_f32d_to_u24_32_c),
MAKE(F32, S24_32, 0, conv_f32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_c), MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_c),
MAKE(F32, S24_32P, 0, conv_f32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_c), MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_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_noise_c, 0, CONV_NOISE),
MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_c), MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_c),
MAKE(F32, F64, 0, conv_f32_to_f64_c), MAKE(F32, F64, 0, conv_f32_to_f64_c),
@ -356,7 +356,7 @@ static struct conv_info conv_table[] =
#define MATCH_DITHER(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, 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 dither_flags) uint32_t n_channels, uint32_t cpu_flags, uint32_t conv_flags)
{ {
size_t i; size_t i;
@ -365,7 +365,7 @@ static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt
conv_table[i].dst_fmt == dst_fmt && conv_table[i].dst_fmt == dst_fmt &&
MATCH_CHAN(conv_table[i].n_channels, n_channels) && 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)) MATCH_DITHER(conv_table[i].conv_flags, conv_flags))
return &conv_table[i]; return &conv_table[i];
} }
return NULL; return NULL;
@ -374,8 +374,8 @@ static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt
static void impl_convert_free(struct convert *conv) static void impl_convert_free(struct convert *conv)
{ {
conv->process = NULL; conv->process = NULL;
free(conv->dither); free(conv->noise);
conv->dither = NULL; conv->noise = NULL;
} }
static bool need_dither(uint32_t format) static bool need_dither(uint32_t format)
@ -395,43 +395,99 @@ static bool need_dither(uint32_t format)
return false; return false;
} }
/* filters based on F-weighted curves
* from 'Psychoacoustically Optimal Noise Shaping' (**)
* this filter is the "F-Weighted" noise filter described by Wannamaker
* It is designed to produce minimum audibility: */
static const float wan3[] = { /* Table 3; 3 Coefficients */
1.623f, -0.982f, 0.109f
};
/* Noise shaping coefficients from[1], moves most power of the
* error noise into inaudible frequency ranges.
*
* [1]
* "Minimally Audible Noise Shaping", Stanley P. Lipshitz,
* John Vanderkooy, and Robert A. Wannamaker,
* J. Audio Eng. Soc., Vol. 39, No. 11, November 1991. */
static const float lips44[] = { /* improved E-weighted (appendix: 5) */
2.033f, -2.165f, 1.959f, -1.590f, 0.6149f
};
static const struct dither_info {
uint32_t method;
uint32_t noise_method;
uint32_t rate;
const float *ns;
uint32_t n_ns;
} dither_info[] = {
{ DITHER_METHOD_NONE, NOISE_METHOD_NONE, },
{ DITHER_METHOD_RECTANGULAR, NOISE_METHOD_RECTANGULAR, },
{ DITHER_METHOD_TRIANGULAR, NOISE_METHOD_TRIANGULAR, },
{ DITHER_METHOD_TRIANGULAR_HF, NOISE_METHOD_TRIANGULAR_HF, },
{ DITHER_METHOD_WANAMAKER_3, NOISE_METHOD_TRIANGULAR_HF, 44100, wan3, SPA_N_ELEMENTS(wan3) },
{ DITHER_METHOD_LIPSHITZ, NOISE_METHOD_TRIANGULAR, 44100, lips44, SPA_N_ELEMENTS(lips44) }
};
static const struct dither_info *find_dither_info(uint32_t method, uint32_t rate)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(dither_info); i++) {
const struct dither_info *di = &dither_info[i];
if (di->method != method)
continue;
/* don't use shaped for too low rates, it moves the noise to
* audible ranges */
if (di->ns != NULL && rate < di->rate * 3 / 4)
return find_dither_info(DITHER_METHOD_TRIANGULAR_HF, rate);
return &dither_info[i];
}
return NULL;
}
int convert_init(struct convert *conv) int convert_init(struct convert *conv)
{ {
const struct conv_info *info; const struct conv_info *info;
uint32_t i, dither_flags; const struct dither_info *dinfo;
uint32_t i, conv_flags;
conv->scale = 1.0f / (float)(INT32_MAX); conv->scale = 1.0f / (float)(INT32_MAX);
if (conv->noise > 0) if (conv->noise_bits > 0)
conv->scale *= (1 << (conv->noise + 1)); conv->scale *= (1 << (conv->noise_bits + 1));
/* disable dither if not needed */ /* disable dither if not needed */
if (!need_dither(conv->dst_fmt)) if (!need_dither(conv->dst_fmt))
conv->method = DITHER_METHOD_NONE; conv->method = DITHER_METHOD_NONE;
/* don't use shaped for too low rates, it moves the noise to dinfo = find_dither_info(conv->method, conv->rate);
* audible ranges */ if (dinfo == NULL)
if (conv->method == DITHER_METHOD_SHAPED_5 && conv->rate < 32000) return -EINVAL;
conv->method = DITHER_METHOD_TRIANGULAR;
if (conv->method < DITHER_METHOD_TRIANGULAR) conv->noise_method = dinfo->noise_method;
if (conv->noise_bits && conv->noise_method == NOISE_METHOD_NONE)
conv->noise_method = NOISE_METHOD_RECTANGULAR;
if (conv->noise_method < NOISE_METHOD_TRIANGULAR)
conv->scale *= 0.5f; conv->scale *= 0.5f;
dither_flags = 0; conv_flags = 0;
if (conv->method != DITHER_METHOD_NONE || conv->noise) if (conv->noise_method != NOISE_METHOD_NONE)
dither_flags |= CONV_DITHER; conv_flags |= CONV_NOISE;
if (conv->method == DITHER_METHOD_SHAPED_5) if (dinfo->n_ns > 0) {
dither_flags |= CONV_SHAPE; conv_flags |= CONV_SHAPE;
conv->n_ns = dinfo->n_ns;
conv->ns = dinfo->ns;
}
info = find_conv_info(conv->src_fmt, conv->dst_fmt, conv->n_channels, info = find_conv_info(conv->src_fmt, conv->dst_fmt, conv->n_channels,
conv->cpu_flags, dither_flags); conv->cpu_flags, conv_flags);
if (info == NULL) if (info == NULL)
return -ENOTSUP; return -ENOTSUP;
conv->dither_size = DITHER_SIZE; conv->noise_size = DITHER_SIZE;
conv->dither = calloc(conv->dither_size + 16 + conv->noise = calloc(conv->noise_size + 16 +
FMT_OPS_MAX_ALIGN / sizeof(float), sizeof(float)); FMT_OPS_MAX_ALIGN / sizeof(float), sizeof(float));
if (conv->dither == NULL) if (conv->noise == NULL)
return -errno; return -errno;
for (i = 0; i < SPA_N_ELEMENTS(conv->random); i++) for (i = 0; i < SPA_N_ELEMENTS(conv->random); i++)

View file

@ -39,8 +39,8 @@
#define ITOF(type,v,scale,offs) \ #define ITOF(type,v,scale,offs) \
(((type)(v)) * (1.0f / (scale)) - (offs)) (((type)(v)) * (1.0f / (scale)) - (offs))
#define FTOI(type,v,scale,offs,dither,min,max) \ #define FTOI(type,v,scale,offs,noise,min,max) \
(type)f32_round(SPA_CLAMP((v) * (scale) + (offs) + (dither), min, max)) (type)f32_round(SPA_CLAMP((v) * (scale) + (offs) + (noise), min, max))
#define FMT_OPS_MAX_ALIGN 32 #define FMT_OPS_MAX_ALIGN 32
@ -202,17 +202,19 @@ static inline int24_t bswap_s24(int24_t src)
#define NS_MASK (NS_MAX-1) #define NS_MASK (NS_MAX-1)
struct shaper { struct shaper {
float e[NS_MAX]; float e[NS_MAX * 2];
uint32_t idx; uint32_t idx;
float r; float r;
}; };
struct convert { struct convert {
uint32_t noise; uint32_t noise_bits;
#define DITHER_METHOD_NONE 0 #define DITHER_METHOD_NONE 0
#define DITHER_METHOD_RECTANGULAR 1 #define DITHER_METHOD_RECTANGULAR 1
#define DITHER_METHOD_TRIANGULAR 2 #define DITHER_METHOD_TRIANGULAR 2
#define DITHER_METHOD_SHAPED_5 3 #define DITHER_METHOD_TRIANGULAR_HF 3
#define DITHER_METHOD_WANAMAKER_3 4
#define DITHER_METHOD_LIPSHITZ 5
uint32_t method; uint32_t method;
uint32_t src_fmt; uint32_t src_fmt;
@ -226,8 +228,16 @@ struct convert {
float scale; float scale;
uint32_t random[16 + FMT_OPS_MAX_ALIGN/4]; uint32_t random[16 + FMT_OPS_MAX_ALIGN/4];
float *dither; int32_t prev[16 + FMT_OPS_MAX_ALIGN/4];
uint32_t dither_size; #define NOISE_METHOD_NONE 0
#define NOISE_METHOD_RECTANGULAR 1
#define NOISE_METHOD_TRIANGULAR 2
#define NOISE_METHOD_TRIANGULAR_HF 3
uint32_t noise_method;
float *noise;
uint32_t noise_size;
const float *ns;
uint32_t n_ns;
struct shaper shaper[64]; struct shaper shaper[64];
void (*process) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], void (*process) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[],
@ -238,14 +248,22 @@ struct convert {
int convert_init(struct convert *conv); int convert_init(struct convert *conv);
static const struct dither_method_info { static const struct dither_method_info {
uint32_t method;
const char *label; const char *label;
const char *description; const char *description;
uint32_t method;
} dither_method_info[] = { } dither_method_info[] = {
[DITHER_METHOD_NONE] = { "none", "Disabled", DITHER_METHOD_NONE }, [DITHER_METHOD_NONE] = { DITHER_METHOD_NONE,
[DITHER_METHOD_RECTANGULAR] = { "rectangular", "Rectangular dithering", DITHER_METHOD_RECTANGULAR }, "none", "Disabled", },
[DITHER_METHOD_TRIANGULAR] = { "triangular", "Triangular dithering", DITHER_METHOD_TRIANGULAR }, [DITHER_METHOD_RECTANGULAR] = { DITHER_METHOD_RECTANGULAR,
[DITHER_METHOD_SHAPED_5] = { "shaped5", "Shaped 5 dithering", DITHER_METHOD_SHAPED_5 } "rectangular", "Rectangular dithering", },
[DITHER_METHOD_TRIANGULAR] = { DITHER_METHOD_TRIANGULAR,
"triangular", "Triangular dithering", },
[DITHER_METHOD_TRIANGULAR_HF] = { DITHER_METHOD_TRIANGULAR_HF,
"triangular-hf", "Sloped Triangular dithering", },
[DITHER_METHOD_WANAMAKER_3] = { DITHER_METHOD_WANAMAKER_3,
"wanamaker3", "Wanamaker 3 dithering", },
[DITHER_METHOD_LIPSHITZ] = { DITHER_METHOD_LIPSHITZ,
"shaped5", "Lipshitz 5 dithering", },
}; };
static inline uint32_t dither_method_from_label(const char *label) static inline uint32_t dither_method_from_label(const char *label)
@ -319,66 +337,66 @@ DEFINE_FUNCTION(f64_to_f32d, c);
DEFINE_FUNCTION(f64s_to_f32d, c); DEFINE_FUNCTION(f64s_to_f32d, c);
DEFINE_FUNCTION(f64d_to_f32, c); DEFINE_FUNCTION(f64d_to_f32, c);
DEFINE_FUNCTION(f32d_to_u8d, c); DEFINE_FUNCTION(f32d_to_u8d, c);
DEFINE_FUNCTION(f32d_to_u8d_dither, c); DEFINE_FUNCTION(f32d_to_u8d_noise, c);
DEFINE_FUNCTION(f32d_to_u8d_shaped, c); DEFINE_FUNCTION(f32d_to_u8d_shaped, c);
DEFINE_FUNCTION(f32_to_u8, c); DEFINE_FUNCTION(f32_to_u8, c);
DEFINE_FUNCTION(f32_to_u8d, c); DEFINE_FUNCTION(f32_to_u8d, c);
DEFINE_FUNCTION(f32d_to_u8, c); DEFINE_FUNCTION(f32d_to_u8, c);
DEFINE_FUNCTION(f32d_to_u8_dither, c); DEFINE_FUNCTION(f32d_to_u8_noise, c);
DEFINE_FUNCTION(f32d_to_u8_shaped, c); DEFINE_FUNCTION(f32d_to_u8_shaped, c);
DEFINE_FUNCTION(f32d_to_s8d, c); DEFINE_FUNCTION(f32d_to_s8d, c);
DEFINE_FUNCTION(f32d_to_s8d_dither, c); DEFINE_FUNCTION(f32d_to_s8d_noise, c);
DEFINE_FUNCTION(f32d_to_s8d_shaped, c); DEFINE_FUNCTION(f32d_to_s8d_shaped, c);
DEFINE_FUNCTION(f32_to_s8, c); DEFINE_FUNCTION(f32_to_s8, c);
DEFINE_FUNCTION(f32_to_s8d, c); DEFINE_FUNCTION(f32_to_s8d, c);
DEFINE_FUNCTION(f32d_to_s8, c); DEFINE_FUNCTION(f32d_to_s8, c);
DEFINE_FUNCTION(f32d_to_s8_dither, c); DEFINE_FUNCTION(f32d_to_s8_noise, c);
DEFINE_FUNCTION(f32d_to_s8_shaped, c); DEFINE_FUNCTION(f32d_to_s8_shaped, c);
DEFINE_FUNCTION(f32d_to_alaw, c); DEFINE_FUNCTION(f32d_to_alaw, c);
DEFINE_FUNCTION(f32d_to_ulaw, c); DEFINE_FUNCTION(f32d_to_ulaw, c);
DEFINE_FUNCTION(f32_to_u16, c); DEFINE_FUNCTION(f32_to_u16, c);
DEFINE_FUNCTION(f32d_to_u16, c); DEFINE_FUNCTION(f32d_to_u16, c);
DEFINE_FUNCTION(f32d_to_s16d, c); DEFINE_FUNCTION(f32d_to_s16d, c);
DEFINE_FUNCTION(f32d_to_s16d_dither, c); DEFINE_FUNCTION(f32d_to_s16d_noise, c);
DEFINE_FUNCTION(f32d_to_s16d_shaped, c); DEFINE_FUNCTION(f32d_to_s16d_shaped, c);
DEFINE_FUNCTION(f32_to_s16, c); DEFINE_FUNCTION(f32_to_s16, c);
DEFINE_FUNCTION(f32_to_s16d, c); DEFINE_FUNCTION(f32_to_s16d, c);
DEFINE_FUNCTION(f32d_to_s16, c); DEFINE_FUNCTION(f32d_to_s16, c);
DEFINE_FUNCTION(f32d_to_s16_dither, c); DEFINE_FUNCTION(f32d_to_s16_noise, c);
DEFINE_FUNCTION(f32d_to_s16_shaped, c); DEFINE_FUNCTION(f32d_to_s16_shaped, c);
DEFINE_FUNCTION(f32d_to_s16s, c); DEFINE_FUNCTION(f32d_to_s16s, c);
DEFINE_FUNCTION(f32d_to_s16s_dither, c); DEFINE_FUNCTION(f32d_to_s16s_noise, c);
DEFINE_FUNCTION(f32d_to_s16s_shaped, c); DEFINE_FUNCTION(f32d_to_s16s_shaped, c);
DEFINE_FUNCTION(f32_to_u32, c); DEFINE_FUNCTION(f32_to_u32, c);
DEFINE_FUNCTION(f32d_to_u32, c); DEFINE_FUNCTION(f32d_to_u32, c);
DEFINE_FUNCTION(f32d_to_s32d, c); DEFINE_FUNCTION(f32d_to_s32d, c);
DEFINE_FUNCTION(f32d_to_s32d_dither, c); DEFINE_FUNCTION(f32d_to_s32d_noise, c);
DEFINE_FUNCTION(f32_to_s32, c); DEFINE_FUNCTION(f32_to_s32, c);
DEFINE_FUNCTION(f32_to_s32d, c); DEFINE_FUNCTION(f32_to_s32d, c);
DEFINE_FUNCTION(f32d_to_s32, c); DEFINE_FUNCTION(f32d_to_s32, c);
DEFINE_FUNCTION(f32d_to_s32_dither, c); DEFINE_FUNCTION(f32d_to_s32_noise, c);
DEFINE_FUNCTION(f32d_to_s32s, c); DEFINE_FUNCTION(f32d_to_s32s, c);
DEFINE_FUNCTION(f32d_to_s32s_dither, c); DEFINE_FUNCTION(f32d_to_s32s_noise, c);
DEFINE_FUNCTION(f32_to_u24, c); DEFINE_FUNCTION(f32_to_u24, c);
DEFINE_FUNCTION(f32d_to_u24, c); DEFINE_FUNCTION(f32d_to_u24, c);
DEFINE_FUNCTION(f32d_to_s24d, c); DEFINE_FUNCTION(f32d_to_s24d, c);
DEFINE_FUNCTION(f32d_to_s24d_dither, c); DEFINE_FUNCTION(f32d_to_s24d_noise, c);
DEFINE_FUNCTION(f32_to_s24, c); DEFINE_FUNCTION(f32_to_s24, c);
DEFINE_FUNCTION(f32_to_s24d, c); DEFINE_FUNCTION(f32_to_s24d, c);
DEFINE_FUNCTION(f32d_to_s24, c); DEFINE_FUNCTION(f32d_to_s24, c);
DEFINE_FUNCTION(f32d_to_s24_dither, c); DEFINE_FUNCTION(f32d_to_s24_noise, c);
DEFINE_FUNCTION(f32d_to_s24s, c); DEFINE_FUNCTION(f32d_to_s24s, c);
DEFINE_FUNCTION(f32d_to_s24s_dither, c); DEFINE_FUNCTION(f32d_to_s24s_noise, c);
DEFINE_FUNCTION(f32_to_u24_32, c); DEFINE_FUNCTION(f32_to_u24_32, c);
DEFINE_FUNCTION(f32d_to_u24_32, c); DEFINE_FUNCTION(f32d_to_u24_32, c);
DEFINE_FUNCTION(f32d_to_s24_32d, c); DEFINE_FUNCTION(f32d_to_s24_32d, c);
DEFINE_FUNCTION(f32d_to_s24_32d_dither, c); DEFINE_FUNCTION(f32d_to_s24_32d_noise, c);
DEFINE_FUNCTION(f32_to_s24_32, c); DEFINE_FUNCTION(f32_to_s24_32, c);
DEFINE_FUNCTION(f32_to_s24_32d, c); DEFINE_FUNCTION(f32_to_s24_32d, c);
DEFINE_FUNCTION(f32d_to_s24_32, c); DEFINE_FUNCTION(f32d_to_s24_32, c);
DEFINE_FUNCTION(f32d_to_s24_32_dither, c); DEFINE_FUNCTION(f32d_to_s24_32_noise, c);
DEFINE_FUNCTION(f32d_to_s24_32s, c); DEFINE_FUNCTION(f32d_to_s24_32s, c);
DEFINE_FUNCTION(f32d_to_s24_32s_dither, c); DEFINE_FUNCTION(f32d_to_s24_32s_noise, c);
DEFINE_FUNCTION(f32d_to_f64d, c); DEFINE_FUNCTION(f32d_to_f64d, c);
DEFINE_FUNCTION(f32_to_f64, c); DEFINE_FUNCTION(f32_to_f64, c);
DEFINE_FUNCTION(f32_to_f64d, c); DEFINE_FUNCTION(f32_to_f64d, c);
@ -410,13 +428,13 @@ DEFINE_FUNCTION(s16_to_f32d, sse2);
DEFINE_FUNCTION(s24_to_f32d, sse2); DEFINE_FUNCTION(s24_to_f32d, sse2);
DEFINE_FUNCTION(s32_to_f32d, sse2); DEFINE_FUNCTION(s32_to_f32d, sse2);
DEFINE_FUNCTION(f32d_to_s32, sse2); DEFINE_FUNCTION(f32d_to_s32, sse2);
DEFINE_FUNCTION(f32d_to_s32_dither, sse2); DEFINE_FUNCTION(f32d_to_s32_noise, sse2);
DEFINE_FUNCTION(f32_to_s16, sse2); DEFINE_FUNCTION(f32_to_s16, sse2);
DEFINE_FUNCTION(f32d_to_s16_2, sse2); DEFINE_FUNCTION(f32d_to_s16_2, sse2);
DEFINE_FUNCTION(f32d_to_s16, sse2); DEFINE_FUNCTION(f32d_to_s16, sse2);
DEFINE_FUNCTION(f32d_to_s16_dither, sse2); DEFINE_FUNCTION(f32d_to_s16_noise, sse2);
DEFINE_FUNCTION(f32d_to_s16d, sse2); DEFINE_FUNCTION(f32d_to_s16d, sse2);
DEFINE_FUNCTION(f32d_to_s16d_dither, sse2); DEFINE_FUNCTION(f32d_to_s16d_noise, sse2);
DEFINE_FUNCTION(32_to_32d, sse2); DEFINE_FUNCTION(32_to_32d, sse2);
DEFINE_FUNCTION(32s_to_32d, sse2); DEFINE_FUNCTION(32s_to_32d, sse2);
DEFINE_FUNCTION(32d_to_32, sse2); DEFINE_FUNCTION(32d_to_32, sse2);

View file

@ -656,7 +656,7 @@ static void run_test_noise(uint32_t fmt, uint32_t noise, uint32_t flags)
spa_zero(conv); spa_zero(conv);
conv.noise = noise; conv.noise_bits = noise;
conv.src_fmt = SPA_AUDIO_FORMAT_F32P; conv.src_fmt = SPA_AUDIO_FORMAT_F32P;
conv.dst_fmt = fmt; conv.dst_fmt = fmt;
conv.n_channels = 2; conv.n_channels = 2;
@ -672,7 +672,7 @@ static void run_test_noise(uint32_t fmt, uint32_t noise, uint32_t flags)
} }
convert_process(&conv, op, ip, N_SAMPLES); convert_process(&conv, op, ip, N_SAMPLES);
range = 1 << conv.noise; range = 1 << conv.noise_bits;
all_zero = true; all_zero = true;
for (i = 0; i < conv.n_channels * N_SAMPLES; i++) { for (i = 0; i < conv.n_channels * N_SAMPLES; i++) {