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 |
index 7da9b957a422488957c00efd2f67b007dce617c0..6c44415130027aa736e2384cfd9557a10bc74347 100644 |
--- a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc |
+++ b/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc |
@@ -8,10 +8,6 @@ |
* be found in the AUTHORS file in the root of the source tree. |
*/ |
-// |
-// Implements helper functions and classes for intelligibility enhancement. |
-// |
- |
#include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h" |
#include <math.h> |
@@ -19,271 +15,46 @@ |
#include <string.h> |
#include <algorithm> |
-using std::complex; |
-using std::min; |
- |
namespace webrtc { |
namespace intelligibility { |
+namespace { |
+ |
+// Return |current| changed towards |target|, with the change being at most |
+// |limit|. |
float UpdateFactor(float target, float current, float limit) { |
float delta = fabsf(target - current); |
- float sign = copysign(1.0f, target - current); |
+ float sign = copysign(1.f, target - current); |
return current + sign * fminf(delta, limit); |
} |
-float AddDitherIfZero(float value) { |
- return value == 0.f ? std::rand() * 0.01f / RAND_MAX : value; |
-} |
- |
-complex<float> zerofudge(complex<float> c) { |
- return complex<float>(AddDitherIfZero(c.real()), AddDitherIfZero(c.imag())); |
-} |
- |
-complex<float> NewMean(complex<float> mean, complex<float> data, size_t count) { |
- return mean + (data - mean) / static_cast<float>(count); |
-} |
+} // namespace |
-void AddToMean(complex<float> data, size_t count, complex<float>* mean) { |
- (*mean) = NewMean(*mean, data, count); |
-} |
- |
- |
-static const size_t kWindowBlockSize = 10; |
- |
-VarianceArray::VarianceArray(size_t num_freqs, |
- StepType type, |
- size_t window_size, |
- float decay) |
- : running_mean_(new complex<float>[num_freqs]()), |
- running_mean_sq_(new complex<float>[num_freqs]()), |
- sub_running_mean_(new complex<float>[num_freqs]()), |
- sub_running_mean_sq_(new complex<float>[num_freqs]()), |
- variance_(new float[num_freqs]()), |
- conj_sum_(new float[num_freqs]()), |
+PowerEstimator::PowerEstimator(size_t num_freqs, |
+ float decay) |
+ : magnitude_(new float[num_freqs]()), |
+ power_(new float[num_freqs]()), |
num_freqs_(num_freqs), |
- window_size_(window_size), |
- decay_(decay), |
- history_cursor_(0), |
- count_(0), |
- array_mean_(0.0f), |
- buffer_full_(false) { |
- history_.reset(new rtc::scoped_ptr<complex<float>[]>[num_freqs_]()); |
- for (size_t i = 0; i < num_freqs_; ++i) { |
- history_[i].reset(new complex<float>[window_size_]()); |
- } |
- subhistory_.reset(new rtc::scoped_ptr<complex<float>[]>[num_freqs_]()); |
- for (size_t i = 0; i < num_freqs_; ++i) { |
- subhistory_[i].reset(new complex<float>[window_size_]()); |
- } |
- subhistory_sq_.reset(new rtc::scoped_ptr<complex<float>[]>[num_freqs_]()); |
- for (size_t i = 0; i < num_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; |
- case kStepBlockBasedMovingAverage: |
- step_func_ = &VarianceArray::BlockBasedMovingAverage; |
- break; |
- } |
+ decay_(decay) { |
+ memset(magnitude_.get(), 0, sizeof(*magnitude_.get()) * num_freqs_); |
+ memset(power_.get(), 0, sizeof(*power_.get()) * num_freqs_); |
} |
-// 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 (size_t i = 0; i < num_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); |
- } |
- array_mean_ += (variance_[i] - array_mean_) / (i + 1); |
- } |
-} |
- |
-// Compute the variance from the beginning, with exponential decaying of the |
+// Compute the magnitude from the beginning, with exponential decaying of the |
// series data. |
-void VarianceArray::DecayStep(const complex<float>* data, bool /*dummy*/) { |
- array_mean_ = 0.0f; |
- ++count_; |
+void PowerEstimator::Step(const std::complex<float>* data) { |
for (size_t i = 0; i < num_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] = (running_mean_sq_[i] - |
- running_mean_[i] * std::conj(running_mean_[i])).real(); |
- } |
- |
- array_mean_ += (variance_[i] - array_mean_) / (i + 1); |
+ magnitude_[i] = decay_ * magnitude_[i] + |
+ (1.f - decay_) * std::abs(data[i]); |
} |
} |
-// 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*/) { |
- size_t num = min(count_ + 1, window_size_); |
- array_mean_ = 0.0f; |
- for (size_t i = 0; i < num_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 (size_t 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*/) { |
- size_t blocks = min(window_size_, history_cursor_ + 1); |
- for (size_t i = 0; i < num_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 (size_t j = 0; j < min(window_size_, history_cursor_); ++j) { |
- AddToMean(subhistory_[i][j], j + 1, &running_mean_[i]); |
- AddToMean(subhistory_sq_[i][j], j + 1, &running_mean_sq_[i]); |
- } |
- ++history_cursor_; |
- } |
- } |
- ++count_; |
- if (count_ == kWindowBlockSize) { |
- count_ = 0; |
- } |
-} |
- |
-// Recomputes variances for each window from scratch based on previous window. |
-void VarianceArray::BlockBasedMovingAverage(const std::complex<float>* data, |
- bool /*dummy*/) { |
- // TODO(ekmeyerson) To mitigate potential divergence, add counter so that |
- // after every so often sums are computed scratch by summing over all |
- // elements instead of subtracting oldest and adding newest. |
- for (size_t i = 0; i < num_freqs_; ++i) { |
- sub_running_mean_[i] += data[i]; |
- sub_running_mean_sq_[i] += data[i] * std::conj(data[i]); |
- } |
- ++count_; |
- |
- // TODO(ekmeyerson) Make kWindowBlockSize nonconstant to allow |
- // experimentation with different block size,window size pairs. |
- if (count_ >= kWindowBlockSize) { |
- count_ = 0; |
- |
- for (size_t i = 0; i < num_freqs_; ++i) { |
- running_mean_[i] -= subhistory_[i][history_cursor_]; |
- running_mean_sq_[i] -= subhistory_sq_[i][history_cursor_]; |
- |
- float scale = 1.f / kWindowBlockSize; |
- subhistory_[i][history_cursor_] = sub_running_mean_[i] * scale; |
- subhistory_sq_[i][history_cursor_] = sub_running_mean_sq_[i] * scale; |
- |
- sub_running_mean_[i] = std::complex<float>(0.0f, 0.0f); |
- sub_running_mean_sq_[i] = std::complex<float>(0.0f, 0.0f); |
- |
- running_mean_[i] += subhistory_[i][history_cursor_]; |
- running_mean_sq_[i] += subhistory_sq_[i][history_cursor_]; |
- |
- scale = 1.f / (buffer_full_ ? window_size_ : history_cursor_ + 1); |
- variance_[i] = std::real(running_mean_sq_[i] * scale - |
- running_mean_[i] * scale * |
- std::conj(running_mean_[i]) * scale); |
- } |
- |
- ++history_cursor_; |
- if (history_cursor_ >= window_size_) { |
- buffer_full_ = true; |
- history_cursor_ = 0; |
- } |
- } |
-} |
- |
-void VarianceArray::Clear() { |
- memset(running_mean_.get(), 0, sizeof(*running_mean_.get()) * num_freqs_); |
- memset(running_mean_sq_.get(), 0, |
- sizeof(*running_mean_sq_.get()) * num_freqs_); |
- memset(variance_.get(), 0, sizeof(*variance_.get()) * num_freqs_); |
- memset(conj_sum_.get(), 0, sizeof(*conj_sum_.get()) * num_freqs_); |
- history_cursor_ = 0; |
- count_ = 0; |
- array_mean_ = 0.0f; |
-} |
- |
-void VarianceArray::ApplyScale(float scale) { |
- array_mean_ = 0.0f; |
+const float* PowerEstimator::Power() { |
for (size_t i = 0; i < num_freqs_; ++i) { |
- variance_[i] *= scale * scale; |
- array_mean_ += (variance_[i] - array_mean_) / (i + 1); |
+ power_[i] = magnitude_[i] * magnitude_[i]; |
} |
+ return &power_[0]; |
} |
GainApplier::GainApplier(size_t freqs, float change_limit) |
@@ -292,17 +63,17 @@ GainApplier::GainApplier(size_t freqs, float change_limit) |
target_(new float[freqs]()), |
current_(new float[freqs]()) { |
for (size_t i = 0; i < freqs; ++i) { |
- target_[i] = 1.0f; |
- current_[i] = 1.0f; |
+ target_[i] = 1.f; |
+ current_[i] = 1.f; |
} |
} |
-void GainApplier::Apply(const complex<float>* in_block, |
- complex<float>* out_block) { |
+void GainApplier::Apply(const std::complex<float>* in_block, |
+ std::complex<float>* out_block) { |
for (size_t i = 0; i < num_freqs_; ++i) { |
float factor = sqrtf(fabsf(current_[i])); |
if (!std::isnormal(factor)) { |
- factor = 1.0f; |
+ factor = 1.f; |
} |
out_block[i] = factor * in_block[i]; |
current_[i] = UpdateFactor(target_[i], current_[i], change_limit_); |