| Index: webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc
|
| diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc b/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc
|
| index 877e437fe7a298cb1cb0242af239b1b12691df17..f0050a2ae12f155d41642116d2b6e0e64e18c058 100644
|
| --- a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc
|
| +++ b/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc
|
| @@ -8,13 +8,6 @@
|
| * be found in the AUTHORS file in the root of the source tree.
|
| */
|
|
|
| -//
|
| -// Implements core class for intelligibility enhancer.
|
| -//
|
| -// Details of the model and algorithm can be found in the original paper:
|
| -// http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6882788
|
| -//
|
| -
|
| #include "webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h"
|
|
|
| #include <math.h>
|
| @@ -32,7 +25,7 @@ namespace webrtc {
|
| namespace {
|
|
|
| const size_t kErbResolution = 2;
|
| -const int kWindowSizeMs = 2;
|
| +const int kWindowSizeMs = 16;
|
| const int kChunkSizeMs = 10; // Size provided by APM.
|
| const float kClipFreq = 200.0f;
|
| const float kConfigRho = 0.02f; // Default production and interpretation SNR.
|
| @@ -49,35 +42,30 @@ float DotProduct(const float* a, const float* b, size_t length) {
|
| return ret;
|
| }
|
|
|
| -// Computes the power across ERB filters from the power spectral density |var|.
|
| +// Computes the power across ERB bands from the power spectral density |pow|.
|
| // Stores it in |result|.
|
| -void FilterVariance(const float* var,
|
| - const std::vector<std::vector<float>>& filter_bank,
|
| - float* result) {
|
| +void MapToErbBands(const float* pow,
|
| + const std::vector<std::vector<float>>& filter_bank,
|
| + float* result) {
|
| for (size_t i = 0; i < filter_bank.size(); ++i) {
|
| RTC_DCHECK_GT(filter_bank[i].size(), 0u);
|
| - result[i] = DotProduct(&filter_bank[i][0], var, filter_bank[i].size());
|
| + result[i] = DotProduct(&filter_bank[i][0], pow, filter_bank[i].size());
|
| }
|
| }
|
|
|
| } // namespace
|
|
|
| -using std::complex;
|
| -using std::max;
|
| -using std::min;
|
| -using VarianceType = intelligibility::VarianceArray::StepType;
|
| -
|
| IntelligibilityEnhancer::TransformCallback::TransformCallback(
|
| IntelligibilityEnhancer* parent)
|
| : parent_(parent) {
|
| }
|
|
|
| void IntelligibilityEnhancer::TransformCallback::ProcessAudioBlock(
|
| - const complex<float>* const* in_block,
|
| + const std::complex<float>* const* in_block,
|
| size_t in_channels,
|
| size_t frames,
|
| size_t /* out_channels */,
|
| - complex<float>* const* out_block) {
|
| + std::complex<float>* const* out_block) {
|
| RTC_DCHECK_EQ(parent_->freqs_, frames);
|
| for (size_t i = 0; i < in_channels; ++i) {
|
| parent_->ProcessClearBlock(in_block[i], out_block[i]);
|
| @@ -101,13 +89,10 @@ IntelligibilityEnhancer::IntelligibilityEnhancer(const Config& config)
|
| num_render_channels_(config.num_render_channels),
|
| analysis_rate_(config.analysis_rate),
|
| active_(true),
|
| - clear_variance_(freqs_,
|
| - config.var_type,
|
| - config.var_window_size,
|
| - config.var_decay_rate),
|
| + clear_power_(freqs_, config.decay_rate),
|
| noise_power_(freqs_, 0.f),
|
| - filtered_clear_var_(new float[bank_size_]),
|
| - filtered_noise_var_(new float[bank_size_]),
|
| + filtered_clear_pow_(new float[bank_size_]),
|
| + filtered_noise_pow_(new float[bank_size_]),
|
| center_freqs_(new float[bank_size_]),
|
| render_filter_bank_(CreateErbBank(freqs_)),
|
| rho_(new float[bank_size_]),
|
| @@ -120,12 +105,12 @@ IntelligibilityEnhancer::IntelligibilityEnhancer(const Config& config)
|
| analysis_step_(0) {
|
| RTC_DCHECK_LE(config.rho, 1.0f);
|
|
|
| - memset(filtered_clear_var_.get(),
|
| + memset(filtered_clear_pow_.get(),
|
| 0,
|
| - bank_size_ * sizeof(filtered_clear_var_[0]));
|
| - memset(filtered_noise_var_.get(),
|
| + bank_size_ * sizeof(filtered_clear_pow_[0]));
|
| + memset(filtered_noise_pow_.get(),
|
| 0,
|
| - bank_size_ * sizeof(filtered_noise_var_[0]));
|
| + bank_size_ * sizeof(filtered_noise_pow_[0]));
|
|
|
| // Assumes all rho equal.
|
| for (size_t i = 0; i < bank_size_; ++i) {
|
| @@ -176,8 +161,9 @@ void IntelligibilityEnhancer::ProcessRenderAudio(float* const* audio,
|
| }
|
| }
|
|
|
| -void IntelligibilityEnhancer::ProcessClearBlock(const complex<float>* in_block,
|
| - complex<float>* out_block) {
|
| +void IntelligibilityEnhancer::ProcessClearBlock(
|
| + const std::complex<float>* in_block,
|
| + std::complex<float>* out_block) {
|
| if (block_count_ < 2) {
|
| memset(out_block, 0, freqs_ * sizeof(*out_block));
|
| ++block_count_;
|
| @@ -186,11 +172,9 @@ void IntelligibilityEnhancer::ProcessClearBlock(const complex<float>* in_block,
|
|
|
| // TODO(ekm): Use VAD to |Step| and |AnalyzeClearBlock| only if necessary.
|
| if (true) {
|
| - clear_variance_.Step(in_block, false);
|
| + clear_power_.Step(in_block);
|
| if (block_count_ % analysis_rate_ == analysis_rate_ - 1) {
|
| - const float power_target = std::accumulate(
|
| - clear_variance_.variance(), clear_variance_.variance() + freqs_, 0.f);
|
| - AnalyzeClearBlock(power_target);
|
| + AnalyzeClearBlock();
|
| ++analysis_step_;
|
| }
|
| ++block_count_;
|
| @@ -201,23 +185,26 @@ void IntelligibilityEnhancer::ProcessClearBlock(const complex<float>* in_block,
|
| }
|
| }
|
|
|
| -void IntelligibilityEnhancer::AnalyzeClearBlock(float power_target) {
|
| - FilterVariance(clear_variance_.variance(),
|
| - render_filter_bank_,
|
| - filtered_clear_var_.get());
|
| - FilterVariance(&noise_power_[0],
|
| - capture_filter_bank_,
|
| - filtered_noise_var_.get());
|
| +void IntelligibilityEnhancer::AnalyzeClearBlock() {
|
| + const float* clear_power = clear_power_.Power();
|
| + MapToErbBands(clear_power,
|
| + render_filter_bank_,
|
| + filtered_clear_pow_.get());
|
| + MapToErbBands(&noise_power_[0],
|
| + capture_filter_bank_,
|
| + filtered_noise_pow_.get());
|
| SolveForGainsGivenLambda(kLambdaTop, start_freq_, gains_eq_.get());
|
| + const float power_target = std::accumulate(
|
| + clear_power, clear_power + freqs_, 0.f);
|
| const float power_top =
|
| - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_);
|
| + DotProduct(gains_eq_.get(), filtered_clear_pow_.get(), bank_size_);
|
| SolveForGainsGivenLambda(kLambdaBot, start_freq_, gains_eq_.get());
|
| const float power_bot =
|
| - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_);
|
| + DotProduct(gains_eq_.get(), filtered_clear_pow_.get(), bank_size_);
|
| if (power_target >= power_bot && power_target <= power_top) {
|
| SolveForLambda(power_target, power_bot, power_top);
|
| UpdateErbGains();
|
| - } // Else experiencing variance underflow, so do nothing.
|
| + } // Else experiencing power underflow, so do nothing.
|
| }
|
|
|
| void IntelligibilityEnhancer::SolveForLambda(float power_target,
|
| @@ -237,7 +224,7 @@ void IntelligibilityEnhancer::SolveForLambda(float power_target,
|
| const float lambda = lambda_bot + (lambda_top - lambda_bot) / 2.0f;
|
| SolveForGainsGivenLambda(lambda, start_freq_, gains_eq_.get());
|
| const float power =
|
| - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_);
|
| + DotProduct(gains_eq_.get(), filtered_clear_pow_.get(), bank_size_);
|
| if (power < power_target) {
|
| lambda_bot = lambda;
|
| } else {
|
| @@ -290,22 +277,22 @@ std::vector<std::vector<float>> IntelligibilityEnhancer::CreateErbBank(
|
| size_t lll, ll, rr, rrr;
|
| static const size_t kOne = 1; // Avoids repeated static_cast<>s below.
|
| lll = static_cast<size_t>(round(
|
| - center_freqs_[max(kOne, i - lf) - 1] * num_freqs /
|
| + center_freqs_[std::max(kOne, i - lf) - 1] * num_freqs /
|
| (0.5f * sample_rate_hz_)));
|
| ll = static_cast<size_t>(round(
|
| - center_freqs_[max(kOne, i) - 1] * num_freqs /
|
| + center_freqs_[std::max(kOne, i) - 1] * num_freqs /
|
| (0.5f * sample_rate_hz_)));
|
| - lll = min(num_freqs, max(lll, kOne)) - 1;
|
| - ll = min(num_freqs, max(ll, kOne)) - 1;
|
| + lll = std::min(num_freqs, std::max(lll, kOne)) - 1;
|
| + ll = std::min(num_freqs, std::max(ll, kOne)) - 1;
|
|
|
| rrr = static_cast<size_t>(round(
|
| - center_freqs_[min(bank_size_, i + rf) - 1] * num_freqs /
|
| + center_freqs_[std::min(bank_size_, i + rf) - 1] * num_freqs /
|
| (0.5f * sample_rate_hz_)));
|
| rr = static_cast<size_t>(round(
|
| - center_freqs_[min(bank_size_, i + 1) - 1] * num_freqs /
|
| + center_freqs_[std::min(bank_size_, i + 1) - 1] * num_freqs /
|
| (0.5f * sample_rate_hz_)));
|
| - rrr = min(num_freqs, max(rrr, kOne)) - 1;
|
| - rr = min(num_freqs, max(rr, kOne)) - 1;
|
| + rrr = std::min(num_freqs, std::max(rrr, kOne)) - 1;
|
| + rr = std::min(num_freqs, std::max(rr, kOne)) - 1;
|
|
|
| float step, element;
|
|
|
| @@ -343,8 +330,8 @@ void IntelligibilityEnhancer::SolveForGainsGivenLambda(float lambda,
|
| size_t start_freq,
|
| float* sols) {
|
| bool quadratic = (kConfigRho < 1.0f);
|
| - const float* var_x0 = filtered_clear_var_.get();
|
| - const float* var_n0 = filtered_noise_var_.get();
|
| + const float* pow_x0 = filtered_clear_pow_.get();
|
| + const float* pow_n0 = filtered_noise_pow_.get();
|
|
|
| for (size_t n = 0; n < start_freq; ++n) {
|
| sols[n] = 1.0f;
|
| @@ -353,11 +340,11 @@ void IntelligibilityEnhancer::SolveForGainsGivenLambda(float lambda,
|
| // Analytic solution for optimal gains. See paper for derivation.
|
| for (size_t n = start_freq - 1; n < bank_size_; ++n) {
|
| float alpha0, beta0, gamma0;
|
| - gamma0 = 0.5f * rho_[n] * var_x0[n] * var_n0[n] +
|
| - lambda * var_x0[n] * var_n0[n] * var_n0[n];
|
| - beta0 = lambda * var_x0[n] * (2 - rho_[n]) * var_x0[n] * var_n0[n];
|
| + gamma0 = 0.5f * rho_[n] * pow_x0[n] * pow_n0[n] +
|
| + lambda * pow_x0[n] * pow_n0[n] * pow_n0[n];
|
| + beta0 = lambda * pow_x0[n] * (2 - rho_[n]) * pow_x0[n] * pow_n0[n];
|
| if (quadratic) {
|
| - alpha0 = lambda * var_x0[n] * (1 - rho_[n]) * var_x0[n] * var_x0[n];
|
| + alpha0 = lambda * pow_x0[n] * (1 - rho_[n]) * pow_x0[n] * pow_x0[n];
|
| sols[n] =
|
| (-beta0 - sqrtf(beta0 * beta0 - 4 * alpha0 * gamma0)) /
|
| (2 * alpha0 + std::numeric_limits<float>::epsilon());
|
|
|