annotate carfac/sai.cc @ 654:a1b82b240328

Rename variables to be consistent with the rest of the library.
author ronw@google.com
date Thu, 27 Jun 2013 15:30:46 +0000
parents f926e0892dee
children 7a0031c321da
rev   line source
ronw@642 1 // Copyright 2013, Google, Inc.
ronw@642 2 // Author: Ron Weiss <ronw@google.com>
ronw@642 3 //
ronw@642 4 // This C++ file is part of an implementation of Lyon's cochlear model:
ronw@642 5 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
ronw@642 6 // to supplement Lyon's upcoming book "Human and Machine Hearing"
ronw@642 7 //
ronw@642 8 // Licensed under the Apache License, Version 2.0 (the "License");
ronw@642 9 // you may not use this file except in compliance with the License.
ronw@642 10 // You may obtain a copy of the License at
ronw@642 11 //
ronw@642 12 // http://www.apache.org/licenses/LICENSE-2.0
ronw@642 13 //
ronw@642 14 // Unless required by applicable law or agreed to in writing, software
ronw@642 15 // distributed under the License is distributed on an "AS IS" BASIS,
ronw@642 16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ronw@642 17 // See the License for the specific language governing permissions and
ronw@642 18 // limitations under the License.
ronw@642 19
ronw@647 20 #include "sai.h"
ronw@647 21
ronw@642 22 #include <assert.h>
ronw@642 23
ronw@642 24 SAI::SAI(const SAIParams& params) : params_(params) {
ronw@650 25 Redesign(params);
ronw@650 26 }
ronw@650 27
ronw@650 28 void SAI::Redesign(const SAIParams& params) {
ronw@642 29 assert(params_.window_width > params_.width &&
ronw@642 30 "SAI window_width must be larger than width.");
ronw@642 31
ronw@642 32 int buffer_width = params_.width +
ronw@654 33 static_cast<int>((1 + static_cast<float>(params_.num_window_pos - 1)/2) *
ronw@642 34 params_.window_width);
ronw@654 35 input_buffer_.setZero(params_.num_channels, buffer_width);
ronw@654 36 output_buffer_.setZero(params_.num_channels, params_.width);
ronw@642 37
ronw@642 38 window_.setLinSpaced(params_.window_width, kPi / params_.window_width, kPi)
ronw@642 39 .sin();
ronw@642 40 }
ronw@642 41
ronw@650 42 void SAI::Reset() {
ronw@650 43 input_buffer_.setZero();
ronw@650 44 output_buffer_.setZero();
ronw@650 45 }
ronw@650 46
alexbrandmeyer@643 47 void SAI::RunSegment(const std::vector<ArrayX>& input,
alexbrandmeyer@643 48 ArrayXX* output_frame) {
ronw@642 49 assert(!input.empty() || input.size() <= params_.window_width &&
ronw@642 50 "Unexpected input size.");
ronw@654 51 assert(input[0].size() == params_.num_channels &&
ronw@642 52 "Unexpected input frame size.");
ronw@642 53
ronw@642 54 // Append new data to the input buffer.
ronw@654 55 int num_shift = input.size();
ronw@654 56 int shift_width = input_buffer_.cols() - num_shift;
ronw@654 57 input_buffer_.topLeftCorner(params_.num_channels, shift_width).swap(
ronw@654 58 input_buffer_.block(0, num_shift, params_.num_channels, shift_width));
ronw@642 59 for (int i = 0; i < input.size(); ++i) {
ronw@642 60 input_buffer_.block(0, shift_width + i, input[i].size(), 1) = input[i];
ronw@642 61 }
ronw@642 62 // Zero-pad the buffer if necessary.
ronw@642 63 if (input.size() < params_.window_width) {
ronw@642 64 int pad_width = params_.window_width - input.size();
ronw@654 65 input_buffer_.topRightCorner(params_.num_channels, pad_width).setZero();
ronw@642 66 }
ronw@642 67
ronw@642 68 StabilizeSegment(input_buffer_, &output_buffer_);
ronw@642 69 *output_frame = output_buffer_;
ronw@642 70 }
ronw@642 71
alexbrandmeyer@643 72 void SAI::StabilizeSegment(const ArrayXX& input_buffer,
alexbrandmeyer@643 73 ArrayXX* output_buffer) const {
ronw@642 74 // Windows are always approximately 50% overlapped.
ronw@642 75 float window_hop = params_.window_width / 2;
ronw@642 76 int window_start = (input_buffer.cols() - params_.window_width) -
ronw@654 77 (params_.num_window_pos - 1) * window_hop;
ronw@642 78 int window_range_start = window_start - params_.future_lags - 1;
ronw@642 79 int offset_range_start = window_start - params_.width;
ronw@642 80 assert(offset_range_start >= 0);
ronw@654 81 for (int i = 0; i < params_.num_channels; ++i) {
ronw@642 82 // TODO(ronw): Rename this here and in the Matlab code since the
ronw@642 83 // input doesn't have to contain naps.
alexbrandmeyer@643 84 const ArrayX& nap_wave = input_buffer.row(i);
ronw@642 85 // TODO(ronw): Smooth row.
ronw@642 86
ronw@654 87 for (int w = 0; w < params_.num_window_pos; ++w) {
ronw@642 88 int current_window_offset = w * window_hop;
ronw@642 89 // Choose a trigger point.
ronw@642 90 int trigger_time;
alexbrandmeyer@643 91 const ArrayX& trigger_window =
ronw@642 92 nap_wave.segment(window_range_start + current_window_offset,
ronw@642 93 params_.window_width);
ronw@642 94 FPType peak_val = (trigger_window * window_).maxCoeff(&trigger_time);
ronw@642 95 if (peak_val <= 0) {
ronw@642 96 peak_val = window_.maxCoeff(&trigger_time);
ronw@642 97 }
ronw@642 98 trigger_time += current_window_offset;
ronw@642 99
ronw@642 100 // Blend the window following the trigger into the output
ronw@642 101 // buffer, weighted according to the the trigger strength (0.05
ronw@642 102 // to near 1.0).
ronw@642 103 FPType alpha = (0.025 + peak_val) / (0.5 + peak_val);
ronw@642 104 output_buffer->row(i) *= 1 - alpha;
ronw@642 105 output_buffer->row(i) += alpha *
ronw@642 106 nap_wave.segment(trigger_time + offset_range_start, params_.width);
ronw@642 107 }
ronw@642 108 }
ronw@642 109 }