blob: 6346ebaa42484244341857d728b275315672514c [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/evaluator.h"
#include <algorithm>
#include <iomanip>
#include "include/util.h"
namespace autotest_client {
namespace audio {
Evaluator::Evaluator(const ParamConfig& config, int range)
: filter_(2 * range + 1),
bin_range_(range),
active_channels_(config.active_mic_channels),
num_channels_(config.num_mic_channels),
buffer_size_(config.fft_size),
format_(config.format),
sample_rate_(config.sample_rate),
bin_(range * 2 + 1),
pass_threshold_(config.pass_threshold),
is_debug_(config.verbose) {
// Initializes original expected filter.
filter_[bin_range_] = 1; // center
double mean = 1.0;
double sigma = 1.0; // standard deviation
// Normalization.
mean /= filter_.size();
sigma = sqrt(sigma / filter_.size() - mean * mean);
for (auto& x : filter_) {
x = (x - mean) / sigma;
}
// Estimates max trial.
// max_trial_ is the reverse of allowed delay plus the threshold to pass.
// And +2 is for the acceptable variation.
max_trial_ =
config.allowed_delay_millisecs / 1000 * sample_rate_ / buffer_size_
+ pass_threshold_ + 2;
}
// If passed all channel then return true, otherwise false.
// It will automatically record the current accumulate confidence
// for the set frequency.
int Evaluator::Evaluate(RecordClient *recorder,
int center_bin,
vector<bool> *single_output) {
bool freq_pass_all;
auto& single_round = *single_output;
vector< vector<double> > buffer(
num_channels_, vector<double>(buffer_size_));
vector<double> accum_confidence(num_channels_);
int trial;
for (trial = 1;
trial <= max_trial_ && !freq_pass_all;
++trial) {
freq_pass_all = true;
if (recorder->Record(&buffer, buffer_size_) <= 0) {
cerr << "Retrieve recorded data error.\n";
assert(false);
}
// Extends samples to complex samples.
vector< vector<double> > complex_sample(
num_channels_, vector<double>(buffer_size_ * 2));
for (int ch = 0; ch < num_channels_; ++ch) {
auto ptr = complex_sample[ch].begin();
for (double s : buffer[ch]) {
*(ptr++) = s;
*(ptr++) = 0.0;
}
}
// Evaluates all channels.
for (auto channel : active_channels_) {
if (accum_confidence[channel] >= pass_threshold_) continue;
accum_confidence[channel] += std::max(
EstimateChannel(&(complex_sample[channel]), center_bin), 0.0);
if (accum_confidence[channel] < pass_threshold_) {
freq_pass_all = false;
} else {
single_round[channel] = true;
}
}
}
return trial;
}
double Evaluator::EstimateChannel(vector<double> *cell_ptr, int center_bin) {
FFT(cell_ptr);
auto& cell = *cell_ptr;
double confidence = 0.0, mean = 0.0, sigma = 0.0;
for (int bin = (center_bin-bin_range_);
bin <= (center_bin+bin_range_);
++bin) {
int index = bin - (center_bin - bin_range_);
bin_[index] = SquareAbs(cell[2 * bin], cell[2 * bin + 1]) / cell.size();
if (is_debug_) std::cerr << bin_[index] << " ";
confidence += bin_[index] * filter_[index];
mean += bin_[index];
sigma += bin_[index] * bin_[index];
}
if (is_debug_) std::cerr << endl;
// Avoids divide by zero.
if (std::abs(sigma) < 1e-9) {
return 0.0;
}
const double power_ratio = bin_[bin_range_] / mean;
mean /= filter_.size();
sigma = sqrt(sigma / filter_.size() - mean * mean);
confidence /= (sigma * filter_.size());
if (is_debug_)
std::cerr << "power: " << power_ratio
<< " conf: " << confidence << std::endl;
return power_ratio * confidence;
}
void Evaluator::FFT(vector<double> *data_ptr) const {
auto& data = *data_ptr;
unsigned order, pos = 1, size = data.size();
// Reverses binary reindexing.
for (unsigned i = 1; i < size; i += 2) {
if (pos > i) {
std::swap(data[pos - 1], data[i - 1]);
std::swap(data[pos], data[i]);
}
order = size / 2;
while (order >= 2 && pos > order) {
pos -= order;
order >>= 1;
}
pos += order;
}
// Danielson-Lanczos lemma.
unsigned mmax = 2, step;
double theta, wtemp;
double cur[2], pre[2], temp[2]; // [0] real, [1] complex
mmax = 2;
while (size > mmax) {
step = mmax << 1;
theta = -(2 * M_PI / mmax);
wtemp = sin(theta / 2);
pre[0] = -2.0 * wtemp * wtemp;
pre[1] = sin(theta);
cur[0] = 1.0;
cur[1] = 0.0;
for (unsigned k = 1; k < mmax; k += 2) {
for (unsigned i = k; i <= size; i += step) {
pos = i + mmax;
temp[0] = cur[0] * data[pos - 1] - cur[1] * data[pos];
temp[1] = cur[0] * data[pos] + cur[1] * data[pos - 1];
data[pos - 1] = data[i - 1] - temp[0];
data[pos] = data[i] - temp[1];
data[i - 1] += temp[0];
data[i] += temp[1];
}
wtemp = cur[0];
cur[0] += wtemp * pre[0] - cur[1] * pre[1];
cur[1] += cur[1] * pre[0] + wtemp * pre[1];
}
mmax = step;
}
}
} // namespace audio
} // namespace autotest_client