Mercurial > hg > aimc
view trunk/src/Modules/SAI/ModuleSAI.cc @ 706:f8e90b5d85fd tip
Delete CARFAC code from this repository.
It has been moved to https://github.com/google/carfac
Please email me with your github username to get access.
I've also created a new mailing list to discuss CARFAC development:
https://groups.google.com/forum/#!forum/carfac-dev
author | ronw@google.com |
---|---|
date | Thu, 18 Jul 2013 20:56:51 +0000 |
parents | b58758854b88 |
children |
line wrap: on
line source
// Copyright 2006-2010, Thomas Walters // // AIM-C: A C++ implementation of the Auditory Image Model // http://www.acousticscale.org/AIMC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /*! \file * \brief SAI module */ /* * \author Thomas Walters <tom@acousticscale.org> * \date created 2007/08/29 * \version \$Id$ */ #include <cmath> #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_version_ = "$Id$"; min_delay_ms_ = parameters_->DefaultFloat("sai.min_delay_ms", 0.0f); max_delay_ms_ = parameters_->DefaultFloat("sai.max_delay_ms", 35.0f); strobe_weight_alpha_ = parameters_->DefaultFloat("sai.strobe_weight_alpha", 0.5f); buffer_memory_decay_ = parameters_->DefaultFloat("sai.buffer_memory_decay", 0.03f); frame_period_ms_ = parameters_->DefaultFloat("sai.frame_period_ms", 20.0f); 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; 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 + floor(input.sample_rate() * max_delay_ms_ / 1000.0f); 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())) { 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.centre_frequency(i)); } // sai_temp_ will be initialized to zero if (!sai_temp_.Initialize(output_)) { LOG_ERROR("Failed to create temporary buffer in SAI module"); return false; } 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()) { 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())); // 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_); } ResetInternal(); return true; } void ModuleSAI::ResetInternal() { // Active Strobes output_.Clear(); sai_temp_.Clear(); active_strobes_.clear(); active_strobes_.resize(channel_count_); fire_counter_ = frame_period_samples_ - 1; } void ModuleSAI::Process(const SignalBank &input) { // 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 (int ch = 0; ch < input.channel_count(); ++ch) { active_strobes_[ch].ShiftStrobes(input.buffer_length()); } // Loop over samples to make the SAI for (int i = 0; i < input.buffer_length(); ++i) { float decay_factor = pow(sai_decay_factor_, fire_counter_); // Loop over channels for (int ch = 0; ch < input.channel_count(); ++ch) { // Local convenience variables StrobeList &active_strobes = active_strobes_[ch]; int next_strobe_index = next_strobes_[ch]; // Update strobes // If we are up to or beyond the next strobe... 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(); } // 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 = 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 = 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 (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; } // 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 (delay >= min_strobe_delay_idx_ && delay < max_strobe_delay_idx_) { // The value at be added to the SAI float sig = input.sample(ch, i); // Weight the sample correctly sig *= active_strobes.Strobe(si).working_weight; // Adjust the weight acording to the number of samples until the // next output frame sig *= decay_factor; // Update the temporary SAI buffer sai_temp_.set_sample(ch, delay, sai_temp_.sample(ch, delay) + sig); } } next_strobes_[ch] = next_strobe_index; } // End loop over channels fire_counter_--; // 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_, frame_period_samples_); for (int ch = 0; ch < input.channel_count(); ++ch) { for (int i = 0; i < output_.buffer_length(); ++i) { output_.set_sample(ch, i, sai_temp_[ch][i] + output_[ch][i] * decay); } } // Zero the temporary signal for (int ch = 0; ch < sai_temp_.channel_count(); ++ch) { for (int i = 0; i < sai_temp_.buffer_length(); ++i) { sai_temp_.set_sample(ch, i, 0.0f); } } fire_counter_ = frame_period_samples_ - 1; // Transfer the current time to the output buffer output_.set_start_time(input.start_time() + i); PushOutput(); } } // End loop over samples } ModuleSAI::~ModuleSAI() { } } // namespace aimc