mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
audioconvert: resampler: add more resampler delay tests
Test also conversions between different rates, and with and without prefill.
This commit is contained in:
parent
07f6dde3dd
commit
79384530b5
1 changed files with 228 additions and 54 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
|
@ -18,27 +19,37 @@ SPA_LOG_IMPL(logger);
|
|||
#include "resample.h"
|
||||
|
||||
|
||||
static float samp_in[4096];
|
||||
static float samp_out[4096];
|
||||
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(float delay, const float *a, size_t len_a, const float *b, size_t len_b)
|
||||
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) {
|
||||
float jf = i + delay;
|
||||
double jf = (i + delay) * b_rate;
|
||||
int j;
|
||||
float bv, x;
|
||||
double x;
|
||||
float bv;
|
||||
|
||||
j = (int)floorf(jf);
|
||||
j = (int)floor(jf);
|
||||
if (j < 0 || j + 1 >= (int)len_b)
|
||||
continue;
|
||||
|
||||
x = jf - j;
|
||||
bv = (1 - x) * b[j] + x * b[j + 1];
|
||||
bv = (float)((1 - x) * b[j] + x * b[j + 1]);
|
||||
|
||||
c += (a[i] - bv) * (a[i] - bv);
|
||||
wa += a[i]*a[i];
|
||||
|
|
@ -56,13 +67,14 @@ struct find_delay_data {
|
|||
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);
|
||||
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)
|
||||
|
|
@ -99,9 +111,9 @@ static double minimum(double x1, double x4, double (*func)(double, void *), void
|
|||
return (f2 < f3) ? x2 : x3;
|
||||
}
|
||||
|
||||
static double find_delay(const float *a, size_t len_a, const float *b, size_t len_b, int maxdelay, double tol)
|
||||
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 };
|
||||
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;
|
||||
|
||||
|
|
@ -135,13 +147,23 @@ static void test_find_delay(void)
|
|||
v2[i] = sinf(0.1f * (i - 3.1234f));
|
||||
}
|
||||
|
||||
delay = find_delay(v1, SPA_N_ELEMENTS(v1), v2, SPA_N_ELEMENTS(v2), 50, tol);
|
||||
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);
|
||||
spa_assert_se(expect - 2*tol < delay && delay < expect + 2*tol);
|
||||
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)
|
||||
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];
|
||||
|
|
@ -158,22 +180,27 @@ static uint32_t feed_sine(struct resample *r, uint32_t in, uint32_t *inp, uint32
|
|||
resample_process(r, src, inp, dst, &out);
|
||||
spa_assert_se(*inp == in);
|
||||
|
||||
fprintf(stderr, "inp(%u) = ", in);
|
||||
for (uint32_t i = 0; i < in; ++i)
|
||||
fprintf(stderr, "%g, ", samp_in[i]);
|
||||
fprintf(stderr, "\n\n");
|
||||
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");
|
||||
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)
|
||||
static void check_delay(double rate, uint32_t out_rate, uint32_t options)
|
||||
{
|
||||
const double tol = 0.001;
|
||||
struct resample r;
|
||||
|
|
@ -185,38 +212,58 @@ static void check_delay(double rate)
|
|||
r.log = &logger.log;
|
||||
r.channels = 1;
|
||||
r.i_rate = 48000;
|
||||
r.o_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, 64, &in, &in_phase);
|
||||
feed_sine(&r, 512, &in, &in_phase, false);
|
||||
|
||||
/* Test delay */
|
||||
expect = resample_delay(&r);
|
||||
out = feed_sine(&r, 64, &in, &in_phase);
|
||||
got = find_delay(samp_in, in, samp_out, out, 40, tol);
|
||||
fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got);
|
||||
expect = resample_delay(&r) + (double)resample_phase_ns(&r) * 48000 / SPA_NSEC_PER_SEC;
|
||||
out = feed_sine(&r, 256, &in, &in_phase, true);
|
||||
got = find_delay(samp_in, in, samp_out, out, out_rate / 48000.0, 100, tol);
|
||||
|
||||
spa_assert_se(expect - 2*tol < got && got < expect + 2*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\n\n");
|
||||
check_delay(1);
|
||||
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\n\n");
|
||||
check_delay(1 + 1e-12);
|
||||
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_interp_vary_rate(double rate)
|
||||
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;
|
||||
|
|
@ -224,39 +271,36 @@ static void check_delay_interp_vary_rate(double rate)
|
|||
uint32_t in, out;
|
||||
double expect, got;
|
||||
|
||||
fprintf(stderr, "\n\n-- check_delay_vary_rate(%g)\n\n", rate);
|
||||
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 = 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);
|
||||
feed_sine(&r, 128, &in, &in_phase, false);
|
||||
|
||||
resample_update_rate(&r, 1.7);
|
||||
feed_sine(&r, 128, &in, &in_phase);
|
||||
feed_sine(&r, 128, &in, &in_phase, false);
|
||||
|
||||
resample_update_rate(&r, 1 + 1e-12);
|
||||
feed_sine(&r, 256, &in, &in_phase);
|
||||
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);
|
||||
out = feed_sine(&r, 64, &in, &in_phase);
|
||||
got = find_delay(samp_in, in, samp_out, out, 40, tol);
|
||||
fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got);
|
||||
expect = (double)resample_delay(&r) + (double)resample_phase_ns(&r) * 48000 / SPA_NSEC_PER_SEC;
|
||||
out = feed_sine(&r, 256, &in, &in_phase, true);
|
||||
got = find_delay(samp_in, in, samp_out, out, out_rate/48000.0, 100, tol);
|
||||
|
||||
#if 0
|
||||
/* XXX: this fails */
|
||||
spa_assert_se(expect - 2*tol < got && got < expect + 2*tol);
|
||||
#else
|
||||
if (!(expect - 2*tol < got && got < expect + 2*tol))
|
||||
fprintf(stderr, "KNOWNFAIL\n");
|
||||
#endif
|
||||
fprintf(stderr, "delay: expect = %g, got = %g\n", expect, got);
|
||||
assert_test(expect - 4*tol < got && got < expect + 4*tol);
|
||||
|
||||
resample_free(&r);
|
||||
}
|
||||
|
|
@ -264,19 +308,149 @@ static void check_delay_interp_vary_rate(double rate)
|
|||
|
||||
static void test_delay_interp_vary_rate(void)
|
||||
{
|
||||
check_delay_interp_vary_rate(1.0123456789);
|
||||
check_delay_interp_vary_rate(1.123456789);
|
||||
check_delay_interp_vary_rate(1.23456789);
|
||||
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_ns(&r) * (double)in_rate / SPA_NSEC_PER_SEC;
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue