blob: 55db226af2509d0507bfc5873e0e87d7e2b8a37e [file] [log] [blame]
andrew@webrtc.org382c0c22014-05-05 18:22:21 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/rms_level.h"
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000012
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000013#include <math.h>
henrik.lundin50499422016-11-29 04:26:24 -080014#include <algorithm>
15#include <numeric>
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/checks.h"
kwiberg9e2be5f2016-09-14 05:23:22 -070018
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000019namespace webrtc {
henrik.lundin50499422016-11-29 04:26:24 -080020namespace {
21static constexpr float kMaxSquaredLevel = 32768 * 32768;
henrik.lundin50499422016-11-29 04:26:24 -080022// kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10).
23static constexpr float kMinLevel = 1.995262314968883e-13f;
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000024
henrik.lundin50499422016-11-29 04:26:24 -080025// 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.
30int ComputeRms(float mean_square) {
31 if (mean_square <= kMinLevel * kMaxSquaredLevel) {
32 // Very faint; simply return the minimum value.
henrik.lundin290d43a2016-11-29 08:09:09 -080033 return RmsLevel::kMinLevelDb;
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000034 }
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000035 // Normalize by the max level.
henrik.lundin50499422016-11-29 04:26:24 -080036 const float mean_square_norm = mean_square / kMaxSquaredLevel;
37 RTC_DCHECK_GT(mean_square_norm, kMinLevel);
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000038 // 20log_10(x^0.5) = 10log_10(x)
henrik.lundin50499422016-11-29 04:26:24 -080039 const float rms = 10.f * log10(mean_square_norm);
40 RTC_DCHECK_LE(rms, 0.f);
henrik.lundin290d43a2016-11-29 08:09:09 -080041 RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb);
henrik.lundin50499422016-11-29 04:26:24 -080042 // Return the negated value.
43 return static_cast<int>(-rms + 0.5f);
44}
45} // namespace
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000046
henrik.lundin50499422016-11-29 04:26:24 -080047RmsLevel::RmsLevel() {
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000048 Reset();
andrew@webrtc.org382c0c22014-05-05 18:22:21 +000049}
50
henrik.lundin50499422016-11-29 04:26:24 -080051RmsLevel::~RmsLevel() = default;
52
53void RmsLevel::Reset() {
54 sum_square_ = 0.f;
55 sample_count_ = 0;
56 max_sum_square_ = 0.f;
Oskar Sundbomaa8b67d2017-11-17 14:34:48 +010057 block_size_ = rtc::nullopt;
henrik.lundin50499422016-11-29 04:26:24 -080058}
59
60void 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
77void RmsLevel::AnalyzeMuted(size_t length) {
78 CheckBlockSize(length);
79 sample_count_ += length;
80}
81
82int RmsLevel::Average() {
henrik.lundin290d43a2016-11-29 08:09:09 -080083 int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb
henrik.lundin50499422016-11-29 04:26:24 -080084 : ComputeRms(sum_square_ / sample_count_);
85 Reset();
86 return rms;
87}
88
89RmsLevel::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 Levels levels = (sample_count_ == 0)
henrik.lundin290d43a2016-11-29 08:09:09 -080094 ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
henrik.lundin50499422016-11-29 04:26:24 -080095 : Levels{ComputeRms(sum_square_ / sample_count_),
96 ComputeRms(max_sum_square_ / *block_size_)};
97 Reset();
98 return levels;
99}
100
101void RmsLevel::CheckBlockSize(size_t block_size) {
Oskar Sundbomaa8b67d2017-11-17 14:34:48 +0100102 if (block_size_ != block_size) {
henrik.lundin50499422016-11-29 04:26:24 -0800103 Reset();
Oskar Sundbomaa8b67d2017-11-17 14:34:48 +0100104 block_size_ = block_size;
henrik.lundin50499422016-11-29 04:26:24 -0800105 }
106}
andrew@webrtc.org382c0c22014-05-05 18:22:21 +0000107} // namespace webrtc