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