| Index: webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc
|
| diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc b/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e6fc3fa6a73b39aee82ea0e039e2ed6dc70a6f53
|
| --- /dev/null
|
| +++ b/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc
|
| @@ -0,0 +1,287 @@
|
| +/*
|
| + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +
|
| +#include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h"
|
| +
|
| +#include <algorithm>
|
| +#include <cmath>
|
| +#include <cstring>
|
| +
|
| +using std::complex;
|
| +
|
| +namespace {
|
| +
|
| +// Return |current| changed towards |target|, with the change being at most
|
| +// |limit|.
|
| +inline float UpdateFactor(float target, float current, float limit) {
|
| + float delta = fabsf(target - current);
|
| + float sign = copysign(1.0f, target - current);
|
| + return current + sign * fminf(delta, limit);
|
| +}
|
| +
|
| +// std::isfinite for complex numbers.
|
| +inline bool cplxfinite(complex<float> c) {
|
| + return std::isfinite(c.real()) && std::isfinite(c.imag());
|
| +}
|
| +
|
| +// std::isnormal for complex numbers.
|
| +inline bool cplxnormal(complex<float> c) {
|
| + return std::isnormal(c.real()) && std::isnormal(c.imag());
|
| +}
|
| +
|
| +// Apply a small fudge to degenerate complex values. The numbers in the array
|
| +// were chosen randomly, so that even a series of all zeroes has some small
|
| +// variability.
|
| +inline complex<float> zerofudge(complex<float> c) {
|
| + const static complex<float> fudge[7] = {
|
| + {0.001f, 0.002f}, {0.008f, 0.001f}, {0.003f, 0.008f}, {0.0006f, 0.0009f},
|
| + {0.001f, 0.004f}, {0.003f, 0.004f}, {0.002f, 0.009f}
|
| + };
|
| + static int fudge_index = 0;
|
| + if (cplxfinite(c) && !cplxnormal(c)) {
|
| + fudge_index = (fudge_index + 1) % 7;
|
| + return c + fudge[fudge_index];
|
| + }
|
| + return c;
|
| +}
|
| +
|
| +// Incremental mean computation. Return the mean of the series with the
|
| +// mean |mean| with added |data|.
|
| +inline complex<float> NewMean(complex<float> mean, complex<float> data,
|
| + int count) {
|
| + return mean + (data - mean) / static_cast<float>(count);
|
| +}
|
| +
|
| +inline void AddToMean(complex<float> data, int count, complex<float>* mean) {
|
| + (*mean) = NewMean(*mean, data, count);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +using std::min;
|
| +
|
| +namespace webrtc {
|
| +
|
| +namespace intelligibility {
|
| +
|
| +static const int kWindowBlockSize = 10;
|
| +
|
| +VarianceArray::VarianceArray(int freqs, StepType type, int window_size,
|
| + float decay)
|
| + : running_mean_(new complex<float>[freqs]()),
|
| + running_mean_sq_(new complex<float>[freqs]()),
|
| + sub_running_mean_(new complex<float>[freqs]()),
|
| + sub_running_mean_sq_(new complex<float>[freqs]()),
|
| + variance_(new float[freqs]()),
|
| + conj_sum_(new float[freqs]()),
|
| + freqs_(freqs),
|
| + window_size_(window_size),
|
| + decay_(decay),
|
| + history_cursor_(0),
|
| + count_(0),
|
| + array_mean_(0.0f) {
|
| + history_.reset(new scoped_ptr<complex<float>[]>[freqs_]());
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + history_[i].reset(new complex<float>[window_size_]());
|
| + }
|
| + subhistory_.reset(new scoped_ptr<complex<float>[]>[freqs_]());
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + subhistory_[i].reset(new complex<float>[window_size_]());
|
| + }
|
| + subhistory_sq_.reset(new scoped_ptr<complex<float>[]>[freqs_]());
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + subhistory_sq_[i].reset(new complex<float>[window_size_]());
|
| + }
|
| + switch (type) {
|
| + case kStepInfinite:
|
| + step_func_ = &VarianceArray::InfiniteStep;
|
| + break;
|
| + case kStepDecaying:
|
| + step_func_ = &VarianceArray::DecayStep;
|
| + break;
|
| + case kStepWindowed:
|
| + step_func_ = &VarianceArray::WindowedStep;
|
| + break;
|
| + case kStepBlocked:
|
| + step_func_ = &VarianceArray::BlockedStep;
|
| + break;
|
| + }
|
| +}
|
| +
|
| +// Compute the variance with Welford's algorithm, adding some fudge to
|
| +// the input in case of all-zeroes.
|
| +void VarianceArray::InfiniteStep(const complex<float>* data, bool skip_fudge) {
|
| + array_mean_ = 0.0f;
|
| + ++count_;
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + complex<float> sample = data[i];
|
| + if (!skip_fudge) {
|
| + sample = zerofudge(sample);
|
| + }
|
| + if (count_ == 1) {
|
| + running_mean_[i] = sample;
|
| + variance_[i] = 0.0f;
|
| + } else {
|
| + float old_sum = conj_sum_[i];
|
| + complex<float> old_mean = running_mean_[i];
|
| + running_mean_[i] = old_mean + (sample - old_mean) /
|
| + static_cast<float>(count_);
|
| + conj_sum_[i] = (old_sum + std::conj(sample - old_mean) *
|
| + (sample - running_mean_[i])).real();
|
| + variance_[i] = conj_sum_[i] / (count_ - 1); // + fudge[fudge_index].real();
|
| + if (skip_fudge && false) {
|
| + //variance_[i] -= fudge[fudge_index].real();
|
| + }
|
| + }
|
| + array_mean_ += (variance_[i] - array_mean_) / (i + 1);
|
| + }
|
| +}
|
| +
|
| +// Compute the variance from the beginning, with exponential decaying of the
|
| +// series data.
|
| +void VarianceArray::DecayStep(const complex<float>* data, bool /*dummy*/) {
|
| + array_mean_ = 0.0f;
|
| + ++count_;
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + complex<float> sample = data[i];
|
| + sample = zerofudge(sample);
|
| +
|
| + if (count_ == 1) {
|
| + running_mean_[i] = sample;
|
| + running_mean_sq_[i] = sample * std::conj(sample);
|
| + variance_[i] = 0.0f;
|
| + } else {
|
| + complex<float> prev = running_mean_[i];
|
| + complex<float> prev2 = running_mean_sq_[i];
|
| + running_mean_[i] = decay_ * prev + (1.0f - decay_) * sample;
|
| + running_mean_sq_[i] = decay_ * prev2 +
|
| + (1.0f - decay_) * sample * std::conj(sample);
|
| + //variance_[i] = decay_ * variance_[i] + (1.0f - decay_) * (
|
| + // (sample - running_mean_[i]) * std::conj(sample - running_mean_[i])).real();
|
| + variance_[i] = (running_mean_sq_[i] - running_mean_[i] * std::conj(running_mean_[i])).real();
|
| + }
|
| +
|
| + array_mean_ += (variance_[i] - array_mean_) / (i + 1);
|
| + }
|
| +}
|
| +
|
| +// Windowed variance computation. On each step, the variances for the
|
| +// window are recomputed from scratch, using Welford's algorithm.
|
| +void VarianceArray::WindowedStep(const complex<float>* data, bool /*dummy*/) {
|
| + int num = min(count_ + 1, window_size_);
|
| + array_mean_ = 0.0f;
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + complex<float> mean;
|
| + float conj_sum = 0.0f;
|
| +
|
| + history_[i][history_cursor_] = data[i];
|
| +
|
| + mean = history_[i][history_cursor_];
|
| + variance_[i] = 0.0f;
|
| + for (int j = 1; j < num; ++j) {
|
| + complex<float> sample = zerofudge(
|
| + history_[i][(history_cursor_ + j) % window_size_]);
|
| + sample = history_[i][(history_cursor_ + j) % window_size_];
|
| + float old_sum = conj_sum;
|
| + complex<float> old_mean = mean;
|
| +
|
| + mean = old_mean + (sample - old_mean) / static_cast<float>(j + 1);
|
| + conj_sum = (old_sum + std::conj(sample - old_mean) *
|
| + (sample - mean)).real();
|
| + variance_[i] = conj_sum / (j);
|
| + }
|
| + array_mean_ += (variance_[i] - array_mean_) / (i + 1);
|
| + }
|
| + history_cursor_ = (history_cursor_ + 1) % window_size_;
|
| + ++count_;
|
| +}
|
| +
|
| +// Variance with a window of blocks. Within each block, the variances are
|
| +// recomputed from scratch at every stp, using |Var(X) = E(X^2) - E^2(X)|.
|
| +// Once a block is filled with kWindowBlockSize samples, it is added to the
|
| +// history window and a new block is started. The variances for the window
|
| +// are recomputed from scratch at each of these transitions.
|
| +void VarianceArray::BlockedStep(const complex<float>* data, bool /*dummy*/) {
|
| + int blocks = min(window_size_, history_cursor_);
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + AddToMean(data[i], count_ + 1, &sub_running_mean_[i]);
|
| + AddToMean(data[i] * std::conj(data[i]), count_ + 1,
|
| + &sub_running_mean_sq_[i]);
|
| + subhistory_[i][history_cursor_ % window_size_] = sub_running_mean_[i];
|
| + subhistory_sq_[i][history_cursor_ % window_size_] = sub_running_mean_sq_[i];
|
| +
|
| + variance_[i] = (NewMean(running_mean_sq_[i], sub_running_mean_sq_[i],
|
| + blocks) -
|
| + NewMean(running_mean_[i], sub_running_mean_[i], blocks) *
|
| + std::conj(NewMean(running_mean_[i], sub_running_mean_[i],
|
| + blocks))).real();
|
| + if (count_ == kWindowBlockSize - 1) {
|
| + sub_running_mean_[i] = complex<float>(0.0f, 0.0f);
|
| + sub_running_mean_sq_[i] = complex<float>(0.0f, 0.0f);
|
| + running_mean_[i] = complex<float>(0.0f, 0.0f);
|
| + running_mean_sq_[i] = complex<float>(0.0f, 0.0f);
|
| + for (int j = 0; j < min(window_size_, history_cursor_); ++j) {
|
| + AddToMean(subhistory_[i][j], j, &running_mean_[i]);
|
| + AddToMean(subhistory_sq_[i][j], j, &running_mean_sq_[i]);
|
| + }
|
| + ++history_cursor_;
|
| + }
|
| + }
|
| + ++count_;
|
| + if (count_ == kWindowBlockSize) {
|
| + count_ = 0;
|
| + }
|
| +}
|
| +
|
| +void VarianceArray::Clear() {
|
| + memset(running_mean_.get(), 0, sizeof(*running_mean_.get()) * freqs_);
|
| + memset(running_mean_sq_.get(), 0, sizeof(*running_mean_sq_.get()) * freqs_);
|
| + memset(variance_.get(), 0, sizeof(*variance_.get()) * freqs_);
|
| + memset(conj_sum_.get(), 0, sizeof(*conj_sum_.get()) * freqs_);
|
| + history_cursor_ = 0;
|
| + count_ = 0;
|
| + array_mean_ = 0.0f;
|
| +}
|
| +
|
| +void VarianceArray::ApplyScale(float scale) {
|
| + array_mean_ = 0.0f;
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + variance_[i] *= scale * scale;
|
| + array_mean_ += (variance_[i] - array_mean_) / (i + 1);
|
| + }
|
| +}
|
| +
|
| +GainApplier::GainApplier(int freqs, float change_limit)
|
| + : freqs_(freqs),
|
| + change_limit_(change_limit),
|
| + target_(new float[freqs]()),
|
| + current_(new float[freqs]()) {
|
| + for (int i = 0; i < freqs; ++i) {
|
| + target_[i] = 1.0f;
|
| + current_[i] = 1.0f;
|
| + }
|
| +}
|
| +
|
| +void GainApplier::Apply(const complex<float>* in_block,
|
| + complex<float>* out_block) {
|
| + for (int i = 0; i < freqs_; ++i) {
|
| + float factor = sqrtf(fabsf(current_[i]));
|
| + if (!std::isnormal(factor)) {
|
| + factor = 1.0f;
|
| + }
|
| + out_block[i] = factor * in_block[i];
|
| + current_[i] = UpdateFactor(target_[i], current_[i], change_limit_);
|
| + }
|
| +}
|
| +
|
| +} // namespace intelligibility
|
| +
|
| +} // namespace webrtc
|
| +
|
|
|