changeset 32:9122efd2b227

-New AIMCopy main for the SSI features (temporary hack till I get a working module load system) -LocalMax strobe criterion. This is faster and better than the parabola version, which still seems buggy. -Noise generator module. Adds noise to a signal. Uses boost for the random number generator. -New options for the SSI -Slice now respects all its flags (oops!). -MATLAB functions for visualisation -Scripts for generating data to view in MATLAB -Script to download and build HTK - useful for running experiments
author tomwalters
date Thu, 25 Feb 2010 22:02:00 +0000
parents fa06bfacf004
children f8fe1aadf097
files SConstruct scripts/install_htk.sh src/Main/AIMCopy_SSI_Features.cc src/Modules/Profile/ModuleSlice.cc src/Modules/SNR/ModuleNoise.cc src/Modules/SNR/ModuleNoise.h src/Modules/SSI/ModuleSSI.cc src/Modules/SSI/ModuleSSI.h src/Modules/Strobes/ModuleLocalMax.cc src/Modules/Strobes/ModuleLocalMax.h src/Modules/Strobes/ModuleParabola.cc src/Scripts/Multi-slice_test.py src/Scripts/Strobes_test.py src/Scripts/plot_feature_tracks.m src/Scripts/plot_strobes.m src/Support/SignalBank.cc src/Support/SignalBank.h swig/aim_modules.i swig/setup.py
diffstat 19 files changed, 1176 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/SConstruct	Wed Feb 24 15:18:00 2010 +0000
+++ b/SConstruct	Thu Feb 25 22:02:00 2010 +0000
@@ -41,7 +41,9 @@
                   'Modules/BMM/ModulePZFC.cc',
                   'Modules/NAP/ModuleHCL.cc',
                   'Modules/Strobes/ModuleParabola.cc',
+                  'Modules/Strobes/ModuleLocalMax.cc',
                   'Modules/SAI/ModuleSAI.cc',
+                  'Modules/SNR/ModuleNoise.cc',
                   'Modules/SSI/ModuleSSI.cc',
                   'Modules/Profile/ModuleSlice.cc',
                   'Modules/Profile/ModuleScaler.cc',
@@ -49,7 +51,7 @@
                   'Modules/Output/FileOutputHTK.cc']
 
 # File which contains main()
-sources = common_sources + ['Main/AIMCopy.cc']
+sources = common_sources + ['Main/AIMCopy_SSI_Features.cc']
 
 # Test sources
 test_sources = ['Modules/Profile/ModuleSlice_unittest.cc']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/install_htk.sh	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,8 @@
+#!/bin/bash
+cd /media/sounds-database/htk
+wget --user $HTK_USERNAME --password $HTK_PASSWORD http://htk.eng.cam.ac.uk/ftp/software/HTK-3.4.1.tar.gz
+tar -xzf HTK-3.4.1.tar.gz
+cd htk
+./configure --disable-hslab
+make
+sudo make install
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Main/AIMCopy_SSI_Features.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,301 @@
+// Copyright 2008-2010, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/*!
+ * \file AIMCopy.cpp
+ * \brief AIM-C replacement for HTK's HCopy
+ *
+ * The following subset of the command-line flags
+ * should be implemented from HCopy:
+ *  -A      Print command line arguments         off
+ *  -C cf   Set config file to cf                default 
+ * (should be able to take multiple config files)
+ *  -S f    Set script file to f                 none
+ *  //! \todo -T N    Set trace flags to N                 0
+ *  -V      Print version information            off
+ *  -D of   Write configuration data to of       none
+ *
+ * \author Thomas Walters <tom@acousticscale.org>
+ * \date created 2008/05/08
+ * \version \$Id$
+ */
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "Modules/Input/ModuleFileInput.h"
+#include "Modules/BMM/ModuleGammatone.h"
+#include "Modules/BMM/ModulePZFC.h"
+#include "Modules/NAP/ModuleHCL.h"
+#include "Modules/Strobes/ModuleParabola.h"
+#include "Modules/SAI/ModuleSAI.h"
+#include "Modules/SSI/ModuleSSI.h"
+#include "Modules/SNR/ModuleNoise.h"
+#include "Modules/Profile/ModuleSlice.h"
+#include "Modules/Profile/ModuleScaler.h"
+#include "Modules/Features/ModuleGaussians.h"
+#include "Modules/Output/FileOutputHTK.h"
+#include "Support/Common.h"
+#include "Support/FileList.h"
+#include "Support/Parameters.h"
+
+using std::ofstream;
+using std::pair;
+using std::vector;
+using std::string;
+int main(int argc, char* argv[]) {
+  string sound_file;
+  string data_file;
+  string config_file;
+  string script_file;
+  bool write_data = false;
+  bool print_version = false;
+
+  string version_string(
+    " AIM-C AIMCopy\n"
+    "  (c) 2006-2010, Thomas Walters and Willem van Engen\n"
+    "  http://www.acoustiscale.org/AIMC/\n"
+    "\n");
+
+  if (argc < 2) {
+    printf("%s", version_string.c_str());
+    printf("AIMCopy is intended as a drop-in replacement for HTK's HCopy\n");
+    printf("command. It is used for making features from audio files for\n");
+    printf("use with HTK.\n");
+    printf("Usage: \n");
+    printf("  -A      Print command line arguments  off\n");
+    printf("  -C cf   Set config file to cf         none\n");
+    printf("  -S f    Set script file to f          none\n");
+    printf("  -V      Print version information     off\n");
+    printf("  -D g    Write configuration data to g none\n");
+    return -1;
+  }
+
+  // Parse command-line arguments
+  for (int i = 1; i < argc; i++) {
+    if (strcmp(argv[i],"-A") == 0) {
+      for (int j = 0; j < argc; j++)
+        printf("%s ",argv[j]);
+      printf("\n");
+      fflush(stdout);
+      continue;
+    }
+    if (strcmp(argv[i],"-C") == 0) {
+      if (++i >= argc) {
+        aimc::LOG_ERROR(_T("Configuration file name expected after -C"));
+        return(-1);
+      }
+      config_file = argv[i];
+      continue;
+    }
+    if (strcmp(argv[i],"-S") == 0) {
+      if (++i >= argc) {
+        aimc::LOG_ERROR(_T("Script file name expected after -S"));
+        return(-1);
+      }
+      script_file = argv[i];
+      continue;
+    }
+    if (strcmp(argv[i],"-D") == 0) {
+      if (++i >= argc) {
+        aimc::LOG_ERROR(_T("Data file name expected after -D"));
+        return(-1);
+      }
+      data_file = argv[i];
+      write_data = true;
+      continue;
+    }
+    if (strcmp(argv[i],"-V") == 0) {
+      print_version = true;
+      continue;
+    }
+    aimc::LOG_ERROR(_T("Unrecognized command-line argument: %s"), argv[i]);
+  }
+
+  if (print_version)
+    printf("%s", version_string.c_str());
+
+  aimc::Parameters params;
+
+  if (!params.Load(config_file.c_str())) {
+    aimc::LOG_ERROR(_T("Couldn't load parameters from file %s"),
+                    config_file.c_str());
+    return -1;
+  }
+
+  vector<pair<string, string> > file_list = aimc::FileList::Load(script_file);
+  if (file_list.size() == 0) {
+    aimc::LOG_ERROR("No data read from file %s", script_file.c_str());
+    return -1;
+  }
+
+  // Set up AIM-C processor here
+  aimc::ModuleFileInput input(&params);
+  //aimc::ModuleNoise noise_maker(&params);
+  aimc::ModuleGammatone bmm(&params);
+  aimc::ModuleHCL nap(&params);
+  aimc::ModuleParabola strobes(&params);
+  aimc::ModuleSAI sai(&params);
+  aimc::ModuleSSI ssi(&params);
+
+  params.SetBool("slice.all", false);
+  params.SetInt("slice.lower_index", 40);
+  params.SetInt("slice.upper_index", 56);
+  aimc::ModuleSlice slice_1(&params);
+
+  params.SetInt("slice.lower_index", 88);
+  params.SetInt("slice.upper_index", 104);
+  aimc::ModuleSlice slice_2(&params);
+
+  params.SetInt("slice.lower_index", 184);
+  params.SetInt("slice.upper_index", 200);
+  aimc::ModuleSlice slice_3(&params);
+
+  params.SetInt("slice.lower_index", 376);
+  params.SetInt("slice.upper_index", 392);
+  aimc::ModuleSlice slice_4(&params);
+
+  params.SetBool("slice.all", true);
+  aimc::ModuleSlice slice_5(&params);
+
+  aimc::ModuleGaussians features_1(&params);
+  aimc::ModuleGaussians features_2(&params);
+  aimc::ModuleGaussians features_3(&params);
+  aimc::ModuleGaussians features_4(&params);
+  aimc::ModuleGaussians features_5(&params);
+
+  aimc::FileOutputHTK output_1(&params);
+  aimc::FileOutputHTK output_2(&params);
+  aimc::FileOutputHTK output_3(&params);
+  aimc::FileOutputHTK output_4(&params);
+  aimc::FileOutputHTK output_5(&params);
+
+  input.AddTarget(&bmm);
+  // No noise for now
+  //noise_maker.AddTarget(&bmm);
+  bmm.AddTarget(&nap);
+  nap.AddTarget(&strobes);
+  strobes.AddTarget(&sai);
+  sai.AddTarget(&ssi);
+
+  ssi.AddTarget(&slice_1);
+  ssi.AddTarget(&slice_2);
+  ssi.AddTarget(&slice_3);
+  ssi.AddTarget(&slice_4);
+  ssi.AddTarget(&slice_5);
+
+  slice_1.AddTarget(&features_1);
+  slice_2.AddTarget(&features_2);
+  slice_3.AddTarget(&features_3);
+  slice_4.AddTarget(&features_4);
+  slice_5.AddTarget(&features_5);
+
+  features_1.AddTarget(&output_1);
+  features_2.AddTarget(&output_2);
+  features_3.AddTarget(&output_3);
+  features_4.AddTarget(&output_4);
+  features_5.AddTarget(&output_5);
+
+  if (write_data) {
+    ofstream outfile(data_file.c_str());
+    if (outfile.fail()) {
+      aimc::LOG_ERROR("Couldn't open data file %s for writing",
+                      data_file.c_str());
+      return -1;
+    }
+    time_t rawtime;
+    struct tm * timeinfo;
+    time(&rawtime);
+    timeinfo = localtime(&rawtime);
+
+
+    outfile << "# AIM-C AIMCopy\n";
+    outfile << "# Run on: " << asctime(timeinfo);
+    char * descr = getenv("USER");
+    if (descr) {
+      outfile << "# By user: " << descr <<"\n";
+    }
+    outfile << "# Module chain: ";
+    outfile << "#  gt";
+    outfile << "#   parabola";
+    outfile << "#    sai_weighted";
+    outfile << "#     ssi";
+    outfile << "#      slice";
+    outfile << "#       features";
+    outfile << "#         output";
+    outfile << "#      slice";
+    outfile << "#       features";
+    outfile << "#         output";
+    outfile << "#      slice";
+    outfile << "#       features";
+    outfile << "#         output";
+    outfile << "#      slice";
+    outfile << "#       features";
+    outfile << "#         output";
+    outfile << "#      slice";
+    outfile << "#       features";
+    outfile << "#         output";
+    outfile << "# ";
+    outfile << "# Module versions:\n";
+    outfile << "# " << input.id() << " : " << input.version() << "\n";
+    outfile << "# " << bmm.id() << " : " << bmm.version() << "\n";
+    outfile << "# " << nap.id() << " : " << nap.version() << "\n";
+    outfile << "# " << strobes.id() << " : " << strobes.version() << "\n";
+    outfile << "# " << sai.id() << " : " << sai.version() << "\n";
+    outfile << "# " << slice_1.id() << " : " << slice_1.version() << "\n";
+    // outfile << "# " << profile.id() << " : " << profile.version() << "\n";
+    // outfile << "# " << scaler.id() << " : " << scaler.version() << "\n";
+    outfile << "# " << features_1.id() << " : " << features_1.version() << "\n";
+    outfile << "# " << output_1.id() << " : " << output_1.version() << "\n";
+    outfile << "#\n";
+    outfile << "# Parameters:\n";
+    outfile << params.WriteString();
+    outfile.close();
+  }
+
+  for (unsigned int i = 0; i < file_list.size(); ++i) {
+    aimc::LOG_INFO(_T("In:  %s"), file_list[i].first.c_str());
+    aimc::LOG_INFO(_T("Out: %s"), file_list[i].second.c_str());
+
+    string filename = file_list[i].second + "_1";
+    output_1.OpenFile(filename.c_str(), 10.0f);
+    filename = file_list[i].second + "_2";
+    output_2.OpenFile(filename.c_str(), 10.0f);
+    filename = file_list[i].second + "_3";
+    output_3.OpenFile(filename.c_str(), 10.0f);
+    filename = file_list[i].second + "_4";
+    output_4.OpenFile(filename.c_str(), 10.0f);
+    filename = file_list[i].second + "_5";
+    output_5.OpenFile(filename.c_str(), 10.0f);
+
+    if (input.LoadFile(file_list[i].first.c_str())) {
+      input.Process();
+    } else {
+      printf("LoadFile failed for file %s\n", file_list[i].first.c_str());
+    }
+    input.Reset();
+  }
+
+  return 0;
+}
--- a/src/Modules/Profile/ModuleSlice.cc	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Modules/Profile/ModuleSlice.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -53,7 +53,7 @@
   buffer_length_ = input.buffer_length();
   channel_count_ = input.channel_count();
 
-  if (lower_limit_ < 0) {
+  if (lower_limit_ < 0 || take_all_) {
     lower_limit_ = 0;
   }
 
@@ -62,14 +62,14 @@
   }
 
   if (temporal_profile_) {
-    if (upper_limit_ > channel_count_) {
+    if (upper_limit_ > channel_count_ || take_all_) {
       upper_limit_ = channel_count_;
     }
     if (lower_limit_ > channel_count_) {
       lower_limit_ = channel_count_;
     }
   } else {
-    if (upper_limit_ > buffer_length_) {
+    if (upper_limit_ > buffer_length_ || take_all_) {
       upper_limit_ = buffer_length_;
     }
     if (lower_limit_ > buffer_length_) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Modules/SNR/ModuleNoise.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,92 @@
+// Copyright 2010, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/*!
+ * \author Thomas Walters <tom@acousticscale.org>
+ * \date created 2010/02/24
+ * \version \$Id$
+ */
+
+// Use the boost RNGs to generate Gaussian noise
+#include <boost/random.hpp>
+#include <math.h>
+
+#include "Modules/SNR/ModuleNoise.h"
+
+namespace aimc {
+ModuleNoise::ModuleNoise(Parameters *params) :
+    Module(params),
+    gaussian_variate_(boost::mt19937(),
+                      boost::normal_distribution<float>(0.0f, 1.0f)) {
+  module_description_ = "Adds noise to a signal";
+  module_identifier_ = "noise";
+  module_type_ = "snr";
+  module_version_ = "$Id$";
+
+  // Noise level relative to unit-variance Gaussian noise (ie. 0dB will give a
+  // noise with an RMS level of 1.0)
+  float snr_db = parameters_->DefaultFloat("noise.level_db", 0.0f);
+  multiplier_ = pow(10.0f, snr_db / 20.0f);
+}
+
+ModuleNoise::~ModuleNoise() {
+}
+
+bool ModuleNoise::InitializeInternal(const SignalBank &input) {
+  // Copy the parameters of the input signal bank into internal variables, so
+  // that they can be checked later.
+  sample_rate_ = input.sample_rate();
+  buffer_length_ = input.buffer_length();
+  channel_count_ = input.channel_count();
+
+  output_.Initialize(input);
+  return true;
+}
+
+void ModuleNoise::ResetInternal() {
+
+}
+
+void ModuleNoise::Process(const SignalBank &input) {
+  // Check to see if the module has been initialized. If not, processing
+  // should not continue.
+  if (!initialized_) {
+    LOG_ERROR(_T("Module %s not initialized."), module_identifier_.c_str());
+    return;
+  }
+
+  // Check that ths input this time is the same as the input passed to
+  // Initialize()
+  if (buffer_length_ != input.buffer_length()
+      || channel_count_ != input.channel_count()) {
+    LOG_ERROR(_T("Mismatch between input to Initialize() and input to "
+                 "Process() in module %s."), module_identifier_.c_str());
+    return;
+  }
+
+  for (int c = 0; c < input.channel_count(); ++c) {
+    for (int i = 0; i < input.buffer_length(); ++i) {
+      float s = input[c][i];
+      s += (multiplier_ * gaussian_variate_());
+      output_.set_sample(c, i, s);
+    }
+  }
+  PushOutput();
+}
+}  // namespace aimc
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Modules/SNR/ModuleNoise.h	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,67 @@
+// Copyright 2010, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/*!
+ * \author Thomas Walters <tom@acousticscale.org>
+ * \date created 2010/02/24
+ * \version \$Id$
+ */
+
+#ifndef AIMC_MODULES_SNR_NOISE_H_
+#define AIMC_MODULES_SNR_NOISE_H_
+
+#include <boost/random.hpp>
+
+#include "Support/Module.h"
+
+namespace aimc {
+class ModuleNoise : public Module {
+ public:
+  explicit ModuleNoise(Parameters *pParam);
+  virtual ~ModuleNoise();
+
+  /*! \brief Process a buffer
+   */
+  virtual void Process(const SignalBank &input);
+
+ private:
+  /*! \brief Reset the internal state of the module
+   */
+  virtual void ResetInternal();
+
+  /*! \brief Prepare the module
+   *  \param input Input signal
+   *  \param output true on success false on failure
+   */
+  virtual bool InitializeInternal(const SignalBank &input);
+
+  float sample_rate_;
+  int buffer_length_;
+  int channel_count_;
+
+  float multiplier_;
+
+  // Random number generator which yeilds Gaussian-distributed values by
+  // The generator is a Mersenne twister
+  boost::variate_generator<boost::mt19937,
+                           boost::normal_distribution<float> >
+                           gaussian_variate_;
+};
+}  // namespace aimc
+
+#endif  // AIMC_MODULES_SNR_NOISE_H_
--- a/src/Modules/SSI/ModuleSSI.cc	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Modules/SSI/ModuleSSI.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -33,8 +33,36 @@
   module_type_ = "ssi";
   module_version_ = "$Id$";
 
-  // do_pitch_cutoff_ = parameters_->DefaultBool("ssi.pitch_cutoff", false);
-  ssi_width_cycles_ = parameters_->DefaultFloat("ssi.width_cycles", 20.0f);
+  // Cut off the SSI at the end of the first cycle
+  do_pitch_cutoff_ = parameters_->DefaultBool("ssi.pitch_cutoff", false);
+
+  // Weight the values in each channel more strongly if the channel was
+  // truncated due to the pitch cutoff. This ensures that the same amount of
+  // energy remains in the SSI spectral profile
+  weight_by_cutoff_ = parameters_->DefaultBool("ssi.weight_by_cutoff", false);
+
+  // Weight the values in each channel more strongly if the channel was
+  // scaled such that the end goes off the edge of the computed SSI.
+  // Again, this ensures that the overall energy of the spectral profile
+  // remains the same.
+  weight_by_scaling_ = parameters_->DefaultBool("ssi.weight_by_scaling",
+                                                false);
+
+  // Time from the zero-lag line of the SAI from which to start searching
+  // for a maximum in the input SAI's temporal profile.
+  pitch_search_start_ms_ = parameters_->DefaultFloat(
+    "ssi.pitch_search_start_ms", 2.0f);
+
+  // Total width in cycles of the whole SSI
+  ssi_width_cycles_ = parameters_->DefaultFloat("ssi.width_cycles", 10.0f);
+
+  // Set to true to make the cycles axis logarithmic (ie indexing by gamma
+  // rather than by cycles) 
+  log_cycles_axis_ = parameters_->DefaultBool("ssi.log_cycles_axis", true);
+
+  // The centre frequency of the channel which will just fill the complete
+  // width of the SSI buffer
+  pivot_cf_ = parameters_->DefaultFloat("ssi.pivot_cf", 1000.0f);
 }
 
 ModuleSSI::~ModuleSSI() {
@@ -47,11 +75,10 @@
   buffer_length_ = input.buffer_length();
   channel_count_ = input.channel_count();
 
-  float lowest_cf = input.centre_frequency(0);
-  ssi_width_samples_ = sample_rate_ * ssi_width_cycles_ / lowest_cf;
+  ssi_width_samples_ = sample_rate_ * ssi_width_cycles_ / pivot_cf_;
   if (ssi_width_samples_ > buffer_length_) {
     ssi_width_samples_ = buffer_length_;
-    float cycles = ssi_width_samples_ * lowest_cf / sample_rate_;
+    float cycles = ssi_width_samples_ * pivot_cf_ / sample_rate_;
     LOG_INFO(_T("Requested SSI width of %f cycles is too long for the "
                 "input buffer length of %d samples. The SSI will be "
                 "truncated at %d samples wide. This corresponds to a width "
@@ -66,6 +93,30 @@
 void ModuleSSI::ResetInternal() {
 }
 
+int ModuleSSI::ExtractPitchIndex(const SignalBank &input) const {
+  // Generate temporal profile of the SAI
+  vector<float> sai_temporal_profile(buffer_length_, 0.0f);
+  for (int i = 0; i < buffer_length_; ++i) {
+    float val = 0.0f;
+    for (int ch = 0; ch < channel_count_; ++ch) {
+      val += input.sample(ch, i);
+    }
+    sai_temporal_profile[i] = val;
+  }
+
+  // Find pitch value
+  int start_sample = floor(pitch_search_start_ms_ * sample_rate_ / 1000.0f);
+  int max_idx = 0;
+  float max_val = 0.0f;
+  for (int i = start_sample; i < buffer_length_; ++i) {
+    if (sai_temporal_profile[i] > max_val) {
+      max_idx = i;
+      max_val = sai_temporal_profile[i];
+    }
+  }
+  return max_idx;
+}
+
 void ModuleSSI::Process(const SignalBank &input) {
   // Check to see if the module has been initialized. If not, processing
   // should not continue.
@@ -85,12 +136,28 @@
 
   output_.set_start_time(input.start_time());
 
+  int pitch_index = buffer_length_ - 1;
+  if (do_pitch_cutoff_) {
+    pitch_index = ExtractPitchIndex(input);
+  }
+
   for (int ch = 0; ch < channel_count_; ++ch) {
+    float centre_frequency = input.centre_frequency(ch);
     // Copy the buffer from input to output, addressing by h-value
     for (int i = 0; i < ssi_width_samples_; ++i) {
-      float h = static_cast<float>(i) * ssi_width_cycles_
-                / static_cast<float>(ssi_width_samples_);
-      float cycle_samples = sample_rate_ / input.centre_frequency(ch);
+      float h;
+      float cycle_samples = sample_rate_ / centre_frequency;
+      if (log_cycles_axis_) {
+        float gamma_min = -1.0f;
+        float gamma_max = log2(ssi_width_cycles_);
+        float gamma = gamma_min + (gamma_max - gamma_min)
+                                   * static_cast<float>(i)
+                                   / static_cast<float>(ssi_width_samples_);
+        h = pow(2.0f, gamma);
+      } else {
+        h = static_cast<float>(i) * ssi_width_cycles_
+            / static_cast<float>(ssi_width_samples_);
+      }
 
       // The index into the input array is a floating-point number, which is
       // split into a whole part and a fractional part. The whole part and
@@ -98,15 +165,38 @@
       // between input samples to yield an output sample.
       double whole_part;
       float frac_part = modf(h * cycle_samples, &whole_part);
-      int sample = static_cast<int>(whole_part);
+      int sample = floor(whole_part);
+
+      float weight = 1.0f;
+
+      int cutoff_index = buffer_length_ - 1;
+      if (do_pitch_cutoff_) {
+        if (pitch_index < cutoff_index) {
+          if (weight_by_cutoff_) {
+            weight *= static_cast<float>(buffer_length_)
+                      / static_cast<float>(pitch_index);
+          }
+          cutoff_index = pitch_index;
+        }
+      }
+
+      if (weight_by_scaling_) {
+        if (centre_frequency > pivot_cf_) {
+          weight *= (centre_frequency / pivot_cf_);
+        }
+      }
 
       float val;
-      if (sample < buffer_length_ - 1) {
+      if (sample < cutoff_index) {
         float curr_sample = input.sample(ch, sample);
         float next_sample = input.sample(ch, sample + 1);
-        val = curr_sample + frac_part * (next_sample - curr_sample);
+        val = weight * (curr_sample
+                        + frac_part * (next_sample - curr_sample));
       } else {
-        val = 0.0f;
+        // Set out-of-range values to a negative number to signify that 
+        // they really don't exist, and shouldn't be used in feature
+        // calculations.
+        val = -1.0f;
       }
       output_.set_sample(ch, i, val);
     }
--- a/src/Modules/SSI/ModuleSSI.h	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Modules/SSI/ModuleSSI.h	Thu Feb 25 22:02:00 2010 +0000
@@ -49,13 +49,20 @@
    */
   virtual bool InitializeInternal(const SignalBank &input);
 
+  int ExtractPitchIndex(const SignalBank &input) const;
+
   float sample_rate_;
   int buffer_length_;
   int channel_count_;
   int ssi_width_samples_;
   float ssi_width_cycles_;
+  float pivot_cf_;
 
   bool do_pitch_cutoff_;
+  bool weight_by_cutoff_;
+  bool weight_by_scaling_;
+  bool log_cycles_axis_;
+  float pitch_search_start_ms_;
 };
 }  // namespace aimc
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Modules/Strobes/ModuleLocalMax.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,144 @@
+// Copyright 2010, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/*!
+ * \file
+ * \brief 
+ *
+ * \author Thomas Walters <tom@acousticscale.org>
+ * \date created 2010/02/23
+ * \version \$Id$
+ */
+
+#include <math.h>
+#include "Modules/Strobes/ModuleLocalMax.h"
+
+namespace aimc {
+ModuleLocalMax::ModuleLocalMax(Parameters *params) : Module(params) {
+  module_description_ = "Local maximum strobe criterion: decaying threshold "
+                        "with timeout";
+  module_identifier_ = "local_max";
+  module_type_ = "strobes";
+  module_version_ = "$Id$";
+
+  decay_time_ms_ = parameters_->DefaultFloat("strobes.decay_time_ms", 20.0f);
+  timeout_ms_ = parameters_->DefaultFloat("strobes.timeout_ms", 3.0f);
+}
+
+ModuleLocalMax::~ModuleLocalMax() {
+}
+
+bool ModuleLocalMax::InitializeInternal(const SignalBank &input) {
+  // Copy the parameters of the input signal bank into internal variables, so
+  // that they can be checked later.
+  sample_rate_ = input.sample_rate();
+  buffer_length_ = input.buffer_length();
+  channel_count_ = input.channel_count();
+  output_.Initialize(input);
+  strobe_timeout_samples_ = floor(timeout_ms_ * sample_rate_ / 1000.0f);
+  strobe_decay_samples_ = floor(decay_time_ms_ * sample_rate_ / 1000.0f);
+  ResetInternal();
+  return true;
+}
+
+void ModuleLocalMax::ResetInternal() {
+  threshold_.clear();
+  threshold_.resize(channel_count_, 0.0f);
+
+  decay_constant_.clear();
+  decay_constant_.resize(channel_count_, 1.0f);
+
+  prev_sample_.clear();
+  prev_sample_.resize(channel_count_, 10000.0f);
+  curr_sample_.clear();
+  curr_sample_.resize(channel_count_, 5000.0f);
+  next_sample_.clear();
+  next_sample_.resize(channel_count_, 0.0f);
+}
+
+void ModuleLocalMax::Process(const SignalBank &input) {
+  // Check to see if the module has been initialized. If not, processing
+  // should not continue.
+  if (!initialized_) {
+    LOG_ERROR(_T("Module %s not initialized."), module_identifier_.c_str());
+    return;
+  }
+
+  // Check that ths input this time is the same as the input passed to
+  // Initialize()
+  if (buffer_length_ != input.buffer_length()
+      || channel_count_ != input.channel_count()) {
+    LOG_ERROR(_T("Mismatch between input to Initialize() and input to "
+                 "Process() in module %s."), module_identifier_.c_str());
+    return;
+  }
+
+  for (int ch = 0; ch < output_.channel_count(); ch++) {
+    output_.ResetStrobes(ch);
+  }
+  output_.set_start_time(input.start_time());
+  for (int i = 0; i < input.buffer_length(); i++) {
+    for (int ch = 0; ch < input.channel_count(); ++ch) {
+      // curr_sample is the sample at time (i - 1)
+      prev_sample_[ch] = curr_sample_[ch];
+      curr_sample_[ch] = next_sample_[ch];
+      next_sample_[ch] = input.sample(ch, i);
+      // Copy input signal to output signal
+      output_.set_sample(ch, i, input.sample(ch, i));
+
+      // If the current sample is above threshold, the threshold is raised to
+      // the level of the current sample, and decays from there.
+      if (curr_sample_[ch] >= threshold_[ch]) {
+        threshold_[ch] = curr_sample_[ch];
+        decay_constant_[ch] = threshold_[ch] / strobe_decay_samples_;
+
+        // If the current sample is also a peak, then it is a potential strobe
+        // point.
+        if (prev_sample_[ch] < curr_sample_[ch]
+            && next_sample_[ch] < curr_sample_[ch]) {
+          // If there are no strobes so far in this channel, then the sample
+          // is definitely a strobe (this means that the timeout is not 
+          // respected across frame boundaries. This is a minor bug, but I
+          // don't believe that it's serious enough to warrant updating the
+          // samples since last strobe all the time.)
+          int count = output_.strobe_count(ch);
+          if (count > 0) {
+            // If there are previous strobes, then calculate the time since
+            // the last one. If it's long enough, then this is a strobe point,
+            // if not, then just move on.
+            int samples_since_last = (i - 1) - output_.strobe(ch, count - 1);
+            if (samples_since_last > strobe_timeout_samples_) {
+              output_.AddStrobe(ch, i - 1);
+            }
+          } else {
+            output_.AddStrobe(ch, i - 1);
+          }
+        }
+      }
+
+      // Update the threshold, decaying as necessary
+      if (threshold_[ch] > decay_constant_[ch])
+        threshold_[ch] -= decay_constant_[ch];
+      else
+        threshold_[ch] = 0.0f;
+    }
+  }
+  PushOutput();
+}
+}  // namespace aimc
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Modules/Strobes/ModuleLocalMax.h	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,71 @@
+// Copyright 2010, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/*!
+ * \author Thomas Walters <tom@acousticscale.org>
+ * \date created 2010/02/23
+ * \version \$Id$
+ */
+
+#ifndef AIMC_MODULES_STROBES_LOCAL_MAX_H_
+#define AIMC_MODULES_STROBES_LOCAL_MAX_H_
+
+#include <vector>
+#include "Support/Module.h"
+
+namespace aimc {
+using std::vector;
+class ModuleLocalMax : public Module {
+ public:
+  explicit ModuleLocalMax(Parameters *pParam);
+  virtual ~ModuleLocalMax();
+
+  /*! \brief Process a buffer
+   */
+  virtual void Process(const SignalBank &input);
+
+ private:
+  /*! \brief Reset the internal state of the module
+   */
+  virtual void ResetInternal();
+
+  /*! \brief Prepare the module
+   *  \param input Input signal
+   *  \param output true on success false on failure
+   */
+  virtual bool InitializeInternal(const SignalBank &input);
+
+  float sample_rate_;
+  int buffer_length_;
+  int channel_count_;
+
+  float decay_time_ms_;
+  float timeout_ms_;
+  int strobe_timeout_samples_;
+  int strobe_decay_samples_;
+
+  vector<float> threshold_;
+  vector<float> decay_constant_;
+
+  vector<float> prev_sample_;
+  vector<float> curr_sample_;
+  vector<float> next_sample_;
+};
+}  // namespace aimc
+
+#endif  // AIMC_MODULES_STROBES_LOCAL_MAX_H_
--- a/src/Modules/Strobes/ModuleParabola.cc	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Modules/Strobes/ModuleParabola.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -75,12 +75,18 @@
 }
 
 void ModuleParabola::ResetInternal() {
+  threshold_.clear();
   threshold_.resize(channel_count_, 0.0f);
+  last_threshold_.clear();
   last_threshold_.resize(channel_count_, 0.0f);
+  samples_since_last_strobe_.clear();
   samples_since_last_strobe_.resize(channel_count_, 0);
 
+  prev_sample_.clear();
   prev_sample_.resize(channel_count_, 10000.0f);
+  curr_sample_.clear();
   curr_sample_.resize(channel_count_, 5000.0f);
+  next_sample_.clear();
   next_sample_.resize(channel_count_, 0.0f);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Scripts/Multi-slice_test.py	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# encoding: utf-8
+#
+# AIM-C: A C++ implementation of the Auditory Image Model
+# http://www.acousticscale.org/AIMC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Profiles_test.py
+
+Created by Thomas Walters on 2010-02-22.
+Copyright 2010 Thomas Walters <tom@acousticscale.org>
+Test the AIM-C model from filterbank to SSI profiles
+"""
+
+import aimc
+from scipy.io import wavfile
+from scipy import io
+import scipy
+import pylab
+from itertools import izip, chain, repeat
+
+def grouper(n, iterable, padvalue=None):
+    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
+    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
+    
+def BankToArray(out_bank):
+  channel_count = out_bank.channel_count()
+  out_buffer_length = out_bank.buffer_length()
+  out = scipy.zeros((channel_count, out_buffer_length))
+  for ch in range(0, channel_count):
+    for i in range(0, out_buffer_length):  
+      out[ch, i] = out_bank.sample(ch, i)
+  return out
+
+def StrobesToList(bank):
+  channel_count = bank.channel_count()
+  strobes = []
+  for ch in range(0, channel_count):
+    s = []
+    for i in range(0, bank.strobe_count(ch)):
+      s.append(bank.strobe(ch, i))
+    strobes.append(s)
+
+def main():
+  wave_path = "/Users/Tom/Documents/Work/PhD/HTK-AIM/Sounds/"
+  #features_path = "/Users/Tom/Documents/Work/PhD/HTK-AIM/work08-jess-original-rec_rubber/features/"
+  
+  file_name = "ii/ii172.5p112.5s100.0t+000itd"
+  
+  wave_suffix = ".wav"
+  features_suffix = ".mat"
+  
+  frame_period_ms = 10;
+    
+  wave_filename = wave_path + file_name + wave_suffix
+  #features_filename = features_path + file_name + features_suffix
+  
+  (sample_rate, input_wave) = wavfile.read(wave_filename)
+  wave_length = input_wave.size
+  buffer_length = int(frame_period_ms * sample_rate / 1000)
+ 
+  #pylab.plot(input_wave)
+  #pylab.show()
+  
+  input_sig = aimc.SignalBank()
+  input_sig.Initialize(1, buffer_length, sample_rate)
+  parameters = aimc.Parameters()
+  parameters.SetFloat("sai.frame_period_ms", 10.0)
+  parameters.SetInt("input.buffersize", 480)
+
+  mod_gt = aimc.ModuleGammatone(parameters)
+  mod_hl = aimc.ModuleHCL(parameters)
+  mod_strobes = aimc.ModuleLocalMax(parameters) 
+  mod_sai = aimc.ModuleSAI(parameters)
+  parameters.SetBool("ssi.pitch_cutoff", True)
+  parameters.SetBool("ssi.weight_by_cutoff", False)
+  parameters.SetBool("ssi.weight_by_scaling", True)
+  parameters.SetBool("ssi.log_cycles_axis", True)
+  mod_ssi = aimc.ModuleSSI(parameters) 
+  
+  parameters.SetFloat("nap.lowpass_cutoff", 100.0)
+  mod_nap_smooth = aimc.ModuleHCL(parameters)
+  mod_scaler = aimc.ModuleScaler(parameters)
+
+  parameters.SetBool("slice.all", False)
+  parameters.SetInt("slice.lower_index", 77)
+  parameters.SetInt("slice.upper_index", 150)
+  slice_1 = aimc.ModuleSlice(parameters)
+
+  parameters.SetInt("slice.lower_index", 210)
+  parameters.SetInt("slice.upper_index", 240)
+  slice_2 = aimc.ModuleSlice(parameters)
+
+  parameters.SetInt("slice.lower_index", 280)
+  parameters.SetInt("slice.upper_index", 304)
+  slice_3 = aimc.ModuleSlice(parameters)
+
+  parameters.SetInt("slice.lower_index", 328)
+  parameters.SetInt("slice.upper_index", 352)
+  slice_4 = aimc.ModuleSlice(parameters)
+
+  parameters.SetBool("slice.all", True)
+  slice_5 = aimc.ModuleSlice(parameters)
+  
+  nap_profile = aimc.ModuleSlice(parameters)
+
+  features_1 = aimc.ModuleGaussians(parameters)
+  features_2 = aimc.ModuleGaussians(parameters)
+  features_3 = aimc.ModuleGaussians(parameters)
+  features_4 = aimc.ModuleGaussians(parameters)
+  features_5 = aimc.ModuleGaussians(parameters)
+
+  mod_gt.AddTarget(mod_hl)
+  mod_gt.AddTarget(mod_nap_smooth)
+  mod_nap_smooth.AddTarget(nap_profile)
+  nap_profile.AddTarget(mod_scaler)
+  mod_hl.AddTarget(mod_strobes)
+  mod_strobes.AddTarget(mod_sai)
+  mod_sai.AddTarget(mod_ssi)
+  mod_ssi.AddTarget(slice_1)
+  mod_ssi.AddTarget(slice_2)
+  mod_ssi.AddTarget(slice_3)
+  mod_ssi.AddTarget(slice_4)
+  mod_ssi.AddTarget(slice_5)
+
+  slice_1.AddTarget(features_1)
+  slice_2.AddTarget(features_2)
+  slice_3.AddTarget(features_3)
+  slice_4.AddTarget(features_4)
+  slice_5.AddTarget(features_5)
+
+  mod_gt.Initialize(input_sig)
+  
+  correct_count = 0;
+  incorrect_count  = 0;
+  
+  scaled_wave = []
+  for sample in input_wave:
+    scaled_wave.append(float(sample / float(pow(2,15) - 1)))
+  i = 0
+  
+  wave_chunks = grouper(buffer_length, scaled_wave, 0)
+
+  out_bmm = []
+  out_nap = []
+  out_smooth_nap_profile = []
+  out_strobes = []
+  out_sais = []
+  out_ssis = []
+  out_slice_1 = []
+  out_slice_2 = []
+  out_slice_3 = []
+  out_slice_4 = []
+  out_slice_5 = []
+  out_feat_1 = []
+  out_feat_2 = []
+  out_feat_3 = []
+  out_feat_4 = []
+  out_feat_5 = []
+  for chunk in wave_chunks:
+    i = 0
+    for sample in chunk:
+      input_sig.set_sample(0, i, float(sample))
+      i += 1
+    mod_gt.Process(input_sig)
+    
+    #out_bmm.append(BankToArray(mod_gt.GetOutputBank()))
+    #out_nap.append(BankToArray(mod_hl.GetOutputBank()))
+    out_smooth_nap_profile.append(BankToArray(mod_scaler.GetOutputBank()))
+    #out_strobes.append(BankToArray(mod_strobes.GetOutputBank()))
+    #out_sais.append(BankToArray(mod_sai.GetOutputBank()))
+    out_ssis.append(BankToArray(mod_ssi.GetOutputBank()))
+    out_slice_1.append(BankToArray(slice_1.GetOutputBank()))
+    out_slice_2.append(BankToArray(slice_2.GetOutputBank()))
+    out_slice_3.append(BankToArray(slice_3.GetOutputBank()))
+    out_slice_4.append(BankToArray(slice_4.GetOutputBank()))
+    out_slice_5.append(BankToArray(slice_5.GetOutputBank()))
+    out_feat_1.append(BankToArray(features_1.GetOutputBank()))
+    out_feat_2.append(BankToArray(features_2.GetOutputBank()))
+    out_feat_3.append(BankToArray(features_3.GetOutputBank()))
+    out_feat_4.append(BankToArray(features_4.GetOutputBank()))
+    out_feat_5.append(BankToArray(features_5.GetOutputBank()))
+  
+  out_bank = mod_gt.GetOutputBank()
+  channel_count = out_bank.channel_count()
+  cfs = scipy.zeros((channel_count))
+  for ch in range(0, channel_count):
+    cfs[ch] = out_bank.centre_frequency(ch)
+  outmat = dict(bmm=out_bmm, nap=out_nap, sais=out_sais,
+                ssis=out_ssis, slice1=out_slice_1, slice2=out_slice_2, 
+                slice3=out_slice_3, slice4=out_slice_4, slice5=out_slice_5,
+                feat1=out_feat_1, feat2=out_feat_2, feat3=out_feat_3,
+                feat4=out_feat_4, feat5=out_feat_5,
+                nap_smooth=out_smooth_nap_profile, centre_freqs=cfs)
+  io.savemat("src/Scripts/profile_out.mat", outmat, oned_as='column')
+
+  pass
+
+
+if __name__ == '__main__':
+  main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Scripts/Strobes_test.py	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# encoding: utf-8
+#
+# AIM-C: A C++ implementation of the Auditory Image Model
+# http://www.acousticscale.org/AIMC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Profiles_test.py
+
+Created by Thomas Walters on 2010-02-22.
+Copyright 2010 Thomas Walters <tom@acousticscale.org>
+Test the AIM-C model from filterbank to SSI profiles
+"""
+
+import aimc
+from scipy.io import wavfile
+from scipy import io
+import scipy
+import pylab
+import numpy
+from itertools import izip, chain, repeat
+
+def grouper(n, iterable, padvalue=None):
+    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
+    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
+    
+def BankToArray(out_bank):
+  channel_count = out_bank.channel_count()
+  out_buffer_length = out_bank.buffer_length()
+  out = scipy.zeros((channel_count,out_buffer_length))
+  for ch in range(0, channel_count):
+    for i in range(0, out_buffer_length):  
+      out[ch, i] = out_bank.sample(ch, i)
+  return out
+
+def StrobesToList(bank):
+  channel_count = bank.channel_count()
+  out = scipy.zeros((channel_count,), dtype=numpy.object)
+  for ch in range(0, channel_count):
+    s = []
+    for i in range(0, bank.strobe_count(ch)):
+      s.append(bank.strobe(ch, i))
+    out[ch] = s
+  return out
+
+def main():
+  wave_path = "/Users/Tom/Documents/Work/PhD/HTK-AIM/Sounds/"
+  
+  file_name = "ii/ii172.5p112.5s100.0t+000itd"
+  
+  wave_suffix = ".wav"
+  
+  frame_period_ms = 10;
+    
+  wave_filename = wave_path + file_name + wave_suffix
+  
+  (sample_rate, input_wave) = wavfile.read(wave_filename)
+  wave_length = input_wave.size
+  buffer_length = int(frame_period_ms * sample_rate / 1000)
+ 
+  
+  input_sig = aimc.SignalBank()
+  input_sig.Initialize(1, buffer_length, sample_rate)
+  parameters = aimc.Parameters()
+  parameters.SetInt("input.buffersize", 480)
+
+  mod_gt = aimc.ModuleGammatone(parameters)
+  mod_hl = aimc.ModuleHCL(parameters)
+  mod_strobes = aimc.ModuleLocalMax(parameters) 
+
+  mod_gt.AddTarget(mod_hl)
+  mod_hl.AddTarget(mod_strobes)
+
+  mod_gt.Initialize(input_sig)
+  
+  correct_count = 0;
+  incorrect_count  = 0;
+  
+  scaled_wave = []
+  for sample in input_wave:
+    scaled_wave.append(float(sample / float(pow(2,15) - 1)))
+  i = 0
+  
+  wave_chunks = grouper(buffer_length, scaled_wave, 0)
+
+  out_nap = []
+  out_strobes = []
+
+  for chunk in wave_chunks:
+    i = 0
+    for sample in chunk:
+      input_sig.set_sample(0, i, float(sample))
+      i += 1
+    mod_gt.Process(input_sig) 
+    out_nap.append(BankToArray(mod_hl.GetOutputBank()))
+    out_strobes.append(StrobesToList(mod_strobes.GetOutputBank()))
+
+  outmat = dict(nap=out_nap, strobes=out_strobes)
+  io.savemat("src/Scripts/strobes_out.mat", outmat, oned_as='column')
+
+  pass
+
+
+if __name__ == '__main__':
+  main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Scripts/plot_feature_tracks.m	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,11 @@
+figure
+plot(data_r(1,:), 'b-');
+hold on
+plot(data_r(2,:), 'b--');
+plot(data_r(3,:), 'b:');
+
+
+plot(data2_r(1,:), 'r-');
+hold on
+plot(data2_r(2,:), 'r--');
+plot(data2_r(3,:), 'r:');
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Scripts/plot_strobes.m	Thu Feb 25 22:02:00 2010 +0000
@@ -0,0 +1,16 @@
+function plot_strobes(nap, strobes, frame_no)
+  n = squeeze(nap(frame_no,:,:));
+  s = strobes(frame_no,:);
+  figure;
+  mesh(n, 'MeshStyle', 'row', 'EdgeColor','black');
+  view([0.01 80]);
+  hold on;
+  for ch = 1:size(n, 1);
+    st = s{1,ch};
+    for i=1:length(st)
+      if (st(i) > 0)
+        plot3(st(i),ch,n(ch, st(i)) ,'Marker','o','MarkerFaceColor',[1 0 0],'MarkerEdgeColor','w','MarkerSize',6);
+      end
+    end
+  end
+  
\ No newline at end of file
--- a/src/Support/SignalBank.cc	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Support/SignalBank.cc	Thu Feb 25 22:02:00 2010 +0000
@@ -115,14 +115,6 @@
   return true;
 }
 
-float SignalBank::sample(int channel, int index) const {
-  return signals_[channel][index];
-}
-
-void SignalBank::set_sample(int channel, int index, float value) {
-  signals_[channel][index] = value;
-}
-
 int SignalBank::strobe(int channel, int index) const {
   return strobes_[channel][index];
 }
--- a/src/Support/SignalBank.h	Wed Feb 24 15:18:00 2010 +0000
+++ b/src/Support/SignalBank.h	Thu Feb 25 22:02:00 2010 +0000
@@ -59,8 +59,14 @@
     return signals_[channel];
   };
 
-  float sample(int channel, int index) const;
-  void set_sample(int channel, int index, float value);
+  inline float sample(int channel, int index) const {
+    return signals_[channel][index];
+  }
+
+  inline void set_sample(int channel, int index, float value) {
+    signals_[channel][index] = value;
+  }
+
   int strobe(int channel, int index) const;
   int strobe_count(int channel) const;
   void AddStrobe(int channel, int time);
--- a/swig/aim_modules.i	Wed Feb 24 15:18:00 2010 +0000
+++ b/swig/aim_modules.i	Thu Feb 25 22:02:00 2010 +0000
@@ -27,8 +27,10 @@
 #include "Modules/BMM/ModulePZFC.h"
 #include "Modules/NAP/ModuleHCL.h"
 #include "Modules/Strobes/ModuleParabola.h"
+#include "Modules/Strobes/ModuleLocalMax.h"
 #include "Modules/SAI/ModuleSAI.h"
 #include "Modules/SSI/ModuleSSI.h"
+#include "Modules/SNR/ModuleNoise.h"
 #include "Modules/Profile/ModuleSlice.h"
 #include "Modules/Profile/ModuleScaler.h"
 #include "Modules/Features/ModuleGaussians.h"
@@ -42,8 +44,10 @@
 %include "Modules/BMM/ModulePZFC.h"
 %include "Modules/NAP/ModuleHCL.h"
 %include "Modules/Strobes/ModuleParabola.h"
+%include "Modules/Strobes/ModuleLocalMax.h"
 %include "Modules/SAI/ModuleSAI.h"
 %include "Modules/SSI/ModuleSSI.h"
+%include "Modules/SNR/ModuleNoise.h"
 %include "Modules/Profile/ModuleSlice.h"
 %include "Modules/Profile/ModuleScaler.h"
 %include "Modules/Features/ModuleGaussians.h"
--- a/swig/setup.py	Wed Feb 24 15:18:00 2010 +0000
+++ b/swig/setup.py	Thu Feb 25 22:02:00 2010 +0000
@@ -34,13 +34,15 @@
                                    '../src/Modules/BMM/ModulePZFC.cc',
                                    '../src/Modules/NAP/ModuleHCL.cc',
                                    '../src/Modules/Strobes/ModuleParabola.cc',
+                                   '../src/Modules/Strobes/ModuleLocalMax.cc',
                                    '../src/Modules/SAI/ModuleSAI.cc',
                                    '../src/Modules/SSI/ModuleSSI.cc',
+                                   '../src/Modules/SNR/ModuleNoise.cc',
                                    '../src/Modules/Profile/ModuleSlice.cc',
                                    '../src/Modules/Profile/ModuleScaler.cc',
                                    '../src/Modules/Output/FileOutputHTK.cc'],
                         swig_opts = ['-c++','-I../src/'], 
-                        include_dirs=['../src/']
+                        include_dirs=['../src/', '/opt/local/include/']
                         )
 
 setup (name = 'aimc',