/* Spa */ /* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ /* SPDX-License-Identifier: MIT */ #include #include #include #include #include #include #include #include #include #include SPA_LOG_IMPL(logger); #include "resample.h" static float samp_in[65536]; static float samp_out[65536]; static bool force_print; static void assert_test(bool check) { if (!check) fprintf(stderr, "FAIL\n\n"); #if 1 spa_assert_se(check); #endif } static double difference(double delay, const float *a, size_t len_a, const float *b, size_t len_b, double b_rate) { size_t i; float c = 0, wa = 0, wb = 0; /* Difference measure: sum((a-b)^2) / sqrt(sum a^2 sum b^2); restricted to overlap */ for (i = 0; i < len_a; ++i) { double jf = (i + delay) * b_rate; int j; double x; float bv; j = (int)floor(jf); if (j < 0 || j + 1 >= (int)len_b) continue; x = jf - j; bv = (float)((1 - x) * b[j] + x * b[j + 1]); c += (a[i] - bv) * (a[i] - bv); wa += a[i]*a[i]; wb = bv*bv; } if (wa == 0 || wb == 0) return 1e30; return c / sqrt(wa * wb); } struct find_delay_data { const float *a; const float *b; size_t len_a; size_t len_b; double b_rate; }; static double find_delay_func(double x, void *user_data) { const struct find_delay_data *data = user_data; return difference((float)x, data->a, data->len_a, data->b, data->len_b, data->b_rate); } static double minimum(double x1, double x4, double (*func)(double, void *), void *user_data, double tol) { /* Find minimum with golden section search */ const double phi = (1 + sqrt(5)) / 2; double x2, x3; double f2, f3; spa_assert(x4 >= x1); x2 = x4 - (x4 - x1) / phi; x3 = x1 + (x4 - x1) / phi; f2 = func(x2, user_data); f3 = func(x3, user_data); while (x4 - x1 > tol) { if (f2 > f3) { x1 = x2; x2 = x3; x3 = x1 + (x4 - x1) / phi; f2 = f3; f3 = func(x3, user_data); } else { x4 = x3; x3 = x2; x2 = x4 - (x4 - x1) / phi; f3 = f2; f2 = func(x2, user_data); } } return (f2 < f3) ? x2 : x3; } static double find_delay(const float *a, size_t len_a, const float *b, size_t len_b, double b_rate, int maxdelay, double tol) { struct find_delay_data data = { .a = a, .len_a = len_a, .b = b, .len_b = len_b, .b_rate = b_rate }; double best_x, best_f; int i; best_x = 0; best_f = find_delay_func(best_x, &data); for (i = -maxdelay; i <= maxdelay; ++i) { double f = find_delay_func(i, &data); if (f < best_f) { best_x = i; best_f = f; } } return minimum(best_x - 2, best_x + 2, find_delay_func, &data, tol); } static void test_find_delay(void) { float v1[1024]; float v2[1024]; const double tol = 0.001; double delay, expect; int i; fprintf(stderr, "\n\n-- test_find_delay\n\n"); for (i = 0; i < 1024; ++i) { v1[i] = sinf(0.1f * i); v2[i] = sinf(0.1f * (i - 3.1234f)); } delay = find_delay(v1, SPA_N_ELEMENTS(v1), v2, SPA_N_ELEMENTS(v2), 1, 50, tol); expect = 3.1234f; fprintf(stderr, "find_delay = %g (exact %g)\n", delay, expect); assert_test(expect - 2*tol < delay && delay < expect + 2*tol); for (i = 0; i < 1024; ++i) { v1[i] = sinf(0.1f * i); v2[i] = sinf(0.1f * (i*3.0f/4 - 3.1234f)); } delay = find_delay(v1, SPA_N_ELEMENTS(v1), v2, SPA_N_ELEMENTS(v2), 4.0/3.0, 50, tol); expect = 3.1234f; fprintf(stderr, "find_delay = %g (exact %g)\n", delay, expect); assert_test(expect - 2*tol < delay && delay < expect + 2*tol); } static uint32_t feed_sine(struct resample *r, uint32_t in, uint32_t *inp, uint32_t *phase, bool print) { uint32_t i, out; const void *src[1]; void *dst[1]; for (i = 0; i < in; ++i) samp_in[i] = sinf(0.01f * (*phase + i)) * expf(-0.001f * (*phase + i)); src[0] = samp_in; dst[0] = samp_out; out = SPA_N_ELEMENTS(samp_out); *inp = in; resample_process(r, src, inp, dst, &out); spa_assert_se(*inp == in); if (print || force_print) { fprintf(stderr, "inp(%u) = ", in); for (uint32_t i = 0; i < in; ++i) fprintf(stderr, "%g, ", samp_in[i]); fprintf(stderr, "\n\n"); fprintf(stderr, "out(%u) = ", out); for (uint32_t i = 0; i < out; ++i) fprintf(stderr, "%g, ", samp_out[i]); fprintf(stderr, "\n\n"); } else { fprintf(stderr, "inp(%u) = ...\n", in); fprintf(stderr, "out(%u) = ...\n", out); } *phase += in; *inp = in; return out; } static void check_delay(double rate, uint32_t out_rate, uint32_t options) { const double tol = 0.001; struct resample r; uint32_t in_phase = 0; uint32_t in, out; double expect, got; spa_zero(r); r.log = &logger.log; r.channels = 1; r.i_rate = 48000; r.o_rate = out_rate; r.quality = RESAMPLE_DEFAULT_QUALITY; r.options = options; resample_native_init(&r); resample_update_rate(&r, rate); feed_sine(&r, 512, &in, &in_phase, false); /* Test delay */ expect = resample_delay(&r) + (double)resample_phase(&r); out = feed_sine(&r, 256, &in, &in_phase, true); got = find_delay(samp_in, in, samp_out, out, out_rate / 48000.0, 100, tol); fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got); assert_test(expect - 4*tol < got && got < expect + 4*tol); resample_free(&r); } static void test_delay_copy(void) { fprintf(stderr, "\n\n-- test_delay_copy (no prefill)\n\n"); check_delay(1, 48000, 0); fprintf(stderr, "\n\n-- test_delay_copy (prefill)\n\n"); check_delay(1, 48000, RESAMPLE_OPTION_PREFILL); } static void test_delay_full(void) { const uint32_t rates[] = { 16000, 32000, 44100, 48000, 88200, 96000, 144000, 192000 }; unsigned int i; for (i = 0; i < SPA_N_ELEMENTS(rates); ++i) { fprintf(stderr, "\n\n-- test_delay_full(%u, no prefill)\n\n", rates[i]); check_delay(1, rates[i], 0); fprintf(stderr, "\n\n-- test_delay_full(%u, prefill)\n\n", rates[i]); check_delay(1, rates[i], RESAMPLE_OPTION_PREFILL); } } static void test_delay_interp(void) { fprintf(stderr, "\n\n-- test_delay_interp(no prefill)\n\n"); check_delay(1 + 1e-12, 48000, 0); fprintf(stderr, "\n\n-- test_delay_interp(prefill)\n\n"); check_delay(1 + 1e-12, 48000, RESAMPLE_OPTION_PREFILL); } static void check_delay_vary_rate(double rate, double end_rate, uint32_t out_rate, uint32_t options) { const double tol = 0.001; struct resample r; uint32_t in_phase = 0; uint32_t in, out; double expect, got; fprintf(stderr, "\n\n-- check_delay_vary_rate(%g, %.14g, %u, %s)\n\n", rate, end_rate, out_rate, (options & RESAMPLE_OPTION_PREFILL) ? "prefill" : "no prefill"); spa_zero(r); r.log = &logger.log; r.channels = 1; r.i_rate = 48000; r.o_rate = out_rate; r.quality = RESAMPLE_DEFAULT_QUALITY; r.options = options; resample_native_init(&r); /* Cause nonzero resampler phase */ resample_update_rate(&r, rate); feed_sine(&r, 128, &in, &in_phase, false); resample_update_rate(&r, 1.7); feed_sine(&r, 128, &in, &in_phase, false); resample_update_rate(&r, end_rate); feed_sine(&r, 128, &in, &in_phase, false); feed_sine(&r, 255, &in, &in_phase, false); /* Test delay */ expect = (double)resample_delay(&r) + (double)resample_phase(&r); out = feed_sine(&r, 256, &in, &in_phase, true); got = find_delay(samp_in, in, samp_out, out, out_rate/48000.0, 100, tol); fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got); assert_test(expect - 4*tol < got && got < expect + 4*tol); resample_free(&r); } static void test_delay_interp_vary_rate(void) { const uint32_t rates[] = { 32000, 44100, 48000, 88200, 96000 }; const double factors[] = { 1.0123456789, 1.123456789, 1.203883, 1.23456789, 1.3456789 }; unsigned int i, j; for (i = 0; i < SPA_N_ELEMENTS(rates); ++i) { for (j = 0; j < SPA_N_ELEMENTS(factors); ++j) { /* Interp at end */ check_delay_vary_rate(factors[j], 1 + 1e-12, rates[i], 0); /* Copy/full at end */ check_delay_vary_rate(factors[j], 1, rates[i], 0); /* Interp at end */ check_delay_vary_rate(factors[j], 1 + 1e-12, rates[i], RESAMPLE_OPTION_PREFILL); /* Copy/full at end */ check_delay_vary_rate(factors[j], 1, rates[i], RESAMPLE_OPTION_PREFILL); } } } static void run(uint32_t in_rate, uint32_t out_rate, double end_rate, double mid_rate, uint32_t options) { const double tol = 0.001; struct resample r; uint32_t in_phase = 0; uint32_t in, out; double expect, got; spa_zero(r); r.log = &logger.log; r.channels = 1; r.i_rate = in_rate; r.o_rate = out_rate; r.quality = RESAMPLE_DEFAULT_QUALITY; r.options = options; resample_native_init(&r); /* Cause nonzero resampler phase */ if (mid_rate != 0.0) { resample_update_rate(&r, mid_rate); feed_sine(&r, 128, &in, &in_phase, true); resample_update_rate(&r, 1.7); feed_sine(&r, 128, &in, &in_phase, true); } resample_update_rate(&r, end_rate); feed_sine(&r, 128, &in, &in_phase, true); feed_sine(&r, 255, &in, &in_phase, true); /* Test delay */ expect = (double)resample_delay(&r) + (double)resample_phase(&r); out = feed_sine(&r, 256, &in, &in_phase, true); got = find_delay(samp_in, in, samp_out, out, ((double)out_rate)/in_rate, 100, tol); fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got); if (!(expect - 4*tol < got && got < expect + 4*tol)) fprintf(stderr, "FAIL\n\n"); resample_free(&r); } int main(int argc, char *argv[]) { static const struct option long_options[] = { { "in-rate", required_argument, NULL, 'i' }, { "out-rate", required_argument, NULL, 'o' }, { "end-full", no_argument, NULL, 'f' }, { "end-interp", no_argument, NULL, 'p' }, { "mid-rate", required_argument, NULL, 'm' }, { "prefill", no_argument, NULL, 'r' }, { "print", no_argument, NULL, 'P' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0} }; const char *help = "%s [options]\n" "\n" "Check resampler delay. If no arguments, run tests.\n" "\n" "-i | --in-rate INRATE input rate\n" "-o | --out-rate OUTRATE output rate\n" "-f | --end-full force full (or copy) resampler\n" "-p | --end-interp force interp resampler\n" "-m | --mid-rate RELRATE force rate adjustment in the middle\n" "-r | --prefill enable prefill\n" "-P | --print force printing\n" "\n"; uint32_t in_rate = 0, out_rate = 0; double end_rate = 1, mid_rate = 0; uint32_t options = 0; int c; logger.log.level = SPA_LOG_LEVEL_TRACE; while ((c = getopt_long(argc, argv, "i:o:fpm:rPh", long_options, NULL)) != -1) { switch (c) { case 'h': fprintf(stderr, help, argv[0]); return 0; case 'i': if (!spa_atou32(optarg, &in_rate, 0)) goto error_arg; break; case 'o': if (!spa_atou32(optarg, &out_rate, 0)) goto error_arg; break; case 'f': end_rate = 1; break; case 'p': end_rate = 1 + 1e-12; break; case 'm': if (!spa_atod(optarg, &mid_rate)) goto error_arg; break; case 'r': options = RESAMPLE_OPTION_PREFILL; break; case 'P': force_print = true; break; default: goto error_arg; } } if (in_rate && out_rate) { run(in_rate, out_rate, end_rate, mid_rate, options); return 0; } test_find_delay(); test_delay_copy(); test_delay_full(); test_delay_interp(); test_delay_interp_vary_rate(); return 0; error_arg: fprintf(stderr, "Invalid arguments\n"); return 1; }