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 const bool have_samples = (sample_count_ != 0);
105 int rms = have_samples ? ComputeRms(sum_square_ / sample_count_)
106 : RmsLevel::kMinLevelDb;
107
108 // To ensure that kMinLevelDb represents digital silence (muted audio
109 // sources) we'll check here if the sum_square is actually 0. If it's not
110 // we'll bump up the return value to `kInaudibleButNotMuted`.
111 // https://datatracker.ietf.org/doc/html/rfc6464
112 if (have_samples && rms == RmsLevel::kMinLevelDb && sum_square_ != 0.0f) {
113 rms = kInaudibleButNotMuted;
114 }
115
116 Reset();
117 return rms;
118 }
119
AverageAndPeak()120 RmsLevel::Levels RmsLevel::AverageAndPeak() {
121 // Note that block_size_ should by design always be non-empty when
122 // sample_count_ != 0. Also, the * operator of absl::optional enforces this
123 // with a DCHECK.
124 Levels levels = (sample_count_ == 0)
125 ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
126 : Levels{ComputeRms(sum_square_ / sample_count_),
127 ComputeRms(max_sum_square_ / *block_size_)};
128 Reset();
129 return levels;
130 }
131
CheckBlockSize(size_t block_size)132 void RmsLevel::CheckBlockSize(size_t block_size) {
133 if (block_size_ != block_size) {
134 Reset();
135 block_size_ = block_size;
136 }
137 }
138 } // namespace webrtc
139