[beast] TESTS: replace slow resampler checks with a much faster resampling test
- From: Stefan Westerfeld <stw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast] TESTS: replace slow resampler checks with a much faster resampling test
- Date: Fri, 28 Jan 2011 10:42:48 +0000 (UTC)
commit ebb3eaadac131b410cd0d2ef0c753640f2b9db05
Author: Stefan Westerfeld <stefan space twc de>
Date: Fri Jan 28 11:42:42 2011 +0100
TESTS: replace slow resampler checks with a much faster resampling test
tests/Makefile.am | 14 ++-
tests/testresamplerq.cc | 263 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 273 insertions(+), 4 deletions(-)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fc62083..5062cf7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -60,9 +60,13 @@ perftest_LDADD = $(progs_ldadd)
noinst_PROGRAMS += testresampler
testresampler_SOURCES = testresampler.cc
testresampler_LDADD = $(progs_ldadd)
+# testresamplerq
+TESTS += testresamplerq
+testresamplerq_SOURCES = testresamplerq.cc
+testresamplerq_LDADD = $(progs_ldadd)
+
# === Resampler tests ===
-check-after: resampler-quick-checks
resampler-quick-checks:
: ## some quick checks (resampler-checks contains actual tests - use make slowcheck)
./testresampler filter-impl
@@ -72,7 +76,6 @@ resampler-quick-checks:
./testresampler accuracy --fpu --oversample --precision=20 --freq-scan=180,18000,1671 --max-threshold=113.5
./testresampler accuracy --subsample --precision=24 --freq-scan=90,9000,983 --max-threshold=126
-slowcheck: resampler-checks
resampler-checks:
: ## test SSE filter implementation
./testresampler filter-impl
@@ -108,8 +111,7 @@ resampler-checks:
./testresampler accuracy --subsample --precision=16 --freq-scan=25,9000,25 --max-threshold=85.5 # ideally: 96dB
./testresampler accuracy --fpu --subsample --precision=16 --freq-scan=25,9000,25 --max-threshold=85.5 # ideally: 96dB
-perf:
- ./perftest
+resampler-perf:
./testresampler perf --fpu --precision=8 --up
./testresampler perf --fpu --precision=8 --down
./testresampler perf --fpu --precision=8 --subsample
@@ -142,3 +144,7 @@ perf:
./testresampler perf --precision=24 --down
./testresampler perf --precision=24 --subsample
./testresampler perf --precision=24 --oversample
+
+# perf tests
+perf:
+ ./perftest
diff --git a/tests/testresamplerq.cc b/tests/testresamplerq.cc
new file mode 100644
index 0000000..48db9fc
--- /dev/null
+++ b/tests/testresamplerq.cc
@@ -0,0 +1,263 @@
+/* Quick test for resampling code
+ * Copyright (C) 2011 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+
+#include <bse/bseresampler.hh>
+#include <bse/bsemathsignal.h>
+#include <bse/bsemain.h>
+#include <bse/bseblockutils.hh>
+#include <bse/gslfft.h>
+#include <sfi/sfitests.h>
+#include <birnet/birnet.hh>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+using Bse::Resampler::Resampler2;
+using Birnet::AlignedArray;
+using std::vector;
+using std::max;
+using std::min;
+
+struct Options
+{
+ size_t test_size;
+ size_t rand_samples;
+
+ // default test parameters
+ Options() :
+ test_size (512),
+ rand_samples (64)
+ {
+ }
+} options;
+
+class ResamplerTest
+{
+public:
+ double passband_err;
+ double stopband_err;
+ double max_error;
+
+ void check_spectrum (const vector<float>& impulse_response, int p);
+ void check_resampler_up (BseResampler2Precision precision);
+ void check_resampler_down (BseResampler2Precision precision);
+};
+
+void
+ResamplerTest::check_spectrum (const vector<float>& impulse_response, int p)
+{
+ vector<double> fft_in (4096), spect (fft_in.size());
+
+ for (size_t i = 0; i < min (impulse_response.size(), fft_in.size()); i++)
+ {
+ fft_in[i] = impulse_response[i] / 2;
+ }
+ gsl_power2_fftar (fft_in.size(), &fft_in[0], &spect[0]);
+ spect[1] = 0; // special packing
+
+ passband_err = 0;
+ stopband_err = 0;
+
+ for (size_t i = 0; i < 4096; i += 2)
+ {
+ const double re = spect[i], im = spect[i + 1];
+ const double mag = sqrt (re * re + im * im);
+ const double freq = i * 44100.0 / 4096;
+ if (freq < 18000)
+ passband_err = max (passband_err, fabs (1 - mag));
+ if (freq > 26100)
+ stopband_err = max (stopband_err, fabs (mag));
+ }
+}
+
+void
+ResamplerTest::check_resampler_up (BseResampler2Precision precision)
+{
+ Resampler2 *ups = Resampler2::create (BSE_RESAMPLER2_MODE_UPSAMPLE, precision);
+ AlignedArray<float,16> input (options.test_size);
+ AlignedArray<float,16> output (options.test_size * 2);
+ vector< vector<float> > results;
+
+ for (size_t i = 0; i < (options.test_size / 2); i++)
+ {
+ input[i] = 1;
+ ups->process_block (&input[0], input.size(), &output[0]);
+ input[i] = 0;
+ results.push_back (vector<float> (&output[0], &output[output.size()]));
+ for (size_t j = 0; j < output.size(); j++)
+ {
+ if (j >= i * 2)
+ {
+ assert (output[j] == results[0][j - i * 2]);
+ }
+ // printf ("%d %.17g #p%d,%d\n", j, output[j], precision, i);
+ }
+ }
+ for (size_t i = 0; i < options.rand_samples; i++)
+ input[i] = g_random_double_range (-1, 1);
+ ups->process_block (&input[0], input.size(), &output[0]);
+
+ max_error = 0;
+ for (size_t j = 0; j < output.size(); j++)
+ {
+ double acc = 0;
+ for (size_t i = 0; i < options.rand_samples; i++)
+ {
+ acc += results[i][j] * input[i];
+ }
+ max_error = max (fabs (output[j] - acc), max_error);
+ }
+ check_spectrum (results[0], precision);
+}
+
+void
+ResamplerTest::check_resampler_down (BseResampler2Precision precision)
+{
+ Resampler2 *downs = Resampler2::create (BSE_RESAMPLER2_MODE_DOWNSAMPLE, precision);
+ AlignedArray<float,16> input (options.test_size * 2);
+ AlignedArray<float,16> output (options.test_size);
+ vector< vector<float> > results;
+
+ for (size_t i = 0; i < (options.test_size / 2); i++)
+ {
+ input[i] = 1;
+ downs->process_block (&input[0], input.size(), &output[0]);
+ input[i] = 0;
+ results.push_back (vector<float> (&output[0], &output[output.size()]));
+ for (size_t j = 0; j < output.size(); j++)
+ {
+ if (j >= i/2)
+ assert (output[j] == results[i % 2][j - i/2]);
+ //printf ("%zd %.17g #%d,%zd\n", j, output[j], precision, i);
+ }
+ }
+ for (size_t i = 0; i < options.rand_samples; i++)
+ input[i] = g_random_double_range (-1, 1);
+ downs->process_block (&input[0], input.size(), &output[0]);
+ max_error = 0;
+ for (size_t j = 0; j < output.size(); j++)
+ {
+ double acc = 0;
+ for (size_t i = 0; i < options.rand_samples; i++)
+ {
+ acc += results[i][j] * input[i];
+ }
+ max_error = max (fabs (output[j] - acc), max_error);
+ }
+
+ /* The downsampler convolves the input with an FIR filter to achieve a
+ * lowpass filter around half the sampling frequency. Since it throws
+ * away every second sample, we need to merge the impulse responses
+ * for two adjacent impulse responses to figure out the total impulse
+ * response of the resampling FIR filter
+ */
+ vector<float> merged_ir;
+ for (size_t i = 0; i < results[0].size(); i++)
+ {
+ merged_ir.push_back (results[1][i] * 2);
+ merged_ir.push_back (results[0][i] * 2);
+ }
+ check_spectrum (merged_ir, precision);
+}
+
+static double
+band_err (BseResampler2Precision p)
+{
+ /* the filter design is not always exactly as specified by the precision,
+ * so sometimes we achieve a lower db value than requested, and sometimes
+ * a higher db value than specified
+ */
+ switch (p)
+ {
+ case BSE_RESAMPLER2_PREC_LINEAR: return -8.5;
+ case BSE_RESAMPLER2_PREC_48DB: return -51;
+ case BSE_RESAMPLER2_PREC_72DB: return -74;
+ case BSE_RESAMPLER2_PREC_96DB: return -95;
+ case BSE_RESAMPLER2_PREC_120DB: return -120;
+ case BSE_RESAMPLER2_PREC_144DB: return -144;
+ default: g_assert_not_reached();
+ }
+}
+
+static void
+run_tests (const char *label)
+{
+ BseResampler2Precision p = BSE_RESAMPLER2_PREC_96DB; // should not be equal to the first resampler precision
+
+ for (int i = 0; i < 32; i++)
+ {
+ BseResampler2Precision new_p = Resampler2::find_precision_for_bits (i);
+ if (new_p != p)
+ {
+ p = new_p;
+
+ TSTART ("Resampler %s Precision %d", label, p);
+
+ ResamplerTest rt_up;
+ rt_up.check_resampler_up (p);
+
+ TASSERT (bse_db_from_factor (rt_up.max_error, -200) < -125);
+ TASSERT (bse_db_from_factor (rt_up.passband_err, -200) < band_err (p));
+ TASSERT (bse_db_from_factor (rt_up.stopband_err, -200) < band_err (p));
+
+ //printf ("## UP %d %.17g %.17g %.17g\n", p, bse_db_from_factor (rt_up.max_error, -200),
+ //bse_db_from_factor (rt_up.passband_err, -200),
+ //bse_db_from_factor (rt_up.stopband_err, -200));
+ ResamplerTest rt_down;
+ rt_down.check_resampler_down (p);
+
+ TASSERT (bse_db_from_factor (rt_up.max_error, -200) < -125);
+ TASSERT (bse_db_from_factor (rt_up.passband_err, -200) < band_err (p));
+ TASSERT (bse_db_from_factor (rt_up.stopband_err, -200) < band_err (p));
+
+ //printf ("## DOWN %d %.17g %.17g %.17g\n", p, bse_db_from_factor (rt_down.max_error, -200),
+ //bse_db_from_factor (rt_down.passband_err, -200),
+ //bse_db_from_factor (rt_down.stopband_err, -200));
+ TDONE();
+ }
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ sfi_init_test (&argc, &argv, NULL);
+ if (argc > 1)
+ {
+ options.test_size = atoi (argv[1]);
+ }
+ if (argc > 2)
+ {
+ options.rand_samples = atoi (argv[2]);
+ }
+ assert (options.rand_samples <= options.test_size / 2);
+ assert (options.test_size >= 128);
+ g_print ("Resampler test parameters: test_size=%zd rand_samples=%zd\n",
+ options.test_size, options.rand_samples);
+ run_tests ("FPU");
+
+ /* load plugins */
+ SfiInitValue config[] = {
+ { "load-core-plugins", "1" },
+ { NULL },
+ };
+ bse_init_test (&argc, &argv, config);
+ /* check for possible specialization */
+ if (Bse::Block::default_singleton() == Bse::Block::current_singleton())
+ return 0; /* nothing changed */
+ run_tests ("SSE");
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]