annotate trunk/src/Modules/SAI/ModuleSAI.cc @ 287:4b3a43b543dd

-<math.h> replaced wit <cmath> where possible -SSI support added but not yet tested
author tomwalters
date Fri, 19 Feb 2010 15:19:27 +0000
parents e55d0c225a57
children fe5ce00a64f5
rev   line source
tomwalters@268 1 // Copyright 2006-2010, Thomas Walters
tomwalters@268 2 //
tomwalters@268 3 // AIM-C: A C++ implementation of the Auditory Image Model
tomwalters@268 4 // http://www.acousticscale.org/AIMC
tomwalters@268 5 //
tomwalters@268 6 // This program is free software: you can redistribute it and/or modify
tomwalters@268 7 // it under the terms of the GNU General Public License as published by
tomwalters@268 8 // the Free Software Foundation, either version 3 of the License, or
tomwalters@268 9 // (at your option) any later version.
tomwalters@268 10 //
tomwalters@268 11 // This program is distributed in the hope that it will be useful,
tomwalters@268 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
tomwalters@268 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
tomwalters@268 14 // GNU General Public License for more details.
tomwalters@268 15 //
tomwalters@268 16 // You should have received a copy of the GNU General Public License
tomwalters@268 17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
tomwalters@268 18
tomwalters@268 19 /*! \file
tomwalters@268 20 * \brief SAI module
tomwalters@268 21 */
tomwalters@268 22
tomwalters@268 23 /*
tomwalters@268 24 * \author Thomas Walters <tom@acousticscale.org>
tomwalters@268 25 * \date created 2007/08/29
tomwalters@268 26 * \version \$Id: ModuleSAI.cc 4 2010-02-03 18:44:58Z tcw $
tomwalters@268 27 */
tomwalters@287 28 #include <cmath>
tomwalters@268 29
tomwalters@268 30 #include "Modules/SAI/ModuleSAI.h"
tomwalters@268 31
tomwalters@277 32 namespace aimc {
tomwalters@268 33 ModuleSAI::ModuleSAI(Parameters *parameters) : Module(parameters) {
tomwalters@277 34 module_identifier_ = "weighted_sai";
tomwalters@277 35 module_type_ = "sai";
tomwalters@268 36 module_description_ = "Stabilised auditory image";
tomwalters@277 37 module_version_ = "$Id: ModuleSAI.cc 4 2010-02-03 18:44:58Z tcw $";
tomwalters@268 38
tomwalters@268 39 min_delay_ms_ = parameters_->DefaultFloat("sai.min_delay_ms", 0.0f);
tomwalters@268 40 max_delay_ms_ = parameters_->DefaultFloat("sai.max_delay_ms", 35.0f);
tomwalters@268 41 strobe_weight_alpha_ = parameters_->DefaultFloat("sai.strobe_weight_alpha",
tomwalters@268 42 0.5f);
tomwalters@268 43 buffer_memory_decay_ = parameters_->DefaultFloat("sai.buffer_memory_decay",
tomwalters@268 44 0.03f);
tomwalters@268 45 frame_period_ms_ = parameters_->DefaultFloat("sai.frame_period_ms", 20.0f);
tomwalters@268 46
tomwalters@277 47 max_concurrent_strobes_
tomwalters@277 48 = parameters_->DefaultInt("sai.max_concurrent_strobes", 50);
tomwalters@277 49
tomwalters@277 50 min_strobe_delay_idx_ = 0;
tomwalters@277 51 max_strobe_delay_idx_ = 0;
tomwalters@268 52 sai_decay_factor_ = 0.0f;
tomwalters@277 53 fire_counter_ = 0;
tomwalters@268 54 }
tomwalters@268 55
tomwalters@268 56 bool ModuleSAI::InitializeInternal(const SignalBank &input) {
tomwalters@268 57 // The SAI output bank must be as long as the SAI's Maximum delay.
tomwalters@268 58 // One sample is added to the SAI buffer length to account for the
tomwalters@268 59 // zero-lag point
tomwalters@278 60 int sai_buffer_length = 1 + floor(input.sample_rate() * max_delay_ms_
tomwalters@278 61 / 1000.0f);
tomwalters@277 62 channel_count_ = input.channel_count();
tomwalters@268 63
tomwalters@268 64 // Make an output SignalBank with the same number of channels and centre
tomwalters@268 65 // frequencies as the input, but with a different buffer length
tomwalters@268 66 if (!output_.Initialize(input.channel_count(),
tomwalters@268 67 sai_buffer_length,
tomwalters@277 68 input.sample_rate())) {
tomwalters@268 69 LOG_ERROR("Failed to create output buffer in SAI module");
tomwalters@268 70 return false;
tomwalters@268 71 }
tomwalters@280 72 for (int i = 0; i < input.channel_count(); ++i) {
tomwalters@277 73 output_.set_centre_frequency(i, input.centre_frequency(i));
tomwalters@268 74 }
tomwalters@268 75
tomwalters@268 76 // sai_temp_ will be initialized to zero
tomwalters@268 77 if (!sai_temp_.Initialize(output_)) {
tomwalters@268 78 LOG_ERROR("Failed to create temporary buffer in SAI module");
tomwalters@268 79 return false;
tomwalters@268 80 }
tomwalters@268 81
tomwalters@277 82 frame_period_samples_ = floor(input.sample_rate() * frame_period_ms_
tomwalters@277 83 / 1000.0f);
tomwalters@277 84 min_strobe_delay_idx_ = floor(input.sample_rate() * min_delay_ms_
tomwalters@277 85 / 1000.0f);
tomwalters@277 86 max_strobe_delay_idx_ = floor(input.sample_rate() * max_delay_ms_
tomwalters@277 87 / 1000.0f);
tomwalters@268 88
tomwalters@268 89 // Make sure we don't go past the output buffer's upper bound
tomwalters@277 90 if (max_strobe_delay_idx_ > output_.buffer_length()) {
tomwalters@268 91 max_strobe_delay_idx_ = output_.buffer_length();
tomwalters@277 92 }
tomwalters@268 93
tomwalters@268 94 // Define decay factor from time since last sample (see ti2003)
tomwalters@268 95 sai_decay_factor_ = pow(0.5f, 1.0f / (buffer_memory_decay_
tomwalters@268 96 * input.sample_rate()));
tomwalters@268 97
tomwalters@268 98 // Precompute strobe weights
tomwalters@268 99 strobe_weights_.resize(max_concurrent_strobes_);
tomwalters@268 100 for (int n = 0; n < max_concurrent_strobes_; ++n) {
tomwalters@277 101 strobe_weights_[n] = pow(1.0f / (n + 1), strobe_weight_alpha_);
tomwalters@268 102 }
tomwalters@268 103
tomwalters@277 104 ResetInternal();
tomwalters@268 105
tomwalters@268 106 return true;
tomwalters@268 107 }
tomwalters@268 108
tomwalters@275 109 void ModuleSAI::ResetInternal() {
tomwalters@277 110 // Active Strobes
tomwalters@277 111 active_strobes_.clear();
tomwalters@277 112 active_strobes_.resize(channel_count_);
tomwalters@277 113 fire_counter_ = frame_period_samples_ - 1;
tomwalters@268 114 }
tomwalters@268 115
tomwalters@268 116 void ModuleSAI::Process(const SignalBank &input) {
tomwalters@268 117 // Reset the next strobe times
tomwalters@268 118 next_strobes_.clear();
tomwalters@268 119 next_strobes_.resize(output_.channel_count(), 0);
tomwalters@268 120
tomwalters@268 121 // Offset the times on the strobes from the previous buffer
tomwalters@277 122 for (int ch = 0; ch < input.channel_count(); ++ch) {
tomwalters@277 123 active_strobes_[ch].ShiftStrobes(input.buffer_length());
tomwalters@268 124 }
tomwalters@268 125
tomwalters@268 126 // Loop over samples to make the SAI
tomwalters@277 127 for (int i = 0; i < input.buffer_length(); ++i) {
tomwalters@268 128 float decay_factor = pow(sai_decay_factor_, fire_counter_);
tomwalters@268 129 // Loop over channels
tomwalters@277 130 for (int ch = 0; ch < input.channel_count(); ++ch) {
tomwalters@268 131 // Local convenience variables
tomwalters@277 132 StrobeList &active_strobes = active_strobes_[ch];
tomwalters@277 133 int next_strobe_index = next_strobes_[ch];
tomwalters@268 134
tomwalters@277 135 // Update strobes
tomwalters@268 136 // If we are up to or beyond the next strobe...
tomwalters@277 137 if (next_strobe_index < input.strobe_count(ch)) {
tomwalters@277 138 if (i == input.strobe(ch, next_strobe_index)) {
tomwalters@277 139 // A new strobe has arrived.
tomwalters@277 140 // If there are too many strobes active, then get rid of the
tomwalters@277 141 // earliest one
tomwalters@277 142 if (active_strobes.strobe_count() >= max_concurrent_strobes_) {
tomwalters@277 143 active_strobes.DeleteFirstStrobe();
tomwalters@268 144 }
tomwalters@268 145
tomwalters@277 146 // Add the active strobe to the list of current strobes and
tomwalters@277 147 // calculate the strobe weight
tomwalters@277 148 float weight = 1.0f;
tomwalters@277 149 if (active_strobes.strobe_count() > 0) {
tomwalters@277 150 int last_strobe_time = active_strobes.Strobe(
tomwalters@277 151 active_strobes.strobe_count() - 1).time;
tomwalters@277 152
tomwalters@277 153 // If the strobe occured within 10 impulse-response
tomwalters@277 154 // cycles of the previous strobe, then lower its weight
tomwalters@277 155 weight = (i - last_strobe_time) / input.sample_rate()
tomwalters@277 156 * input.centre_frequency(ch) / 10.0f;
tomwalters@277 157 if (weight > 1.0f)
tomwalters@277 158 weight = 1.0f;
tomwalters@277 159 }
tomwalters@277 160 active_strobes.AddStrobe(i, weight);
tomwalters@277 161 next_strobe_index++;
tomwalters@277 162
tomwalters@277 163
tomwalters@277 164 // Update the strobe weights
tomwalters@268 165 float total_strobe_weight = 0.0f;
tomwalters@277 166 for (int si = 0; si < active_strobes.strobe_count(); ++si) {
tomwalters@277 167 total_strobe_weight += (active_strobes.Strobe(si).weight
tomwalters@277 168 * strobe_weights_[active_strobes.strobe_count() - si - 1]);
tomwalters@268 169 }
tomwalters@277 170 for (int si = 0; si < active_strobes.strobe_count(); ++si) {
tomwalters@277 171 active_strobes.SetWorkingWeight(si,
tomwalters@277 172 (active_strobes.Strobe(si).weight
tomwalters@277 173 * strobe_weights_[active_strobes.strobe_count() - si - 1])
tomwalters@277 174 / total_strobe_weight);
tomwalters@268 175 }
tomwalters@268 176 }
tomwalters@268 177 }
tomwalters@268 178
tomwalters@277 179 // Remove inactive strobes
tomwalters@277 180 while (active_strobes.strobe_count() > 0) {
tomwalters@277 181 // Get the relative time of the first strobe, and see if it exceeds
tomwalters@277 182 // the maximum allowed time.
tomwalters@277 183 if ((i - active_strobes.Strobe(0).time) > max_strobe_delay_idx_)
tomwalters@277 184 active_strobes.DeleteFirstStrobe();
tomwalters@268 185 else
tomwalters@268 186 break;
tomwalters@268 187 }
tomwalters@268 188
tomwalters@277 189 // Update the SAI buffer with the weighted effect of all the active
tomwalters@277 190 // strobes at the current sample
tomwalters@277 191 for (int si = 0; si < active_strobes.strobe_count(); ++si) {
tomwalters@277 192 // Add the effect of active strobe at correct place in the SAI buffer
tomwalters@277 193 // Calculate 'delay', the time from the strobe event to now
tomwalters@277 194 int delay = i - active_strobes.Strobe(si).time;
tomwalters@268 195
tomwalters@268 196 // If the delay is greater than the (user-set)
tomwalters@268 197 // minimum strobe delay, the strobe can be used
tomwalters@277 198 if (delay >= min_strobe_delay_idx_ && delay < max_strobe_delay_idx_) {
tomwalters@268 199 // The value at be added to the SAI
tomwalters@277 200 float sig = input.sample(ch, i);
tomwalters@268 201
tomwalters@268 202 // Weight the sample correctly
tomwalters@277 203 sig *= active_strobes.Strobe(si).working_weight;
tomwalters@268 204
tomwalters@268 205 // Adjust the weight acording to the number of samples until the
tomwalters@268 206 // next output frame
tomwalters@277 207 sig *= decay_factor;
tomwalters@268 208
tomwalters@268 209 // Update the temporary SAI buffer
tomwalters@277 210 output_.set_sample(ch, delay, output_.sample(ch, delay) + sig);
tomwalters@268 211 }
tomwalters@268 212 }
tomwalters@268 213
tomwalters@277 214 next_strobes_[ch] = next_strobe_index;
tomwalters@280 215 } // End loop over channels
tomwalters@268 216
tomwalters@277 217 fire_counter_--;
tomwalters@268 218
tomwalters@277 219 // Check to see if we need to output an SAI frame on this sample
tomwalters@277 220 if (fire_counter_ <= 0) {
tomwalters@268 221 // Decay the SAI by the correct amount and add the current output frame
tomwalters@277 222 float decay = pow(sai_decay_factor_, frame_period_samples_);
tomwalters@268 223
tomwalters@277 224 for (int ch = 0; ch < input.channel_count(); ++ch) {
tomwalters@268 225 for (int i = 0; i < output_.buffer_length(); ++i) {
tomwalters@277 226 output_.set_sample(ch, i,
tomwalters@277 227 sai_temp_[ch][i] + output_[ch][i] * decay);
tomwalters@268 228 }
tomwalters@268 229 }
tomwalters@268 230
tomwalters@268 231 // Zero the temporary signal
tomwalters@268 232 for (int ch = 0; ch < sai_temp_.channel_count(); ++ch) {
tomwalters@268 233 for (int i = 0; i < sai_temp_.buffer_length(); ++i) {
tomwalters@268 234 sai_temp_.set_sample(ch, i, 0.0f);
tomwalters@268 235 }
tomwalters@268 236 }
tomwalters@268 237
tomwalters@277 238 fire_counter_ = frame_period_samples_ - 1;
tomwalters@268 239
tomwalters@277 240 // Transfer the current time to the output buffer
tomwalters@277 241 output_.set_start_time(input.start_time() + i);
tomwalters@268 242 PushOutput();
tomwalters@268 243 }
tomwalters@280 244 } // End loop over samples
tomwalters@268 245 }
tomwalters@268 246
tomwalters@268 247 ModuleSAI::~ModuleSAI() {
tomwalters@268 248 }
tomwalters@277 249 } // namespace aimc