diff trunk/src/Modules/SAI/ModuleSAI.cc @ 277:6b4921704eb1

- Ported over HTK file output - Added some more meat to the Slaney IIR gammatone implementation - Ported over the AIM-MAT sf2003 parabola strobe algorithm - Finished making the SAI implementation compile - Ported over the strobe list class (now uses STL deques internally)
author tomwalters
date Thu, 18 Feb 2010 16:55:40 +0000
parents ce2bab04f155
children 5b8b9ea1218a
line wrap: on
line diff
--- a/trunk/src/Modules/SAI/ModuleSAI.cc	Tue Feb 16 18:40:55 2010 +0000
+++ b/trunk/src/Modules/SAI/ModuleSAI.cc	Thu Feb 18 16:55:40 2010 +0000
@@ -29,11 +29,12 @@
 
 #include "Modules/SAI/ModuleSAI.h"
 
+namespace aimc {
 ModuleSAI::ModuleSAI(Parameters *parameters) : Module(parameters) {
+  module_identifier_ = "weighted_sai";
+  module_type_ = "sai";
   module_description_ = "Stabilised auditory image";
-  module_name_ = "sai2007";
-  module_type_ = "sai";
-  module_id_ = "$Id: ModuleSAI.cc 4 2010-02-03 18:44:58Z tcw $";
+  module_version_ = "$Id: ModuleSAI.cc 4 2010-02-03 18:44:58Z tcw $";
 
   min_delay_ms_ = parameters_->DefaultFloat("sai.min_delay_ms", 0.0f);
   max_delay_ms_ = parameters_->DefaultFloat("sai.max_delay_ms", 35.0f);
@@ -43,30 +44,32 @@
                                                    0.03f);
   frame_period_ms_ = parameters_->DefaultFloat("sai.frame_period_ms", 20.0f);
 
-  min_strobe_delay_index_ = 0;
-  max_strobe_delay_index_ = 0;
+  max_concurrent_strobes_
+    = parameters_->DefaultInt("sai.max_concurrent_strobes", 50);
+
+  min_strobe_delay_idx_ = 0;
+  max_strobe_delay_idx_ = 0;
   sai_decay_factor_ = 0.0f;
-  max_concurrent_strobes_ = 0;
-  output_frame_period_ms_ = 0.0f;
-  last_output_frame_time_ms_ = 0.0f;
+  fire_counter_ = 0;
 }
 
 bool ModuleSAI::InitializeInternal(const SignalBank &input) {
   // The SAI output bank must be as long as the SAI's Maximum delay.
   // One sample is added to the SAI buffer length to account for the
   // zero-lag point
-  int sai_buffer_length = 1 + Round(input.sample_rate() * max_delay_ms_);
+  int sai_buffer_length = 1 + floor(input.sample_rate() * max_delay_ms_);
+  channel_count_ = input.channel_count();
 
   // Make an output SignalBank with the same number of channels and centre
   // frequencies as the input, but with a different buffer length
   if (!output_.Initialize(input.channel_count(),
                           sai_buffer_length,
-                          input.sample_rate());) {
+                          input.sample_rate())) {
     LOG_ERROR("Failed to create output buffer in SAI module");
     return false;
   }
   for (int i = 0; i < input.channel_count(); ++i ) {
-    output_.set_centre_frequency(i, input.get_centre_frequency(i));
+    output_.set_centre_frequency(i, input.centre_frequency(i));
   }
 
   // sai_temp_ will be initialized to zero
@@ -75,169 +78,154 @@
     return false;
   }
 
-  last_output_time_ms_ = 0.0f;
-
-  frame_period_samples_ = Round(input.sample_rate()
-                                * frame_period_ms_ / 1000.0f);
-
-  min_strobe_delay_idx_ = Round(input.sample_rate() * min_delay_ms_ / 1000.0f);
-  max_strobe_delay_idx_ = Round(input.sample_rate() * max_delay_ms_ / 1000.0f);
+  frame_period_samples_ = floor(input.sample_rate() * frame_period_ms_
+                                / 1000.0f);
+  min_strobe_delay_idx_ = floor(input.sample_rate() * min_delay_ms_
+                                / 1000.0f);
+  max_strobe_delay_idx_ = floor(input.sample_rate() * max_delay_ms_
+                                / 1000.0f);
 
   // Make sure we don't go past the output buffer's upper bound
-  if (max_strobe_delay_idx_ > output_.buffer_length()))
+  if (max_strobe_delay_idx_ > output_.buffer_length()) {
     max_strobe_delay_idx_ = output_.buffer_length();
+  }
 
   // Define decay factor from time since last sample (see ti2003)
   sai_decay_factor_ = pow(0.5f, 1.0f / (buffer_memory_decay_
                                         * input.sample_rate()));
 
-  // Maximum strobes that can be active at the same time within maxdelay.
-  //! \todo Choose this value in a more principled way
-  max_concurrent_strobes_ = Round(1000.0f * max_delay_ * 5);
-
   // Precompute strobe weights
   strobe_weights_.resize(max_concurrent_strobes_);
   for (int n = 0; n < max_concurrent_strobes_; ++n) {
-    strobe_weights_[n] = pow(1.0f / (n + 1)), strobe_weight_alpha_);
+    strobe_weights_[n] = pow(1.0f / (n + 1), strobe_weight_alpha_);
   }
 
-  // Active Strobes
-  active_strobes_.Resize(input.channel_count());
-  for (int i = 0; i < input.channel_count(); ++i) {
-    active_strobes_[i].Create(max_concurrent_strobes_);
-  }
-  next_strobes_.resize(input.channel_count(), 0);
+  ResetInternal();
 
   return true;
 }
 
 void ModuleSAI::ResetInternal() {
+  // Active Strobes
+  active_strobes_.clear();
+  active_strobes_.resize(channel_count_);
+  fire_counter_ = frame_period_samples_ - 1;
 }
 
 void ModuleSAI::Process(const SignalBank &input) {
-  int s;
-  int c;
-  int output_buffer_length = output_.buffer_length();
 
   // Reset the next strobe times
   next_strobes_.clear();
   next_strobes_.resize(output_.channel_count(), 0);
 
   // Offset the times on the strobes from the previous buffer
-  for (c = 0; c < input.channel_count(), ++c) {
-    active_strobes_[c].shiftStrobes(input.buffer_length());
+  for (int ch = 0; ch < input.channel_count(); ++ch) {
+    active_strobes_[ch].ShiftStrobes(input.buffer_length());
   }
 
-  // Make sure only start time is transferred to the output
-  output_.set_start_time(input.start_time());
-
   // Loop over samples to make the SAI
-  for (s = 0; s < input_buffer_length; ++s) {
+  for (int i = 0; i < input.buffer_length(); ++i) {
     float decay_factor = pow(sai_decay_factor_, fire_counter_);
     // Loop over channels
-    for (c = 0; c < input.channel_count(); ++c) {
+    for (int ch = 0; ch < input.channel_count(); ++ch) {
       // Local convenience variables
-      StrobeList &active_strobes = active_strobes_[c];
-      float centre_frequency = input.get_centre_frequency(c);
-      int next_strobe = next_strobes_[c];
+      StrobeList &active_strobes = active_strobes_[ch];
+      int next_strobe_index = next_strobes_[ch];
 
-      // 1. Update strobes
+      // Update strobes
       // If we are up to or beyond the next strobe...
-      if (next_strobe < input.strobe_count(c)) {
-        if (s == pSigIn->getStrobe(iNextStrobe)) {
-          //A new strobe has arrived
-          // if there aren't already too many strobes active...
-          if ((active_strobes.getStrobeCount() + 1) < max_concurrent_strobes_) {
-            // ...add the active strobe to the list of current strobes
-            // calculate the strobe weight
-            float weight = 1.0f;
-            if (active_strobes.getStrobeCount() > 0) {
-              int last_strobe = active_strobes.getTime(
-                active_strobes.getStrobeCount());
-
-              // If the strobe occured within 10 impulse-response
-              // cycles of the previous strobe, then lower its weight
-              weight = (s - iLastStrobe) / input.sample_rate()
-                       * centre_frequency / 10.0f;
-              if (weight > 1.0f)
-                weight = 1.0f;
-            }
-            pActiveStrobes->addStrobe(iCurrentSample, weight);
-            iNextStrobe++;
-          } else {
-            // We have a problem
-            aimASSERT(0);
+      if (next_strobe_index < input.strobe_count(ch)) {
+        if (i == input.strobe(ch, next_strobe_index)) {
+          // A new strobe has arrived.
+          // If there are too many strobes active, then get rid of the
+          // earliest one
+          if (active_strobes.strobe_count() >= max_concurrent_strobes_) {
+            active_strobes.DeleteFirstStrobe();
           }
 
-          // 2. Having updated the strobes, we now need to update the
-          // strobe weights
+          // Add the active strobe to the list of current strobes and
+          // calculate the strobe weight
+          float weight = 1.0f;
+          if (active_strobes.strobe_count() > 0) {
+            int last_strobe_time = active_strobes.Strobe(
+              active_strobes.strobe_count() - 1).time;
+
+            // If the strobe occured within 10 impulse-response
+            // cycles of the previous strobe, then lower its weight
+            weight = (i - last_strobe_time) / input.sample_rate()
+                     * input.centre_frequency(ch) / 10.0f;
+            if (weight > 1.0f)
+              weight = 1.0f;
+          }
+          active_strobes.AddStrobe(i, weight);
+          next_strobe_index++;
+
+
+          // Update the strobe weights
           float total_strobe_weight = 0.0f;
-          for (int si = 1; si <= pActiveStrobes->getStrobeCount(); ++si) {
-            total_strobe_weight += (pActiveStrobes->getWeight(si) 
-              * m_pStrobeWeights[pActiveStrobes->getStrobeCount() - si]);
+          for (int si = 0; si < active_strobes.strobe_count(); ++si) {
+            total_strobe_weight += (active_strobes.Strobe(si).weight
+              * strobe_weights_[active_strobes.strobe_count() - si - 1]);
           }
-          for (int si = 1; si <= pActiveStrobes->getStrobeCount(); ++si) {
-            active_strobes.setWorkingWeight(si,(active_strobes.getWeight(si)
-              * strobe_weights_[active_strobes.getStrobeCount() - si])
-                / total_strobe_weight);
+          for (int si = 0; si < active_strobes.strobe_count(); ++si) {
+            active_strobes.SetWorkingWeight(si,
+              (active_strobes.Strobe(si).weight
+               * strobe_weights_[active_strobes.strobe_count() - si - 1])
+              / total_strobe_weight);
           }
         }
       }
 
-      // remove inactive strobes...
-      while (pActiveStrobes->getStrobeCount() > 0) {
-        // Get the time of the first strobe (ordering of strobes is
-        // from one, not zero)
-        int iStrobeTime = pActiveStrobes->getTime(1);
-        int iDelay = iCurrentSample - iStrobeTime;
-        // ... do we now need to remove this strobe?
-        if (iDelay > m_maxStrobeDelayIdx)
-          pActiveStrobes->deleteFirstStrobe();
+      // Remove inactive strobes
+      while (active_strobes.strobe_count() > 0) {
+        // Get the relative time of the first strobe, and see if it exceeds
+        // the maximum allowed time.
+        if ((i - active_strobes.Strobe(0).time) > max_strobe_delay_idx_)
+          active_strobes.DeleteFirstStrobe();
         else
           break;
-          // Since the strobes are ordered, we don't need to go
-          // beyond the first still-active strobe
       }
 
-      // 3. Loop over active strobes
-      for (int si = 1; si <= pActiveStrobes->getStrobeCount(); si++) {
-        // 3.1 Add effect of active strobe at correct place in the SAI buffer
-        // Calculate the time from the strobe event to 'now': iDelay
-        int iStrobeTime = pActiveStrobes->getTime(si);
-        int iDelay = iCurrentSample - iStrobeTime;
+      // Update the SAI buffer with the weighted effect of all the active
+      // strobes at the current sample
+      for (int si = 0; si < active_strobes.strobe_count(); ++si) {
+        // Add the effect of active strobe at correct place in the SAI buffer
+        // Calculate 'delay', the time from the strobe event to now
+        int delay = i - active_strobes.Strobe(si).time;
 
         // If the delay is greater than the (user-set)
         // minimum strobe delay, the strobe can be used
-        if (iDelay >= m_minStrobeDelayIdx && iDelay < m_maxStrobeDelayIdx) {
+        if (delay >= min_strobe_delay_idx_ && delay < max_strobe_delay_idx_) {
           // The value at be added to the SAI
-          float sig = pSigIn->getSample(iCurrentSample, audCh);
+          float sig = input.sample(ch, i);
 
           // Weight the sample correctly
-          sig *= pActiveStrobes->getWorkingWeight(si);
+          sig *= active_strobes.Strobe(si).working_weight;
 
           // Adjust the weight acording to the number of samples until the
           // next output frame
-          sig *= fDecayFactor;
+          sig *= decay_factor;
 
           // Update the temporary SAI buffer
-          pSigOut->setSample(iDelay, audCh,
-                             pSigOut->getSample(iDelay, audCh)+sig);
+          output_.set_sample(ch, delay, output_.sample(ch, delay) + sig);
         }
       }
 
-      m_pNextStrobes[bankCh]=iNextStrobe;
+      next_strobes_[ch] = next_strobe_index;
 
     } // End loop over channels
 
+    fire_counter_--;
 
-    //Check to see if we need to output an SAI frame this sample
-    if (m_iFireCounter-- == 0) {
+    // Check to see if we need to output an SAI frame on this sample
+    if (fire_counter_ <= 0) {
       // Decay the SAI by the correct amount and add the current output frame
-      float decay = pow(sai_decay_factor_, fire_period_samples_);
+      float decay = pow(sai_decay_factor_, frame_period_samples_);
 
-      for (c = 0; c < input.channel_count(); ++c) {
+      for (int ch = 0; ch < input.channel_count(); ++ch) {
         for (int i = 0; i < output_.buffer_length(); ++i) {
-          output_.set_sample(c, i, sai_temp_[c][i] + output_[c][i] * decay);
+          output_.set_sample(ch, i,
+                             sai_temp_[ch][i] + output_[ch][i] * decay);
         }
       }
 
@@ -248,10 +236,10 @@
         }
       }
 
-      m_iFireCounter=m_iFirePeriodSamples-1;
+      fire_counter_ = frame_period_samples_ - 1;
 
-      // Make sure the start time is transferred to the output
-      m_pOutputData->setStartTime(m_pInputData->getSignal(0)->getStartTime()+(SIGNAL_SAMPLE)((float)iCurrentSample*1000.0f/(float)m_pInputData->getSamplerate()));
+      // Transfer the current time to the output buffer
+      output_.set_start_time(input.start_time() + i);
       PushOutput();
     }
   } // End loop over samples
@@ -259,3 +247,4 @@
 
 ModuleSAI::~ModuleSAI() {
 }
+}  // namespace aimc