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 9ed2d07584dec92c49ab31a0a4854f3d56dd8960..b4cf294a9e0170ca769852518795807cb9550782 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> |
@@ -31,7 +24,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. |
@@ -48,35 +41,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]); |
@@ -100,13 +88,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_]), |
@@ -119,12 +104,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) { |
@@ -175,8 +160,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_; |
@@ -185,11 +171,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_; |
@@ -200,23 +184,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, |
@@ -235,7 +222,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 { |
@@ -288,22 +275,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; |
@@ -341,8 +328,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; |
@@ -351,11 +338,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); |
} else { |