[beast] TESTS: replace slow resampler checks with a much faster resampling test



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]