1 /*
2  *  Copyright (c) 2014 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/rms_level.h"
12 
13 #include <algorithm>
14 #include <cmath>
15 #include <numeric>
16 
17 #include "rtc_base/checks.h"
18 
19 namespace webrtc {
20 namespace {
21 static constexpr float kMaxSquaredLevel = 32768 * 32768;
22 // kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10).
23 static constexpr float kMinLevel = 1.995262314968883e-13f;
24 
25 // Calculates the normalized RMS value from a mean square value. The input
26 // should be the sum of squared samples divided by the number of samples. The
27 // value will be normalized to full range before computing the RMS, wich is
28 // returned as a negated dBfs. That is, 0 is full amplitude while 127 is very
29 // faint.
ComputeRms(float mean_square)30 int ComputeRms(float mean_square) {
31   if (mean_square <= kMinLevel * kMaxSquaredLevel) {
32     // Very faint; simply return the minimum value.
33     return RmsLevel::kMinLevelDb;
34   }
35   // Normalize by the max level.
36   const float mean_square_norm = mean_square / kMaxSquaredLevel;
37   RTC_DCHECK_GT(mean_square_norm, kMinLevel);
38   // 20log_10(x^0.5) = 10log_10(x)
39   const float rms = 10.f * std::log10(mean_square_norm);
40   RTC_DCHECK_LE(rms, 0.f);
41   RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb);
42   // Return the negated value.
43   return static_cast<int>(-rms + 0.5f);
44 }
45 }  // namespace
46 
RmsLevel()47 RmsLevel::RmsLevel() {
48   Reset();
49 }
50 
51 RmsLevel::~RmsLevel() = default;
52 
Reset()53 void RmsLevel::Reset() {
54   sum_square_ = 0.f;
55   sample_count_ = 0;
56   max_sum_square_ = 0.f;
57   block_size_ = absl::nullopt;
58 }
59 
Analyze(rtc::ArrayView<const int16_t> data)60 void RmsLevel::Analyze(rtc::ArrayView<const int16_t> data) {
61   if (data.empty()) {
62     return;
63   }
64 
65   CheckBlockSize(data.size());
66 
67   const float sum_square =
68       std::accumulate(data.begin(), data.end(), 0.f,
69                       [](float a, int16_t b) { return a + b * b; });
70   RTC_DCHECK_GE(sum_square, 0.f);
71   sum_square_ += sum_square;
72   sample_count_ += data.size();
73 
74   max_sum_square_ = std::max(max_sum_square_, sum_square);
75 }
76 
Analyze(rtc::ArrayView<const float> data)77 void RmsLevel::Analyze(rtc::ArrayView<const float> data) {
78   if (data.empty()) {
79     return;
80   }
81 
82   CheckBlockSize(data.size());
83 
84   float sum_square = 0.f;
85 
86   for (float data_k : data) {
87     int16_t tmp =
88         static_cast<int16_t>(std::min(std::max(data_k, -32768.f), 32767.f));
89     sum_square += tmp * tmp;
90   }
91   RTC_DCHECK_GE(sum_square, 0.f);
92   sum_square_ += sum_square;
93   sample_count_ += data.size();
94 
95   max_sum_square_ = std::max(max_sum_square_, sum_square);
96 }
97 
AnalyzeMuted(size_t length)98 void RmsLevel::AnalyzeMuted(size_t length) {
99   CheckBlockSize(length);
100   sample_count_ += length;
101 }
102 
Average()103 int RmsLevel::Average() {
104   int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb
105                                  : ComputeRms(sum_square_ / sample_count_);
106   Reset();
107   return rms;
108 }
109 
AverageAndPeak()110 RmsLevel::Levels RmsLevel::AverageAndPeak() {
111   // Note that block_size_ should by design always be non-empty when
112   // sample_count_ != 0. Also, the * operator of absl::optional enforces this
113   // with a DCHECK.
114   Levels levels = (sample_count_ == 0)
115                       ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
116                       : Levels{ComputeRms(sum_square_ / sample_count_),
117                                ComputeRms(max_sum_square_ / *block_size_)};
118   Reset();
119   return levels;
120 }
121 
CheckBlockSize(size_t block_size)122 void RmsLevel::CheckBlockSize(size_t block_size) {
123   if (block_size_ != block_size) {
124     Reset();
125     block_size_ = block_size;
126   }
127 }
128 }  // namespace webrtc
129