Line data Source code
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 "webrtc/modules/audio_processing/rms_level.h"
12 :
13 : #include <math.h>
14 : #include <algorithm>
15 : #include <numeric>
16 :
17 : #include "webrtc/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.
30 0 : int ComputeRms(float mean_square) {
31 0 : if (mean_square <= kMinLevel * kMaxSquaredLevel) {
32 : // Very faint; simply return the minimum value.
33 0 : return RmsLevel::kMinLevelDb;
34 : }
35 : // Normalize by the max level.
36 0 : const float mean_square_norm = mean_square / kMaxSquaredLevel;
37 0 : RTC_DCHECK_GT(mean_square_norm, kMinLevel);
38 : // 20log_10(x^0.5) = 10log_10(x)
39 0 : const float rms = 10.f * log10(mean_square_norm);
40 0 : RTC_DCHECK_LE(rms, 0.f);
41 0 : RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb);
42 : // Return the negated value.
43 0 : return static_cast<int>(-rms + 0.5f);
44 : }
45 : } // namespace
46 :
47 0 : RmsLevel::RmsLevel() {
48 0 : Reset();
49 0 : }
50 :
51 : RmsLevel::~RmsLevel() = default;
52 :
53 0 : void RmsLevel::Reset() {
54 0 : sum_square_ = 0.f;
55 0 : sample_count_ = 0;
56 0 : max_sum_square_ = 0.f;
57 0 : block_size_ = rtc::Optional<size_t>();
58 0 : }
59 :
60 0 : void RmsLevel::Analyze(rtc::ArrayView<const int16_t> data) {
61 0 : if (data.empty()) {
62 0 : return;
63 : }
64 :
65 0 : CheckBlockSize(data.size());
66 :
67 : const float sum_square =
68 0 : std::accumulate(data.begin(), data.end(), 0.f,
69 0 : [](float a, int16_t b) { return a + b * b; });
70 0 : RTC_DCHECK_GE(sum_square, 0.f);
71 0 : sum_square_ += sum_square;
72 0 : sample_count_ += data.size();
73 :
74 0 : max_sum_square_ = std::max(max_sum_square_, sum_square);
75 : }
76 :
77 0 : void RmsLevel::AnalyzeMuted(size_t length) {
78 0 : CheckBlockSize(length);
79 0 : sample_count_ += length;
80 0 : }
81 :
82 0 : int RmsLevel::Average() {
83 0 : int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb
84 0 : : ComputeRms(sum_square_ / sample_count_);
85 0 : Reset();
86 0 : return rms;
87 : }
88 :
89 0 : RmsLevel::Levels RmsLevel::AverageAndPeak() {
90 : // Note that block_size_ should by design always be non-empty when
91 : // sample_count_ != 0. Also, the * operator of rtc::Optional enforces this
92 : // with a DCHECK.
93 0 : Levels levels = (sample_count_ == 0)
94 : ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
95 0 : : Levels{ComputeRms(sum_square_ / sample_count_),
96 0 : ComputeRms(max_sum_square_ / *block_size_)};
97 0 : Reset();
98 0 : return levels;
99 : }
100 :
101 0 : void RmsLevel::CheckBlockSize(size_t block_size) {
102 0 : if (block_size_ != rtc::Optional<size_t>(block_size)) {
103 0 : Reset();
104 0 : block_size_ = rtc::Optional<size_t>(block_size);
105 : }
106 0 : }
107 : } // namespace webrtc
|