r4121 - in trunk/bse: . tests



Author: stw
Date: 2006-12-03 15:27:33 -0500 (Sun, 03 Dec 2006)
New Revision: 4121

Modified:
   trunk/bse/ChangeLog
   trunk/bse/bsedatahandle-fir.cc
   trunk/bse/gsldatahandle.h
   trunk/bse/tests/firhandle.cc
Log:
Sun Dec  3 21:13:32 2006  Stefan Westerfeld  <stefan space twc de>

	* bsedatahandle-fir.cc: Made state size computation work correctly.
	Added lowpass handle.

	* tests/firhandle.cc: Extended tests to cover both, the highpass and
	the lowpass handle.


Modified: trunk/bse/ChangeLog
===================================================================
--- trunk/bse/ChangeLog	2006-12-03 13:08:42 UTC (rev 4120)
+++ trunk/bse/ChangeLog	2006-12-03 20:27:33 UTC (rev 4121)
@@ -1,3 +1,11 @@
+Sun Dec  3 21:13:32 2006  Stefan Westerfeld  <stefan space twc de>
+
+	* bsedatahandle-fir.cc: Made state size computation work correctly.
+	Added lowpass handle.
+
+	* tests/firhandle.cc: Extended tests to cover both, the highpass and
+	the lowpass handle.
+
 Sat Dec  2 23:55:44 2006  Stefan Westerfeld  <stefan space twc de>
 
 	* tests/Makefile.am:

Modified: trunk/bse/bsedatahandle-fir.cc
===================================================================
--- trunk/bse/bsedatahandle-fir.cc	2006-12-03 13:08:42 UTC (rev 4120)
+++ trunk/bse/bsedatahandle-fir.cc	2006-12-03 20:27:33 UTC (rev 4121)
@@ -205,8 +205,7 @@
     // m_src_handle must be opened and have valid state size
     g_return_val_if_fail (source_state_length >= 0, 0);  
 
-    int64 per_channel_state = 0;
-    return source_state_length + per_channel_state * m_dhandle.setup.n_channels;
+    return source_state_length + m_history;
   }
 
   static GslDataHandle*
@@ -312,6 +311,48 @@
   }
 };
 
+class DataHandleFirLowpass : public DataHandleFir
+{
+protected:
+  gdouble m_cutoff_freq;
+
+public:
+  DataHandleFirLowpass (GslDataHandle *src_handle,
+			gdouble        cutoff_freq,
+			guint          order) :
+    DataHandleFir (src_handle, order),
+    m_cutoff_freq (cutoff_freq)
+  {
+    if (m_init_ok)
+      m_dhandle.name = g_strconcat (m_src_handle->name, "// #lowpass /", NULL);
+  }
+
+  virtual void
+  design_filter_coefficients (double mix_freq)
+  {
+    const guint transfer_func_length = 4;
+    double transfer_func_freqs[transfer_func_length];
+    double transfer_func_values[transfer_func_length];
+
+    transfer_func_freqs[0]  = 1; // 0 dB
+    transfer_func_values[0] = 1;
+
+    transfer_func_freqs[1]  = m_cutoff_freq / mix_freq * 2 * M_PI;
+    transfer_func_values[1] = 1; // 0 dB
+
+    transfer_func_freqs[2]  = m_cutoff_freq / mix_freq * 2 * M_PI;
+    transfer_func_values[2] = 0;
+
+    transfer_func_freqs[3]  = PI;
+    transfer_func_values[3] = 0;
+
+    gsl_filter_fir_approx (m_a.size() - 1, &m_a[0],
+                           transfer_func_length, transfer_func_freqs, transfer_func_values,
+			   false); // interpolate dB
+  }
+};
+
+
 }
 
 #if 0 // debugging
@@ -334,7 +375,7 @@
 
 using namespace Bse;
 
-/*
+/**
  *           __________
  *          /
  *         /
@@ -354,3 +395,24 @@
   DataHandleFir *cxx_dh = new DataHandleFirHighpass (src_handle, cutoff_freq, order);
   return DataHandleFir::dh_create (cxx_dh);
 }
+
+/**
+ * ______                    
+ *       \    
+ *        \  
+ *         \
+ *          \__________
+ *        |
+ *   cutoff_freq
+ *
+ * @cutoff_freq: cutoff frequency in Hz in intervall [0..SR/2]
+ * @order:       number of filter coefficients
+ */
+extern "C" GslDataHandle*
+bse_data_handle_new_fir_lowpass (GslDataHandle *src_handle,
+				 gdouble        cutoff_freq,
+				 guint          order)
+{
+  DataHandleFir *cxx_dh = new DataHandleFirLowpass (src_handle, cutoff_freq, order);
+  return DataHandleFir::dh_create (cxx_dh);
+}

Modified: trunk/bse/gsldatahandle.h
===================================================================
--- trunk/bse/gsldatahandle.h	2006-12-03 13:08:42 UTC (rev 4120)
+++ trunk/bse/gsldatahandle.h	2006-12-03 20:27:33 UTC (rev 4121)
@@ -123,6 +123,9 @@
 GslDataHandle*	  bse_data_handle_new_fir_highpass  (GslDataHandle *src_handle,		// implemented in bsedatahandle-fir.cc
 						     gdouble        cutoff_freq,
 						     guint          order);
+GslDataHandle*	  bse_data_handle_new_fir_lowpass   (GslDataHandle *src_handle,		// implemented in bsedatahandle-fir.cc
+						     gdouble        cutoff_freq,
+						     guint          order);
 
 
 /* --- xinfo handling --- */

Modified: trunk/bse/tests/firhandle.cc
===================================================================
--- trunk/bse/tests/firhandle.cc	2006-12-03 13:08:42 UTC (rev 4120)
+++ trunk/bse/tests/firhandle.cc	2006-12-03 20:27:33 UTC (rev 4121)
@@ -30,6 +30,7 @@
 using std::vector;
 using std::min;
 using std::max;
+using Birnet::string_printf;
 
 static void
 read_through (GslDataHandle *handle)
@@ -65,10 +66,81 @@
   return diff;
 }
 
-void
-test_highpass_with_sine_sweep()
+static double
+band_min (const vector<double>& scanned_freq,
+          const vector<double>& scanned_values,
+	  double                start_freq,
+	  double                end_freq)
 {
-  TSTART ("Highpass Handle (sweep)");
+  g_assert (scanned_freq.size() == scanned_values.size());
+  
+  bool	  init = false;
+  double  min_value = 1e19;
+  for (size_t i = 0; i < scanned_values.size(); i++)
+    {
+      if (scanned_freq[i] >= start_freq && scanned_freq[i] <= end_freq)
+	{
+	  if (init)
+	    min_value = min (scanned_values[i], min_value);
+	  else
+	    {
+	      min_value = scanned_values[i];
+	      init = true;
+	    }
+	}
+    }
+  g_assert (init);
+  return min_value;
+}
+
+static double
+band_max (const vector<double>& scanned_freq,
+          const vector<double>& scanned_values,
+	  double                start_freq,
+	  double                end_freq)
+{
+  g_assert (scanned_freq.size() == scanned_values.size());
+  
+  bool	  init = false;
+  double  max_value = -1e19;
+  for (size_t i = 0; i < scanned_values.size(); i++)
+    {
+      if (scanned_freq[i] >= start_freq && scanned_freq[i] <= end_freq)
+	{
+	  if (init)
+	    max_value = max (scanned_values[i], max_value);
+	  else
+	    {
+	      max_value = scanned_values[i];
+	      init = true;
+	    }
+	}
+    }
+  g_assert (init);
+  return max_value;
+}
+
+enum FirHandleType
+{
+  FIR_HIGHPASS,
+  FIR_LOWPASS
+};
+
+static const char*
+handle_name (FirHandleType type)
+{
+  switch (type)
+    {
+      case FIR_HIGHPASS:  return "Highpass";
+      case FIR_LOWPASS:	  return "Lowpass";
+      default:		  g_assert_not_reached();
+    }
+}
+
+static void
+test_with_sine_sweep (FirHandleType type)
+{
+  TSTART ("%s Handle (sweep)", handle_name (type));
   vector<float> sweep_sin (50000);
   vector<float> sweep_cos (50000);
   vector<double> sweep_freq (50000);
@@ -94,9 +166,20 @@
   GslDataHandle *ihandle_cos = gsl_data_handle_new_mem (1, 32, mix_freq, 440, sweep_cos.size(), &sweep_cos[0], NULL);
 
   const int order = 64;
-  GslDataHandle *fir_handle_sin = bse_data_handle_new_fir_highpass (ihandle_sin, 9000.0, order);
-  GslDataHandle *fir_handle_cos = bse_data_handle_new_fir_highpass (ihandle_cos, 9000.0, order);
+  GslDataHandle *fir_handle_sin = NULL;
+  GslDataHandle *fir_handle_cos = NULL;
 
+  if (type == FIR_HIGHPASS)
+    {
+      fir_handle_sin = bse_data_handle_new_fir_highpass (ihandle_sin, 9000.0, order);
+      fir_handle_cos = bse_data_handle_new_fir_highpass (ihandle_cos, 9000.0, order);
+    }
+  else if (type == FIR_LOWPASS)
+    {
+      fir_handle_sin = bse_data_handle_new_fir_lowpass (ihandle_sin, 6000.0, order);
+      fir_handle_cos = bse_data_handle_new_fir_lowpass (ihandle_cos, 6000.0, order);
+    }
+
   BseErrorType error;
   error = gsl_data_handle_open (fir_handle_sin);
   TASSERT (error == 0);
@@ -106,63 +189,66 @@
   GslDataPeekBuffer peek_buffer_sin = { +1 /* incremental direction */, 0, };
   GslDataPeekBuffer peek_buffer_cos = { +1 /* incremental direction */, 0, };
 
-  double stop_min_db = 1e19, stop_max_db = -1e19;
-  double trans_min_db = 1e19, trans_max_db = -1e19;
-  double pass1_min_db = 1e19, pass1_max_db = -1e19;
-  double pass2_min_db = 1e19, pass2_max_db = -1e19;
-  double phase_diff_max = 0;
+  vector<double> scanned_freq, scanned_level_db, scanned_abs_phase_diff;
+
   for (size_t i = ((order + 2) / 2); i < sweep_sin.size() - ((order + 2) / 2); i++)
     {
       double filtered_sin = gsl_data_handle_peek_value (fir_handle_sin, i, &peek_buffer_sin);
       double filtered_cos = gsl_data_handle_peek_value (fir_handle_cos, i, &peek_buffer_cos);
       std::complex<double> filtered (filtered_sin, filtered_cos);
-      double level = abs (filtered);
-      double level_db = bse_db_from_factor (level, -200);
 
       // check frequency response
-      // printf ("%f %.17g\n", sweep_freq[i], level_db);
-      if (sweep_freq[i] < 7050)
-	{
-	  stop_min_db = min (stop_min_db, level_db);
-	  stop_max_db = max (stop_max_db, level_db);
-	}
-      if (sweep_freq[i] > 7050 && sweep_freq[i] < 9500)
-	{
-	  trans_min_db = min (trans_min_db, level_db);
-	  trans_max_db = max (trans_max_db, level_db);
-	}
-      if (sweep_freq[i] > 9500 && sweep_freq[i] < 11000)
-	{
-	  pass1_min_db = min (pass1_min_db, level_db);
-	  pass1_max_db = max (pass1_max_db, level_db);
-	}
-      if (sweep_freq[i] > 11000)
-	{
-	  pass2_min_db = min (pass2_min_db, level_db);
-	  pass2_max_db = max (pass2_max_db, level_db);
-	}
+      double level = abs (filtered);
+      scanned_freq.push_back (sweep_freq[i]);
+      scanned_level_db.push_back (bse_db_from_factor (level, -200));
+      // printf ("%f %.17g\n", sweep_freq[i], scanned_level_db.back());
       
       // check phase response in passband
       std::complex<double> orig (sweep_sin[i], sweep_cos[i]);
-      double abs_phase_diff = fabs (phase_diff (arg (orig), arg (filtered)));
-      if (sweep_freq[i] > 11000)
-	{
-	  phase_diff_max = max (phase_diff_max, abs_phase_diff);
-	  // printf ("%f %.17g\n", sweep_freq[i], abs_phase_diff);
-	}
+      scanned_abs_phase_diff.push_back (fabs (phase_diff (arg (orig), arg (filtered))));
+      // printf ("%f %.17g\n", sweep_freq[i], scanned_abs_phase_diff.back());
     }
-#if 0
-  printf ("stop = %f..%f dB\n", stop_min_db, stop_max_db);
-  printf ("trans = %f..%f dB\n", trans_min_db, trans_max_db);
-  printf ("pass1 = %f..%f dB\n", pass1_min_db, pass1_max_db);
-  printf ("pass2 = %f..%f dB\n", pass2_min_db, pass2_max_db);
-  printf ("max phase diff = %f\n", phase_diff_max);
-#endif
-  TASSERT (stop_max_db < -75);
-  TASSERT (trans_min_db > -77 && trans_max_db < -2.8);
-  TASSERT (pass1_min_db > -2.82 && pass1_max_db < -0.002);
-  TASSERT (pass2_min_db > -0.004 && pass2_max_db < 0.002);
-  TASSERT (phase_diff_max < 0.0002);
+
+  if (type == FIR_HIGHPASS)
+    {
+      // stop band
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,     0,  7050), <, -75);
+
+      // transition band
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db,  7050,  9500), >, -77);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,  7050,  9500), <, -2.8);
+
+      // passband (1)
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db,  9500, 11000), >, -2.82);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,  9500, 11000), <, -0.002);
+
+      // passband (2)
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db, 11000, 24000), >, -0.004);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db, 11000, 24000), <, 0.002);
+
+      // zero phase in passband (2)
+      TASSERT_CMP (band_max (scanned_freq, scanned_abs_phase_diff, 11000, 24000), <, 0.0002);
+    }
+  else	// FIR_LOWPASS
+    {
+      // passband (2)
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db,     0,  5500), >, -0.002);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,     0,  5500), <, 0.002);
+
+      // passband (1)
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db,  5500,  7000), >, -1.9);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,  5500,  7000), <, -0.001);
+
+      // transition band
+      TASSERT_CMP (band_min (scanned_freq, scanned_level_db,  7000, 10000), >, -81);
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db,  7000, 10000), <, -1.8);
+
+      // stop band
+      TASSERT_CMP (band_max (scanned_freq, scanned_level_db, 10000, 24000), <, -75);
+
+      // zero phase in passband (2)
+      TASSERT_CMP (band_max (scanned_freq, scanned_abs_phase_diff, 0, 5500), <, 0.00002);
+    }
   TDONE();
 
   /* test speed */
@@ -185,12 +271,14 @@
             m = e;
         }
       samples_per_second = sweep_sin.size() / (m / dups);
-      treport_maximized ("Highpass O64 mono", samples_per_second, TUNIT (SAMPLE, SECOND));
-      treport_maximized ("CPU Highpass mono", samples_per_second / 44100.0, TUNIT_STREAM);
+      treport_maximized (string_printf ("%s O64 mono", handle_name (type)).c_str(),
+                         samples_per_second, TUNIT (SAMPLE, SECOND));
+      treport_maximized (string_printf ("CPU %s mono", handle_name (type)).c_str(),
+			 samples_per_second / 44100.0, TUNIT_STREAM);
     }
 }
 
-double
+static double
 raised_cosine_fade (int64 pos,
 		    int64 length,
 		    int64 fade_length)
@@ -203,10 +291,10 @@
     return (0.5 - cos (fade_factor * PI) * 0.5);
 }
 
-void
-test_highpass_multi_channel()
+static void
+test_multi_channel (FirHandleType type)
 {
-  TSTART ("Highpass Handle (multichannel)");
+  TSTART ("%s Handle (multichannel)", handle_name (type));
   for (int n_channels = 1; n_channels <= 10; n_channels++)
     {
       const double    mix_freq = 48000;
@@ -230,7 +318,8 @@
 	  const double  invalue     = sin (phase[c]) * fade_factor;
 
 	  input[i] = invalue;
-	  expected[i] = (freq[c] > cutoff_freq) ? invalue : 0.0;
+	  if ((freq[c] > cutoff_freq && type == FIR_HIGHPASS) || (freq[c] < cutoff_freq && type == FIR_LOWPASS))
+	    expected[i] = invalue;
 
 	  phase[c] += freq[c] / mix_freq * 2.0 * M_PI;
 	  if (phase[c] > 2.0 * M_PI)
@@ -239,7 +328,12 @@
 
       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);
+      GslDataHandle *fir_handle = NULL;
+      
+      if (type == FIR_HIGHPASS)
+	fir_handle = bse_data_handle_new_fir_highpass (ihandle, cutoff_freq, order);
+      else
+	fir_handle = bse_data_handle_new_fir_lowpass (ihandle, cutoff_freq, order);
 
       BseErrorType error;
       error = gsl_data_handle_open (fir_handle);
@@ -268,7 +362,12 @@
       char **argv)
 {
   bse_init_test (&argc, &argv, NULL);
-  test_highpass_with_sine_sweep();
-  test_highpass_multi_channel();
+
+  test_with_sine_sweep (FIR_HIGHPASS);
+  test_multi_channel (FIR_HIGHPASS);
+
+  test_with_sine_sweep (FIR_LOWPASS);
+  test_multi_channel (FIR_LOWPASS);
+
   return 0;
 }




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