diff --git a/spa/include/spa/utils/defs.h b/spa/include/spa/utils/defs.h index eb9b9917e..fb1e3ddb7 100644 --- a/spa/include/spa/utils/defs.h +++ b/spa/include/spa/utils/defs.h @@ -171,7 +171,8 @@ struct spa_param_info { #define SPA_ROUND_DOWN_N(num,align) ((num) & ~((align) - 1)) #define SPA_ROUND_UP_N(num,align) SPA_ROUND_DOWN_N((num) + ((align) - 1),align) -#define SPA_IS_ALIGNED(p,align) (((intptr_t)(p) & ((align)-1)) == 0) +#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1)) +#define SPA_IS_ALIGNED(p,align) (SPA_PTR_ALIGNMENT(p,align) == 0) #define SPA_PTR_ALIGN(p,align,type) (type*)SPA_ROUND_UP_N((intptr_t)(p), (intptr_t)(align)) #ifndef SPA_LIKELY diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build index 93d76a446..e323a2870 100644 --- a/spa/plugins/audioconvert/meson.build +++ b/spa/plugins/audioconvert/meson.build @@ -15,6 +15,7 @@ audioconvertlib = shared_library('spa-audioconvert', test_apps = [ 'test-fmt-ops', + 'test-resample', ] foreach a : test_apps diff --git a/spa/plugins/audioconvert/resample-native-c.h b/spa/plugins/audioconvert/resample-native-c.h new file mode 100644 index 000000000..c2ff6d289 --- /dev/null +++ b/spa/plugins/audioconvert/resample-native-c.h @@ -0,0 +1,51 @@ +/* Spa + * + * Copyright © 2019 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. + */ + +static void inner_product_c(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT taps, uint32_t n_taps) +{ + float sum = 0.0f; + uint32_t i; + + for (i = 0; i < n_taps; i++) + sum += s[i] * taps[i]; + *d = sum; +} + +static void inner_product_ip_c(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, + uint32_t n_taps) +{ + float sum[2] = { 0.0f, 0.0f }; + uint32_t i; + + for (i = 0; i < n_taps; i++) { + sum[0] += s[i] * t0[i]; + sum[1] += s[i] * t1[i]; + } + *d = (sum[1] - sum[0]) * x + sum[0]; +} + +MAKE_RESAMPLER_FULL(c); +MAKE_RESAMPLER_INTER(c); diff --git a/spa/plugins/audioconvert/resample-native-impl.h b/spa/plugins/audioconvert/resample-native-impl.h new file mode 100644 index 000000000..1a25c2909 --- /dev/null +++ b/spa/plugins/audioconvert/resample-native-impl.h @@ -0,0 +1,112 @@ +/* Spa + * + * Copyright © 2019 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. + */ + +#define MAKE_RESAMPLER_FULL(arch) \ +static void do_resample_full_##arch(struct resample *r, \ + const void * SPA_RESTRICT src[], uint32_t *in_len, \ + void * SPA_RESTRICT dst[], uint32_t offs, uint32_t *out_len) \ +{ \ + struct native_data *data = r->data; \ + uint32_t index, phase; \ + uint32_t out_rate = data->out_rate; \ + uint32_t n_taps = data->n_taps; \ + uint32_t c, o, olen = *out_len, ilen = *in_len; \ + \ + if (r->channels == 0) \ + return; \ + \ + for (c = 0; c < r->channels; c++) { \ + const float *s = src[c]; \ + float *d = dst[c]; \ + \ + index = data->index; \ + phase = data->phase; \ + \ + for (o = offs; o < olen && index + n_taps <= ilen; o++) { \ + const float *ip, *taps; \ + \ + ip = &s[index]; \ + taps = &data->filter[phase * n_taps]; \ + index += data->inc; \ + phase += data->frac; \ + if (phase >= out_rate) { \ + phase -= out_rate; \ + index += 1; \ + } \ + inner_product_##arch(&d[o], ip, taps, n_taps); \ + } \ + } \ + *in_len = index - data->index; \ + *out_len = o; \ + data->index = index; \ + data->phase = phase; \ +} + +#define MAKE_RESAMPLER_INTER(arch) \ +static void do_resample_inter_##arch(struct resample *r, \ + const void * SPA_RESTRICT src[], uint32_t *in_len, \ + void * SPA_RESTRICT dst[], uint32_t offs, uint32_t *out_len) \ +{ \ + struct native_data *data = r->data; \ + uint32_t index, phase; \ + uint32_t n_phases = data->n_phases, out_rate = data->out_rate; \ + uint32_t n_taps = data->n_taps; \ + uint32_t c, o, olen = *out_len, ilen = *in_len; \ + \ + if (r->channels == 0) \ + return; \ + \ + for (c = 0; c < r->channels; c++) { \ + const float *s = src[c]; \ + float *d = dst[c]; \ + \ + index = data->index; \ + phase = data->phase; \ + \ + for (o = offs; o < olen && index + n_taps <= ilen; o++) { \ + const float *ip; \ + float ph, x, *t0, *t1; \ + uint32_t offset; \ + \ + ip = &s[index]; \ + ph = (float)phase * n_phases / out_rate; \ + offset = floor(ph); \ + x = ph - (float)offset; \ + \ + t0 = &data->filter[(offset + 0) * n_taps]; \ + t1 = &data->filter[(offset + 1) * n_taps]; \ + index += data->inc; \ + phase += data->frac; \ + if (phase >= out_rate) { \ + phase -= out_rate; \ + index += 1; \ + } \ + inner_product_ip_##arch(&d[o], ip, t0, t1, x, n_taps); \ + } \ + } \ + *in_len = index - data->index; \ + *out_len = o; \ + data->index = index; \ + data->phase = phase; \ +} diff --git a/spa/plugins/audioconvert/resample-native-sse.h b/spa/plugins/audioconvert/resample-native-sse.h new file mode 100644 index 000000000..d52c14753 --- /dev/null +++ b/spa/plugins/audioconvert/resample-native-sse.h @@ -0,0 +1,74 @@ +/* Spa + * + * Copyright © 2019 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 + +#pragma GCC target("ssse3") +#include + +static void inner_product_sse(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT taps, uint32_t n_taps) +{ + __m128 sum = _mm_setzero_ps(); + uint32_t i; + + for (i = 0; i < n_taps; i += 8) { + sum = _mm_add_ps(sum, + _mm_mul_ps( + _mm_loadu_ps(s + i + 0), + _mm_load_ps(taps + i + 0))); + sum = _mm_add_ps(sum, + _mm_mul_ps( + _mm_loadu_ps(s + i + 4), + _mm_load_ps(taps + i + 4))); + } + sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum)); + sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55)); + _mm_store_ss(d, sum); +} + +static void inner_product_ip_sse(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, + uint32_t n_taps) +{ + __m128 sum[2] = { _mm_setzero_ps (), _mm_setzero_ps () }, t; + uint32_t i; + + for (i = 0; i < n_taps; i += 8) { + t = _mm_loadu_ps(s + i + 0); + sum[0] = _mm_add_ps(sum[0], _mm_mul_ps(t, _mm_load_ps(t0 + i + 0))); + sum[1] = _mm_add_ps(sum[1], _mm_mul_ps(t, _mm_load_ps(t1 + i + 0))); + t = _mm_loadu_ps(s + i + 4); + sum[0] = _mm_add_ps(sum[0], _mm_mul_ps(t, _mm_load_ps(t0 + i + 4))); + sum[1] = _mm_add_ps(sum[1], _mm_mul_ps(t, _mm_load_ps(t1 + i + 4))); + } + sum[1] = _mm_mul_ps(_mm_sub_ps(sum[1], sum[0]), _mm_load1_ps(&x)); + sum[0] = _mm_add_ps(sum[0], sum[1]); + sum[0] = _mm_add_ps(sum[0], _mm_movehl_ps(sum[0], sum[0])); + sum[0] = _mm_add_ss(sum[0], _mm_shuffle_ps(sum[0], sum[0], 0x55)); + _mm_store_ss(d, sum[0]); +} + +MAKE_RESAMPLER_FULL(sse); +MAKE_RESAMPLER_INTER(sse); diff --git a/spa/plugins/audioconvert/resample-native-ssse3.h b/spa/plugins/audioconvert/resample-native-ssse3.h new file mode 100644 index 000000000..4812d891f --- /dev/null +++ b/spa/plugins/audioconvert/resample-native-ssse3.h @@ -0,0 +1,110 @@ +/* Spa + * + * Copyright © 2019 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 + +static void inner_product_ssse3(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT taps, uint32_t n_taps) +{ + __m128 sum = _mm_setzero_ps(); + __m128 t0, t1; + uint32_t i; + + switch (SPA_PTR_ALIGNMENT(s, 16)) { + case 0: + for (i = 0; i < n_taps; i += 8) { + sum = _mm_add_ps(sum, + _mm_mul_ps( + _mm_load_ps(s + i + 0), + _mm_load_ps(taps + i + 0))); + sum = _mm_add_ps(sum, + _mm_mul_ps( + _mm_load_ps(s + i + 4), + _mm_load_ps(taps + i + 4))); + } + break; + case 4: + t0 = _mm_load_ps(s - 1); + for (i = 0; i < n_taps; i += 8) { + t1 = _mm_load_ps(s + i + 3); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 4); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 0))); + t0 = t1; + t1 = _mm_load_ps(s + i + 7); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 4); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 4))); + t0 = t1; + } + break; + case 8: + t0 = _mm_load_ps(s - 2); + for (i = 0; i < n_taps; i += 8) { + t1 = _mm_load_ps(s + i + 2); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 8); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 0))); + t0 = t1; + t1 = _mm_load_ps(s + i + 6); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 8); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 4))); + t0 = t1; + } + break; + case 12: + t0 = _mm_load_ps(s - 3); + for (i = 0; i < n_taps; i += 8) { + t1 = _mm_load_ps(s + i + 1); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 12); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 0))); + t0 = t1; + t1 = _mm_load_ps(s + i + 5); + t0 = (__m128)_mm_alignr_epi8((__m128i)t1, (__m128i)t0, 12); + sum = _mm_add_ps(sum, + _mm_mul_ps(t0, _mm_load_ps(taps + i + 4))); + t0 = t1; + } + break; + } + sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum)); + sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55)); + _mm_store_ss(d, sum); +} + +static void inner_product_ip_ssse3(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, + uint32_t n_taps) +{ +#if defined (__SSE__) + inner_product_ip_sse(d, s, t0, t1, x, n_taps); +#else + inner_product_ip_c(d, s, t0, t1, x, n_taps); +#endif +} + +MAKE_RESAMPLER_FULL(ssse3); +MAKE_RESAMPLER_INTER(ssse3); diff --git a/spa/plugins/audioconvert/resample-native.h b/spa/plugins/audioconvert/resample-native.h new file mode 100644 index 000000000..4c5628f60 --- /dev/null +++ b/spa/plugins/audioconvert/resample-native.h @@ -0,0 +1,273 @@ +/* Spa + * + * Copyright © 2019 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 + +typedef void (*resample_func_t)(struct resample *r, + const void * SPA_RESTRICT src[], uint32_t *in_len, + void * SPA_RESTRICT dst[], uint32_t offs, uint32_t *out_len); + +struct native_data { + double rate; + uint32_t n_taps; + uint32_t n_phases; + uint32_t in_rate; + uint32_t out_rate; + uint32_t index; + uint32_t phase; + uint32_t inc; + uint32_t frac; + uint32_t hist; + float **history; + resample_func_t func; + float *filter; + float *hist_mem; +}; + +#include "resample-native-impl.h" +#include "resample-native-c.h" +#if defined (__SSE__) +#include "resample-native-sse.h" +#endif +#if defined (__SSSE3__) +#include "resample-native-ssse3.h" +#endif + +struct quality { + uint32_t n_taps; + double cutoff; +}; + +#define DEFAULT_QUALITY 4 + +static const struct quality blackman_qualities[] = { + { 8, 0.5, }, + { 16, 0.6, }, + { 24, 0.72, }, + { 32, 0.8, }, + { 48, 0.85, }, /* default */ + { 64, 0.90, }, + { 80, 0.92, }, + { 96, 0.933, }, + { 128, 0.950, }, + { 144, 0.955, }, + { 160, 0.960, } +}; + +static inline double sinc(double x) +{ + if (x < 1e-6) return 1.0; + x *= M_PI; + return sin(x) / x; +} + +static inline double blackman(double x, double n_taps) +{ + double w = 2.0 * x * M_PI / n_taps + M_PI; + return 0.3635819 - 0.4891775 * cos(w) + + 0.1365995 * cos(2 * w) - 0.0106411 * cos(3 * w); +} + +static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff) +{ + uint32_t i, j, n_taps12 = n_taps/2; + + for (i = 0; i <= n_phases; i++) { + double t = (double) i / (double) n_phases; + for (j = 0; j < n_taps12; j++, t += 1.0) + taps[i * stride + (n_taps12 - j - 1)] = + cutoff * sinc(t * cutoff) * blackman(t, n_taps); + } + for (i = 0; i <= n_phases; i++) { + for (j = n_taps12; j < n_taps; j++) + taps[i * stride + j] = taps[(n_phases - i) * stride + n_taps - j - 1]; + } + return 0; +} + +static void impl_native_free(struct resample *r) +{ + free(r->data); + r->data = NULL; +} + +static void impl_native_update_rate(struct resample *r, double rate) +{ +} + +static void impl_native_process(struct resample *r, + const void * SPA_RESTRICT src[], uint32_t *in_len, + void * SPA_RESTRICT dst[], uint32_t *out_len) +{ + struct native_data *data = r->data; + uint32_t n_taps = data->n_taps; + float **history = data->history; + const float **s; + uint32_t c, refill, hist, in, out, remain; + + out = refill = in = 0; + hist = data->hist; + + if (hist) { + if (hist < n_taps) { + refill = SPA_MIN(*in_len, n_taps); + for (c = 0; c < r->channels; c++) + memcpy(&history[c][hist], src[c], refill * sizeof(float)); + + if (hist + refill < n_taps) { + data->hist = hist + refill; + *in_len = refill; + *out_len = 0; + return; + } + } + in = hist + refill; + out = *out_len; + data->func(r, (const void**)history, &in, dst, 0, &out); + data->index -= hist; + } + + if (out < *out_len) { + in = *in_len; + s = (const float **)src; + data->func(r, src, &in, dst, out, out_len); + remain = *in_len - in; + } else { + s = (const float **)history; + remain = hist - in; + if (*in_len < n_taps) { + remain += refill; + *in_len = refill; + } else { + *in_len = 0; + } + } + if (remain < n_taps) { + for (c = 0; c < r->channels; c++) + memmove(history[c], &s[c][in], remain * sizeof(float)); + } else { + remain = 0; + *in_len = in; + } + data->index = 0; + data->hist = remain; + return; +} + +static void impl_native_reset (struct resample *r) +{ + struct native_data *d = r->data; + memset(d->hist_mem, 0, r->channels * sizeof(float) * d->n_taps * 2); + d->hist = d->n_taps / 2; + d->index = 0; + d->phase = 0; +} + +static uint32_t impl_native_delay (struct resample *r) +{ + struct native_data *d = r->data; + return d->n_taps; +} + +static inline uint32_t calc_gcd(uint32_t a, uint32_t b) +{ + while (b != 0) { + uint32_t temp = a; + a = b; + b = b % temp; + } + return a; +} + +static int impl_native_init(struct resample *r) +{ + struct native_data *d; + const struct quality *q = &blackman_qualities[DEFAULT_QUALITY]; + double scale; + uint32_t c, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, stride; + uint32_t history_stride, history_size; + + r->free = impl_native_free; + r->update_rate = impl_native_update_rate; + r->process = impl_native_process; + r->reset = impl_native_reset; + r->delay = impl_native_delay; + + gcd = calc_gcd(r->i_rate, r->o_rate); + + in_rate = r->i_rate /= gcd; + out_rate = r->o_rate /= gcd; + + scale = SPA_MIN(q->cutoff * r->o_rate / r->i_rate, 1.0); + n_taps = SPA_ROUND_UP_N((uint32_t)ceil(q->n_taps / scale), 8); + stride = n_taps * sizeof(float); + n_phases = out_rate; + + fprintf(stderr, "in %d out %d %d %d %d %f\n", in_rate, out_rate, gcd, n_taps, n_phases, scale); + + filter_size = stride * (n_phases + 1); + history_stride = 2 * n_taps * sizeof(float); + history_size = r->channels * history_stride; + + d = malloc(sizeof(struct native_data) + + filter_size + + history_size + + (r->channels * sizeof(float*)) + + 32); + + if (d == NULL) + return -ENOMEM; + + r->data = d; + d->rate = 1.0f; + d->filter = SPA_MEMBER(d, sizeof(struct native_data), float); + d->filter = SPA_PTR_ALIGN(d->filter, 16, float); + d->hist_mem = SPA_MEMBER(d->filter, filter_size, float); + d->hist_mem = SPA_PTR_ALIGN(d->hist_mem, 16, float); + d->history = SPA_MEMBER(d->hist_mem, history_size, float*); + for (c = 0; c < r->channels; c++) + d->history[c] = SPA_MEMBER(d->hist_mem, c * history_stride, float); + + d->in_rate = in_rate; + d->out_rate = out_rate; + d->inc = in_rate / out_rate; + d->frac = in_rate % out_rate; + d->n_taps = n_taps; + d->n_phases = n_phases; + + build_filter(d->filter, n_taps, n_taps, n_phases, scale); + + impl_native_reset (r); + + d->func = d->rate == 1.0f ? do_resample_full_c : do_resample_inter_c; +#if defined (__SSE__) + if (r->cpu_flags & SPA_CPU_FLAG_SSE) + d->func = d->rate == 1.0f ? do_resample_full_sse : do_resample_inter_sse; +#endif +#if defined (__SSSE3__) + if (r->cpu_flags & SPA_CPU_FLAG_SSSE3) + d->func = d->rate == 1.0f ? do_resample_full_ssse3 : do_resample_inter_ssse3; +#endif + return 0; +} diff --git a/spa/plugins/audioconvert/resample.c b/spa/plugins/audioconvert/resample.c index 0c930be5c..08f78ae76 100644 --- a/spa/plugins/audioconvert/resample.c +++ b/spa/plugins/audioconvert/resample.c @@ -40,6 +40,7 @@ #include "resample-speex.h" #include "resample-peaks.h" +#include "resample-native.h" #define NAME "resample" @@ -155,6 +156,8 @@ static int setup_convert(struct impl *this, if (this->monitor) err = impl_peaks_init(&this->resample); + else if (1) + err = impl_native_init(&this->resample); else err = impl_speex_init(&this->resample); diff --git a/spa/plugins/audioconvert/resample.h b/spa/plugins/audioconvert/resample.h index 96d738c7e..4f46e6d3d 100644 --- a/spa/plugins/audioconvert/resample.h +++ b/spa/plugins/audioconvert/resample.h @@ -30,12 +30,13 @@ struct resample { uint32_t i_rate; uint32_t o_rate; - void (*free) (struct resample *r); - void (*update_rate) (struct resample *r, double rate); - void (*process) (struct resample *r, - const void * SPA_RESTRICT src[], uint32_t *in_len, - void * SPA_RESTRICT dst[], uint32_t *out_len); - void (*reset) (struct resample *r); + void (*free) (struct resample *r); + void (*update_rate) (struct resample *r, double rate); + void (*process) (struct resample *r, + const void * SPA_RESTRICT src[], uint32_t *in_len, + void * SPA_RESTRICT dst[], uint32_t *out_len); + void (*reset) (struct resample *r); + uint32_t (*delay) (struct resample *r); void *data; }; @@ -43,3 +44,4 @@ struct resample { #define resample_update_rate(r,...) (r)->update_rate(r,__VA_ARGS__) #define resample_process(r,...) (r)->process(r,__VA_ARGS__) #define resample_reset(r) (r)->reset(r) +#define resample_delay(r) (r)->delay(r) diff --git a/spa/plugins/audioconvert/test-resample.c b/spa/plugins/audioconvert/test-resample.c new file mode 100644 index 000000000..0a82060d2 --- /dev/null +++ b/spa/plugins/audioconvert/test-resample.c @@ -0,0 +1,86 @@ +/* Spa + * + * Copyright © 2019 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 "resample.h" +#include "resample-native.h" + +#define N_SAMPLES 253 +#define N_CHANNELS 11 + +static float samp_in[N_SAMPLES * 4]; +static float samp_out[N_SAMPLES * 4]; + +static void feed_1(struct resample *r) +{ + uint32_t i; + const void *src[1]; + void *dst[1]; + + spa_zero(samp_out); + src[0] = samp_in; + dst[0] = samp_out; + + for (i = 0; i < 500; i++) { + uint32_t in, out; + + in = out = 1; + samp_in[0] = i; + resample_process(r, src, &in, dst, &out); + fprintf(stderr, "%d %d %f %d\n", i, in, samp_out[0], out); + } +} + +static void test_native(void) +{ + struct resample r; + + r.channels = 1; + r.i_rate = 44100; + r.o_rate = 44100; + + impl_native_init(&r); + + feed_1(&r); + + + + + +} + +int main(int argc, char *argv[]) +{ + test_native(); + + return 0; +}