1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/audio_processing/agc/loudness_histogram.h" 12 13 #include <string.h> 14 15 #include <cmath> 16 17 #include "rtc_base/checks.h" 18 19 namespace webrtc { 20 21 static const double kHistBinCenters[] = { 22 7.59621091765857e-02, 9.02036021061016e-02, 1.07115112009343e-01, 23 1.27197217770508e-01, 1.51044347572047e-01, 1.79362373905283e-01, 24 2.12989507320644e-01, 2.52921107370304e-01, 3.00339145144454e-01, 25 3.56647189489147e-01, 4.23511952494003e-01, 5.02912623991786e-01, 26 5.97199455365749e-01, 7.09163326739184e-01, 8.42118356728544e-01, 27 1.00000000000000e+00, 1.18748153630660e+00, 1.41011239906908e+00, 28 1.67448243801153e+00, 1.98841697800836e+00, 2.36120844786349e+00, 29 2.80389143520905e+00, 3.32956930911896e+00, 3.95380207843188e+00, 30 4.69506696634852e+00, 5.57530533426190e+00, 6.62057214370769e+00, 31 7.86180718043869e+00, 9.33575086877358e+00, 1.10860317842269e+01, 32 1.31644580546776e+01, 1.56325508754123e+01, 1.85633655299256e+01, 33 2.20436538184971e+01, 2.61764319021997e+01, 3.10840295702492e+01, 34 3.69117111886792e+01, 4.38319755100383e+01, 5.20496616180135e+01, 35 6.18080121423973e+01, 7.33958732149108e+01, 8.71562442838066e+01, 36 1.03496430860848e+02, 1.22900100720889e+02, 1.45941600416277e+02, 37 1.73302955873365e+02, 2.05794060286978e+02, 2.44376646872353e+02, 38 2.90192756065437e+02, 3.44598539797631e+02, 4.09204403447902e+02, 39 4.85922673669740e+02, 5.77024203055553e+02, 6.85205587130498e+02, 40 8.13668983291589e+02, 9.66216894324125e+02, 1.14736472207740e+03, 41 1.36247442287647e+03, 1.61791322085579e+03, 1.92124207711260e+03, 42 2.28143949334655e+03, 2.70916727454970e+03, 3.21708611729384e+03, 43 3.82023036499473e+03, 4.53645302286906e+03, 5.38695420497926e+03, 44 6.39690865534207e+03, 7.59621091765857e+03, 9.02036021061016e+03, 45 1.07115112009343e+04, 1.27197217770508e+04, 1.51044347572047e+04, 46 1.79362373905283e+04, 2.12989507320644e+04, 2.52921107370304e+04, 47 3.00339145144454e+04, 3.56647189489147e+04}; 48 49 static const double kProbQDomain = 1024.0; 50 // Loudness of -15 dB (smallest expected loudness) in log domain, 51 // loudness_db = 13.5 * log10(rms); 52 static const double kLogDomainMinBinCenter = -2.57752062648587; 53 // Loudness step of 1 dB in log domain 54 static const double kLogDomainStepSizeInverse = 5.81954605750359; 55 56 static const int kTransientWidthThreshold = 7; 57 static const double kLowProbabilityThreshold = 0.2; 58 59 static const int kLowProbThresholdQ10 = 60 static_cast<int>(kLowProbabilityThreshold * kProbQDomain); 61 62 LoudnessHistogram::LoudnessHistogram() 63 : num_updates_(0), 64 audio_content_q10_(0), 65 bin_count_q10_(), 66 activity_probability_(), 67 hist_bin_index_(), 68 buffer_index_(0), 69 buffer_is_full_(false), 70 len_circular_buffer_(0), 71 len_high_activity_(0) { 72 static_assert( 73 kHistSize == sizeof(kHistBinCenters) / sizeof(kHistBinCenters[0]), 74 "histogram bin centers incorrect size"); 75 } 76 77 LoudnessHistogram::LoudnessHistogram(int window_size) 78 : num_updates_(0), 79 audio_content_q10_(0), 80 bin_count_q10_(), 81 activity_probability_(new int[window_size]), 82 hist_bin_index_(new int[window_size]), 83 buffer_index_(0), 84 buffer_is_full_(false), 85 len_circular_buffer_(window_size), 86 len_high_activity_(0) {} 87 88 LoudnessHistogram::~LoudnessHistogram() {} 89 90 void LoudnessHistogram::Update(double rms, double activity_probaility) { 91 // If circular histogram is activated then remove the oldest entry. 92 if (len_circular_buffer_ > 0) 93 RemoveOldestEntryAndUpdate(); 94 95 // Find the corresponding bin. 96 int hist_index = GetBinIndex(rms); 97 // To Q10 domain. 98 int prob_q10 = 99 static_cast<int16_t>(floor(activity_probaility * kProbQDomain)); 100 InsertNewestEntryAndUpdate(prob_q10, hist_index); 101 } 102 103 // Doing nothing if buffer is not full, yet. 104 void LoudnessHistogram::RemoveOldestEntryAndUpdate() { 105 RTC_DCHECK_GT(len_circular_buffer_, 0); 106 // Do nothing if circular buffer is not full. 107 if (!buffer_is_full_) 108 return; 109 110 int oldest_prob = activity_probability_[buffer_index_]; 111 int oldest_hist_index = hist_bin_index_[buffer_index_]; 112 UpdateHist(-oldest_prob, oldest_hist_index); 113 } 114 115 void LoudnessHistogram::RemoveTransient() { 116 // Don't expect to be here if high-activity region is longer than 117 // |kTransientWidthThreshold| or there has not been any transient. 118 RTC_DCHECK_LE(len_high_activity_, kTransientWidthThreshold); 119 int index = 120 (buffer_index_ > 0) ? (buffer_index_ - 1) : len_circular_buffer_ - 1; 121 while (len_high_activity_ > 0) { 122 UpdateHist(-activity_probability_[index], hist_bin_index_[index]); 123 activity_probability_[index] = 0; 124 index = (index > 0) ? (index - 1) : (len_circular_buffer_ - 1); 125 len_high_activity_--; 126 } 127 } 128 129 void LoudnessHistogram::InsertNewestEntryAndUpdate(int activity_prob_q10, 130 int hist_index) { 131 // Update the circular buffer if it is enabled. 132 if (len_circular_buffer_ > 0) { 133 // Removing transient. 134 if (activity_prob_q10 <= kLowProbThresholdQ10) { 135 // Lower than threshold probability, set it to zero. 136 activity_prob_q10 = 0; 137 // Check if this has been a transient. 138 if (len_high_activity_ <= kTransientWidthThreshold) 139 RemoveTransient(); // Remove this transient. 140 len_high_activity_ = 0; 141 } else if (len_high_activity_ <= kTransientWidthThreshold) { 142 len_high_activity_++; 143 } 144 // Updating the circular buffer. 145 activity_probability_[buffer_index_] = activity_prob_q10; 146 hist_bin_index_[buffer_index_] = hist_index; 147 // Increment the buffer index and check for wrap-around. 148 buffer_index_++; 149 if (buffer_index_ >= len_circular_buffer_) { 150 buffer_index_ = 0; 151 buffer_is_full_ = true; 152 } 153 } 154 155 num_updates_++; 156 if (num_updates_ < 0) 157 num_updates_--; 158 159 UpdateHist(activity_prob_q10, hist_index); 160 } 161 162 void LoudnessHistogram::UpdateHist(int activity_prob_q10, int hist_index) { 163 bin_count_q10_[hist_index] += activity_prob_q10; 164 audio_content_q10_ += activity_prob_q10; 165 } 166 167 double LoudnessHistogram::AudioContent() const { 168 return audio_content_q10_ / kProbQDomain; 169 } 170 171 LoudnessHistogram* LoudnessHistogram::Create() { 172 return new LoudnessHistogram; 173 } 174 175 LoudnessHistogram* LoudnessHistogram::Create(int window_size) { 176 if (window_size < 0) 177 return NULL; 178 return new LoudnessHistogram(window_size); 179 } 180 181 void LoudnessHistogram::Reset() { 182 // Reset the histogram, audio-content and number of updates. 183 memset(bin_count_q10_, 0, sizeof(bin_count_q10_)); 184 audio_content_q10_ = 0; 185 num_updates_ = 0; 186 // Empty the circular buffer. 187 buffer_index_ = 0; 188 buffer_is_full_ = false; 189 len_high_activity_ = 0; 190 } 191 192 int LoudnessHistogram::GetBinIndex(double rms) { 193 // First exclude overload cases. 194 if (rms <= kHistBinCenters[0]) { 195 return 0; 196 } else if (rms >= kHistBinCenters[kHistSize - 1]) { 197 return kHistSize - 1; 198 } else { 199 // The quantizer is uniform in log domain. Alternatively we could do binary 200 // search in linear domain. 201 double rms_log = log(rms); 202 203 int index = static_cast<int>( 204 floor((rms_log - kLogDomainMinBinCenter) * kLogDomainStepSizeInverse)); 205 // The final decision is in linear domain. 206 double b = 0.5 * (kHistBinCenters[index] + kHistBinCenters[index + 1]); 207 if (rms > b) { 208 return index + 1; 209 } 210 return index; 211 } 212 } 213 214 double LoudnessHistogram::CurrentRms() const { 215 double p; 216 double mean_val = 0; 217 if (audio_content_q10_ > 0) { 218 double p_total_inverse = 1. / static_cast<double>(audio_content_q10_); 219 for (int n = 0; n < kHistSize; n++) { 220 p = static_cast<double>(bin_count_q10_[n]) * p_total_inverse; 221 mean_val += p * kHistBinCenters[n]; 222 } 223 } else { 224 mean_val = kHistBinCenters[0]; 225 } 226 return mean_val; 227 } 228 229 } // namespace webrtc 230