xref: /aosp_15_r20/external/webrtc/modules/audio_processing/rms_level.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "modules/audio_processing/rms_level.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <cmath>
15*d9f75844SAndroid Build Coastguard Worker #include <numeric>
16*d9f75844SAndroid Build Coastguard Worker 
17*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
18*d9f75844SAndroid Build Coastguard Worker 
19*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
20*d9f75844SAndroid Build Coastguard Worker namespace {
21*d9f75844SAndroid Build Coastguard Worker static constexpr float kMaxSquaredLevel = 32768 * 32768;
22*d9f75844SAndroid Build Coastguard Worker // kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10).
23*d9f75844SAndroid Build Coastguard Worker static constexpr float kMinLevel = 1.995262314968883e-13f;
24*d9f75844SAndroid Build Coastguard Worker 
25*d9f75844SAndroid Build Coastguard Worker // Calculates the normalized RMS value from a mean square value. The input
26*d9f75844SAndroid Build Coastguard Worker // should be the sum of squared samples divided by the number of samples. The
27*d9f75844SAndroid Build Coastguard Worker // value will be normalized to full range before computing the RMS, wich is
28*d9f75844SAndroid Build Coastguard Worker // returned as a negated dBfs. That is, 0 is full amplitude while 127 is very
29*d9f75844SAndroid Build Coastguard Worker // faint.
ComputeRms(float mean_square)30*d9f75844SAndroid Build Coastguard Worker int ComputeRms(float mean_square) {
31*d9f75844SAndroid Build Coastguard Worker   if (mean_square <= kMinLevel * kMaxSquaredLevel) {
32*d9f75844SAndroid Build Coastguard Worker     // Very faint; simply return the minimum value.
33*d9f75844SAndroid Build Coastguard Worker     return RmsLevel::kMinLevelDb;
34*d9f75844SAndroid Build Coastguard Worker   }
35*d9f75844SAndroid Build Coastguard Worker   // Normalize by the max level.
36*d9f75844SAndroid Build Coastguard Worker   const float mean_square_norm = mean_square / kMaxSquaredLevel;
37*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GT(mean_square_norm, kMinLevel);
38*d9f75844SAndroid Build Coastguard Worker   // 20log_10(x^0.5) = 10log_10(x)
39*d9f75844SAndroid Build Coastguard Worker   const float rms = 10.f * std::log10(mean_square_norm);
40*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_LE(rms, 0.f);
41*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb);
42*d9f75844SAndroid Build Coastguard Worker   // Return the negated value.
43*d9f75844SAndroid Build Coastguard Worker   return static_cast<int>(-rms + 0.5f);
44*d9f75844SAndroid Build Coastguard Worker }
45*d9f75844SAndroid Build Coastguard Worker }  // namespace
46*d9f75844SAndroid Build Coastguard Worker 
RmsLevel()47*d9f75844SAndroid Build Coastguard Worker RmsLevel::RmsLevel() {
48*d9f75844SAndroid Build Coastguard Worker   Reset();
49*d9f75844SAndroid Build Coastguard Worker }
50*d9f75844SAndroid Build Coastguard Worker 
51*d9f75844SAndroid Build Coastguard Worker RmsLevel::~RmsLevel() = default;
52*d9f75844SAndroid Build Coastguard Worker 
Reset()53*d9f75844SAndroid Build Coastguard Worker void RmsLevel::Reset() {
54*d9f75844SAndroid Build Coastguard Worker   sum_square_ = 0.f;
55*d9f75844SAndroid Build Coastguard Worker   sample_count_ = 0;
56*d9f75844SAndroid Build Coastguard Worker   max_sum_square_ = 0.f;
57*d9f75844SAndroid Build Coastguard Worker   block_size_ = absl::nullopt;
58*d9f75844SAndroid Build Coastguard Worker }
59*d9f75844SAndroid Build Coastguard Worker 
Analyze(rtc::ArrayView<const int16_t> data)60*d9f75844SAndroid Build Coastguard Worker void RmsLevel::Analyze(rtc::ArrayView<const int16_t> data) {
61*d9f75844SAndroid Build Coastguard Worker   if (data.empty()) {
62*d9f75844SAndroid Build Coastguard Worker     return;
63*d9f75844SAndroid Build Coastguard Worker   }
64*d9f75844SAndroid Build Coastguard Worker 
65*d9f75844SAndroid Build Coastguard Worker   CheckBlockSize(data.size());
66*d9f75844SAndroid Build Coastguard Worker 
67*d9f75844SAndroid Build Coastguard Worker   const float sum_square =
68*d9f75844SAndroid Build Coastguard Worker       std::accumulate(data.begin(), data.end(), 0.f,
69*d9f75844SAndroid Build Coastguard Worker                       [](float a, int16_t b) { return a + b * b; });
70*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(sum_square, 0.f);
71*d9f75844SAndroid Build Coastguard Worker   sum_square_ += sum_square;
72*d9f75844SAndroid Build Coastguard Worker   sample_count_ += data.size();
73*d9f75844SAndroid Build Coastguard Worker 
74*d9f75844SAndroid Build Coastguard Worker   max_sum_square_ = std::max(max_sum_square_, sum_square);
75*d9f75844SAndroid Build Coastguard Worker }
76*d9f75844SAndroid Build Coastguard Worker 
Analyze(rtc::ArrayView<const float> data)77*d9f75844SAndroid Build Coastguard Worker void RmsLevel::Analyze(rtc::ArrayView<const float> data) {
78*d9f75844SAndroid Build Coastguard Worker   if (data.empty()) {
79*d9f75844SAndroid Build Coastguard Worker     return;
80*d9f75844SAndroid Build Coastguard Worker   }
81*d9f75844SAndroid Build Coastguard Worker 
82*d9f75844SAndroid Build Coastguard Worker   CheckBlockSize(data.size());
83*d9f75844SAndroid Build Coastguard Worker 
84*d9f75844SAndroid Build Coastguard Worker   float sum_square = 0.f;
85*d9f75844SAndroid Build Coastguard Worker 
86*d9f75844SAndroid Build Coastguard Worker   for (float data_k : data) {
87*d9f75844SAndroid Build Coastguard Worker     int16_t tmp =
88*d9f75844SAndroid Build Coastguard Worker         static_cast<int16_t>(std::min(std::max(data_k, -32768.f), 32767.f));
89*d9f75844SAndroid Build Coastguard Worker     sum_square += tmp * tmp;
90*d9f75844SAndroid Build Coastguard Worker   }
91*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(sum_square, 0.f);
92*d9f75844SAndroid Build Coastguard Worker   sum_square_ += sum_square;
93*d9f75844SAndroid Build Coastguard Worker   sample_count_ += data.size();
94*d9f75844SAndroid Build Coastguard Worker 
95*d9f75844SAndroid Build Coastguard Worker   max_sum_square_ = std::max(max_sum_square_, sum_square);
96*d9f75844SAndroid Build Coastguard Worker }
97*d9f75844SAndroid Build Coastguard Worker 
AnalyzeMuted(size_t length)98*d9f75844SAndroid Build Coastguard Worker void RmsLevel::AnalyzeMuted(size_t length) {
99*d9f75844SAndroid Build Coastguard Worker   CheckBlockSize(length);
100*d9f75844SAndroid Build Coastguard Worker   sample_count_ += length;
101*d9f75844SAndroid Build Coastguard Worker }
102*d9f75844SAndroid Build Coastguard Worker 
Average()103*d9f75844SAndroid Build Coastguard Worker int RmsLevel::Average() {
104*d9f75844SAndroid Build Coastguard Worker   const bool have_samples = (sample_count_ != 0);
105*d9f75844SAndroid Build Coastguard Worker   int rms = have_samples ? ComputeRms(sum_square_ / sample_count_)
106*d9f75844SAndroid Build Coastguard Worker                          : RmsLevel::kMinLevelDb;
107*d9f75844SAndroid Build Coastguard Worker 
108*d9f75844SAndroid Build Coastguard Worker   // To ensure that kMinLevelDb represents digital silence (muted audio
109*d9f75844SAndroid Build Coastguard Worker   // sources) we'll check here if the sum_square is actually 0. If it's not
110*d9f75844SAndroid Build Coastguard Worker   // we'll bump up the return value to `kInaudibleButNotMuted`.
111*d9f75844SAndroid Build Coastguard Worker   // https://datatracker.ietf.org/doc/html/rfc6464
112*d9f75844SAndroid Build Coastguard Worker   if (have_samples && rms == RmsLevel::kMinLevelDb && sum_square_ != 0.0f) {
113*d9f75844SAndroid Build Coastguard Worker     rms = kInaudibleButNotMuted;
114*d9f75844SAndroid Build Coastguard Worker   }
115*d9f75844SAndroid Build Coastguard Worker 
116*d9f75844SAndroid Build Coastguard Worker   Reset();
117*d9f75844SAndroid Build Coastguard Worker   return rms;
118*d9f75844SAndroid Build Coastguard Worker }
119*d9f75844SAndroid Build Coastguard Worker 
AverageAndPeak()120*d9f75844SAndroid Build Coastguard Worker RmsLevel::Levels RmsLevel::AverageAndPeak() {
121*d9f75844SAndroid Build Coastguard Worker   // Note that block_size_ should by design always be non-empty when
122*d9f75844SAndroid Build Coastguard Worker   // sample_count_ != 0. Also, the * operator of absl::optional enforces this
123*d9f75844SAndroid Build Coastguard Worker   // with a DCHECK.
124*d9f75844SAndroid Build Coastguard Worker   Levels levels = (sample_count_ == 0)
125*d9f75844SAndroid Build Coastguard Worker                       ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
126*d9f75844SAndroid Build Coastguard Worker                       : Levels{ComputeRms(sum_square_ / sample_count_),
127*d9f75844SAndroid Build Coastguard Worker                                ComputeRms(max_sum_square_ / *block_size_)};
128*d9f75844SAndroid Build Coastguard Worker   Reset();
129*d9f75844SAndroid Build Coastguard Worker   return levels;
130*d9f75844SAndroid Build Coastguard Worker }
131*d9f75844SAndroid Build Coastguard Worker 
CheckBlockSize(size_t block_size)132*d9f75844SAndroid Build Coastguard Worker void RmsLevel::CheckBlockSize(size_t block_size) {
133*d9f75844SAndroid Build Coastguard Worker   if (block_size_ != block_size) {
134*d9f75844SAndroid Build Coastguard Worker     Reset();
135*d9f75844SAndroid Build Coastguard Worker     block_size_ = block_size;
136*d9f75844SAndroid Build Coastguard Worker   }
137*d9f75844SAndroid Build Coastguard Worker }
138*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
139