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