r4115 - in trunk/bse: . tests



Author: stw
Date: 2006-12-02 16:12:57 -0500 (Sat, 02 Dec 2006)
New Revision: 4115

Modified:
   trunk/bse/ChangeLog
   trunk/bse/bsedatahandle-fir.cc
   trunk/bse/tests/firhandle.cc
Log:
Sat Dec  2 22:03:57 2006  Stefan Westerfeld  <stefan space twc de>

	* tests/firhandle.cc: Added a highpass test for multichannel signals.

	* bsedatahandle-fir.cc: Fixed the code for n_channels != 1. Minor
	code cleanups.


Modified: trunk/bse/ChangeLog
===================================================================
--- trunk/bse/ChangeLog	2006-11-29 01:04:38 UTC (rev 4114)
+++ trunk/bse/ChangeLog	2006-12-02 21:12:57 UTC (rev 4115)
@@ -1,3 +1,10 @@
+Sat Dec  2 22:03:57 2006  Stefan Westerfeld  <stefan space twc de>
+
+	* tests/firhandle.cc: Added a highpass test for multichannel signals.
+
+	* bsedatahandle-fir.cc: Fixed the code for n_channels != 1. Minor
+	code cleanups.
+
 Tue Nov 28 21:05:16 2006  Stefan Westerfeld  <stefan space twc de>
 
 	* bsedatahandle-fir.cc: Some optimizations, more to come.

Modified: trunk/bse/bsedatahandle-fir.cc
===================================================================
--- trunk/bse/bsedatahandle-fir.cc	2006-11-29 01:04:38 UTC (rev 4114)
+++ trunk/bse/bsedatahandle-fir.cc	2006-12-02 21:12:57 UTC (rev 4115)
@@ -49,6 +49,10 @@
   int64                 m_history;
   bool			m_init_ok;
 
+protected:
+  virtual void
+  design_filter_coefficients (double mix_freq) = 0;
+
 public:
   DataHandleFir (GslDataHandle *src_handle,
 		 guint          order) :
@@ -90,8 +94,8 @@
 
     // since we need overlapping data for consecutive reads we buffer data locally
     m_block_size = 1024 * m_src_handle->setup.n_channels;
-    m_history = (m_a.size() + 1) / 2;
-    m_input_data.resize (m_block_size + (2 * m_history) * m_src_handle->setup.n_channels);
+    m_history = ((m_a.size() + 1) / 2) * m_src_handle->setup.n_channels;
+    m_input_data.resize (m_block_size + 2 * m_history);
     m_input_voffset = -2 * m_block_size;
 
     design_filter_coefficients (gsl_data_handle_mix_freq (m_src_handle));
@@ -99,9 +103,6 @@
     return BSE_ERROR_NONE;
   }
 
-  virtual void
-  design_filter_coefficients (double mix_freq) = 0;
-
   void
   close()
   {
@@ -115,16 +116,17 @@
 	     gfloat       *dest)
   {
     /* tiny FIR evaluation: not optimized for speed */
-    guint i, j;
+    const guint channels = m_dhandle.setup.n_channels;
     const guint iorder = m_a.size();
-    for (i = 0; i < n_samples; i++)
+    for (guint i = 0; i < n_samples; i++)
       {
 	gdouble accu = 0;
-	for (j = 0; j <= iorder; j++)
+	GslLong p = i;
+	p -= (iorder / 2) * channels; 
+	for (guint j = 0; j <= iorder; j++)
 	  {
-	    GslLong p = i + j;
-	    p -= iorder / 2;
 	    accu += m_a[j] * src[p];
+	    p += channels;
 	  }
 	dest[i] = accu;
       }
@@ -136,9 +138,11 @@
     int64 i = 0;
     g_return_val_if_fail (voffset % m_block_size == 0, -1);
 
+    // if this is a consecutive read, the history can be built from the values
+    // we already read last time
     if (m_input_voffset == voffset - m_block_size)
       {
-	int64 overlap_values = 2 * m_history * m_dhandle.setup.n_channels;
+	int64 overlap_values = 2 * m_history;
 	copy (m_input_data.end() - overlap_values, m_input_data.end(), m_input_data.begin());
 	i += overlap_values;
       }
@@ -150,7 +154,7 @@
 	  {
 	    int64 values_todo = min (static_cast<int64> (m_input_data.size()) - i, m_dhandle.setup.n_values - offset);
 	    int64 l = gsl_data_handle_read (m_src_handle, offset, values_todo, &m_input_data[i]);
-	    if (l < 0)
+	    if (l < 0)  // pass on errors
 	      {
 		// invalidate m_input_data
 		voffset = -2 * m_block_size;
@@ -176,12 +180,12 @@
 	float *values)
   {
     int64 ivoffset = voffset;
-    ivoffset = ivoffset - ivoffset % m_block_size;
+    ivoffset -= ivoffset % m_block_size;
 
     if (ivoffset != m_input_voffset)
       {
 	int64 l = seek (ivoffset);
-	if (l < 0)
+	if (l < 0)   // pass on errors
 	  return l;
       }
 

Modified: trunk/bse/tests/firhandle.cc
===================================================================
--- trunk/bse/tests/firhandle.cc	2006-11-29 01:04:38 UTC (rev 4114)
+++ trunk/bse/tests/firhandle.cc	2006-12-02 21:12:57 UTC (rev 4115)
@@ -50,7 +50,7 @@
 void
 test_highpass_with_sine_sweep()
 {
-  TSTART ("Highpass Handle");
+  TSTART ("Highpass Handle (sweep)");
   vector<float> sweep_sin (50000);
   vector<float> sweep_cos (50000);
   vector<double> sweep_freq (50000);
@@ -72,7 +72,6 @@
 	phase -= 2.0 * M_PI;
     }
 
-  /* FIXME: handle n_channels != 1 */
   GslDataHandle *ihandle_sin = gsl_data_handle_new_mem (1, 32, mix_freq, 440, sweep_sin.size(), &sweep_sin[0], NULL);
   GslDataHandle *ihandle_cos = gsl_data_handle_new_mem (1, 32, mix_freq, 440, sweep_cos.size(), &sweep_cos[0], NULL);
 
@@ -149,11 +148,85 @@
   TDONE();
 }
 
+double
+raised_cosine_fade (int64 pos,
+		    int64 length,
+		    int64 fade_length)
+{
+  double fade_delta  = 1.0 / fade_length;
+  double fade_factor = fade_delta * min (pos, length - pos);
+  if (fade_factor >= 1.0)
+    return 1.0;
+  else
+    return (0.5 - cos (fade_factor * PI) * 0.5);
+}
+
+void
+test_highpass_multi_channel()
+{
+  TSTART ("Highpass Handle (multichannel)");
+  for (int n_channels = 1; n_channels <= 10; n_channels++)
+    {
+      const double    mix_freq = 48000;
+      const double    cutoff_freq = 7500;
+      const double    test_freqs[] = {
+	50, 100, 234.567, 557, 901, 1350, 1780, 2345, 3745, 4500,	     // below cutoff
+	11000, 12000, 13945, 14753, 15934, 16734, 17943, 18930, 19320, 20940 // above cutoff
+      };
+      vector<float>   input (2500 * n_channels);
+      vector<double>  expected (input.size());
+      vector<double>  freq (n_channels);
+      vector<double>  phase (n_channels);
+
+      for (int c = 0; c < n_channels; c++)
+	freq[c] = test_freqs [g_random_int_range (0, sizeof (test_freqs) / sizeof (test_freqs[0]))];
+
+      for (size_t i = 0; i < input.size(); i++)
+	{
+	  const int	c           = i % n_channels;
+	  const double  fade_factor = raised_cosine_fade (i / n_channels, input.size() / n_channels, 500);
+	  const double  invalue     = sin (phase[c]) * fade_factor;
+
+	  input[i] = invalue;
+	  expected[i] = (freq[c] > cutoff_freq) ? invalue : 0.0;
+
+	  phase[c] += freq[c] / mix_freq * 2.0 * M_PI;
+	  if (phase[c] > 2.0 * M_PI)
+	    phase[c] -= 2.0 * M_PI;
+	}
+
+      GslDataHandle *ihandle = gsl_data_handle_new_mem (n_channels, 32, mix_freq, 440, input.size(), &input[0], NULL);
+      const int order = 116;
+      GslDataHandle *fir_handle = bse_data_handle_new_fir_highpass (ihandle, cutoff_freq, order);
+
+      BseErrorType error;
+      error = gsl_data_handle_open (fir_handle);
+      TASSERT (error == 0);
+
+      for (int repeat = 1; repeat <= 2; repeat++)
+	{
+	  GslDataPeekBuffer peek_buffer = { +1 /* incremental direction */, 0, };
+	  double worst_diff = 0.0;
+	  for (int64 i = 0; i < fir_handle->setup.n_values; i++)
+	    {
+	      double filtered = gsl_data_handle_peek_value (fir_handle, i, &peek_buffer);
+	      worst_diff = max (filtered - expected[i], worst_diff);
+	    }
+	  double worst_diff_db = bse_db_from_factor (worst_diff, -200);
+	  TPRINT ("n_channels = %d: linear(%dst read) read worst_diff = %f (%f dB)\n",
+	          n_channels, repeat, worst_diff, worst_diff_db);
+	  TASSERT (worst_diff_db < -90);
+	}
+    }
+  TDONE();
+}
+
 int
 main (int    argc,
       char **argv)
 {
   bse_init_test (&argc, &argv, NULL);
   test_highpass_with_sine_sweep();
+  test_highpass_multi_channel();
   return 0;
 }




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]