From 3cade43cf36b5bfdf176a93fcfcadf79313eed05 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 27 Jul 2025 13:18:08 +0300 Subject: [PATCH] test-resample: add test for floating point rounding producing bad in_len If phase is float, calculations in impl_native_in_len/out_len don't necessarily match with do_resample, because e.g. float phase0 = 7999.99; float phase = phase0; int frac = 8000, out_rate = 8000, n = 64, count = 0; for (int j = 0; j < n; ++j) { phase += frac; if (phase >= out_rate) { phase -= out_rate; count++; } } printf("count = %d\n", count); /* count = 64 */ count = (int)(phase0 + n*frac) / out_rate; printf("count = %d\n", count); /* count = 65 */ don't give the same result. Also add test where floating point multiplication rounding up to nearest in float ph = phase * pm; uint32_t offset = (uint32_t)floorf(ph); computation results to offset+1 > data->n_phases, accessing filter array beyond bounds. (The accessed value is still inside allocated memory block, but contains unrelated values; the test passes silently.) --- spa/plugins/audioconvert/test-resample.c | 53 ++++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/spa/plugins/audioconvert/test-resample.c b/spa/plugins/audioconvert/test-resample.c index cc97bd275..0848d8f80 100644 --- a/spa/plugins/audioconvert/test-resample.c +++ b/spa/plugins/audioconvert/test-resample.c @@ -15,6 +15,7 @@ SPA_LOG_IMPL(logger); #include "resample.h" +#include "resample-native-impl.h" #define N_SAMPLES 253 #define N_CHANNELS 11 @@ -125,14 +126,20 @@ static void pull_blocks_out(struct resample *r, uint32_t first, uint32_t size, u } } -static void check_inout_len(struct resample *r, uint32_t first, uint32_t size, double rate) +static void check_inout_len(struct resample *r, uint32_t first, uint32_t size, double rate, double phase) { + struct native_data *data = r->data; + resample_reset(r); resample_update_rate(r, rate); + if (phase != 0.0) + data->phase = (float)phase; pull_blocks(r, first, size, 500); resample_reset(r); resample_update_rate(r, rate); + if (phase != 0.0) + data->phase = (float)phase; pull_blocks_out(r, first, size, 500); } @@ -148,7 +155,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 1024, 1024, 1.0); + check_inout_len(&r, 1024, 1024, 1.0, 0); resample_free(&r); spa_zero(r); @@ -159,7 +166,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 1024, 1024, 1.0); + check_inout_len(&r, 1024, 1024, 1.0, 0); resample_free(&r); spa_zero(r); @@ -170,7 +177,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 1024, 1024, 1.0); + check_inout_len(&r, 1024, 1024, 1.0, 0); resample_free(&r); spa_zero(r); @@ -181,7 +188,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 513, 64, 1.0); + check_inout_len(&r, 513, 64, 1.0, 0); resample_free(&r); spa_zero(r); @@ -192,7 +199,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 513, 64, 1.02); + check_inout_len(&r, 513, 64, 1.02, 0); resample_free(&r); spa_zero(r); @@ -203,7 +210,7 @@ static void test_inout_len(void) r.quality = RESAMPLE_DEFAULT_QUALITY; resample_native_init(&r); - check_inout_len(&r, 513, 64, 1.0002); + check_inout_len(&r, 513, 64, 1.0002, 0); resample_free(&r); spa_zero(r); @@ -215,7 +222,37 @@ static void test_inout_len(void) r.options = RESAMPLE_OPTION_PREFILL; resample_native_init(&r); - check_inout_len(&r, 513, 64, 1.0002); + check_inout_len(&r, 513, 64, 1.0002, 0); + resample_free(&r); + + /* Test value of phase that in floating-point arithmetic produces + * inconsistent in_len + */ + spa_zero(r); + r.log = &logger.log; + r.channels = 1; + r.i_rate = 8000; + r.o_rate = 8000; + r.quality = RESAMPLE_DEFAULT_QUALITY; + r.options = RESAMPLE_OPTION_PREFILL; + resample_native_init(&r); + + check_inout_len(&r, 64, 64, 1.0 + 1e-10, 7999.99); + resample_free(&r); + + /* Test value of phase that overflows filter buffer due to floating point rounding + * up to nearest + */ + spa_zero(r); + r.log = &logger.log; + r.channels = 1; + r.i_rate = 8000; + r.o_rate = 8000; + r.quality = RESAMPLE_DEFAULT_QUALITY; + r.options = RESAMPLE_OPTION_PREFILL; + resample_native_init(&r); + + check_inout_len(&r, 64, 64, 1.0 + 1e-10, nextafterf(8000, 0)); resample_free(&r); }