blob: 6992a15194021c1b7734a255670cdfcab926d2f5 [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
henrik.lundin50499422016-11-29 04:26:24 -080013#include <algorithm>
Oleh Prypin19929582019-04-23 08:50:04 +020014#include <cmath>
henrik.lundin50499422016-11-29 04:26:24 -080015#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)
Oleh Prypin19929582019-04-23 08:50:04 +020039 const float rms = 10.f * std::log10(mean_square_norm);
henrik.lundin50499422016-11-29 04:26:24 -080040 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;
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +020057 block_size_ = absl::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
Per Ã…hgren928146f2019-08-20 09:19:21 +020077void 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
henrik.lundin50499422016-11-29 04:26:24 -080098void RmsLevel::AnalyzeMuted(size_t length) {
99 CheckBlockSize(length);
100 sample_count_ += length;
101}
102
103int RmsLevel::Average() {
henrik.lundin290d43a2016-11-29 08:09:09 -0800104 int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb
henrik.lundin50499422016-11-29 04:26:24 -0800105 : ComputeRms(sum_square_ / sample_count_);
106 Reset();
107 return rms;
108}
109
110RmsLevel::Levels RmsLevel::AverageAndPeak() {
111 // Note that block_size_ should by design always be non-empty when
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +0200112 // sample_count_ != 0. Also, the * operator of absl::optional enforces this
henrik.lundin50499422016-11-29 04:26:24 -0800113 // with a DCHECK.
114 Levels levels = (sample_count_ == 0)
henrik.lundin290d43a2016-11-29 08:09:09 -0800115 ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb}
henrik.lundin50499422016-11-29 04:26:24 -0800116 : Levels{ComputeRms(sum_square_ / sample_count_),
117 ComputeRms(max_sum_square_ / *block_size_)};
118 Reset();
119 return levels;
120}
121
122void RmsLevel::CheckBlockSize(size_t block_size) {
Oskar Sundbomaa8b67d2017-11-17 14:34:48 +0100123 if (block_size_ != block_size) {
henrik.lundin50499422016-11-29 04:26:24 -0800124 Reset();
Oskar Sundbomaa8b67d2017-11-17 14:34:48 +0100125 block_size_ = block_size;
henrik.lundin50499422016-11-29 04:26:24 -0800126 }
127}
andrew@webrtc.org382c0c22014-05-05 18:22:21 +0000128} // namespace webrtc