rms_level.cc (4235B)
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 <cstddef> 16 #include <cstdint> 17 #include <numeric> 18 #include <optional> 19 20 #include "api/array_view.h" 21 #include "rtc_base/checks.h" 22 23 namespace webrtc { 24 namespace { 25 constexpr float kMaxSquaredLevel = 32768 * 32768; 26 // kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10). 27 constexpr float kMinLevel = 1.995262314968883e-13f; 28 29 // Calculates the normalized RMS value from a mean square value. The input 30 // should be the sum of squared samples divided by the number of samples. The 31 // value will be normalized to full range before computing the RMS, wich is 32 // returned as a negated dBfs. That is, 0 is full amplitude while 127 is very 33 // faint. 34 int ComputeRms(float mean_square) { 35 if (mean_square <= kMinLevel * kMaxSquaredLevel) { 36 // Very faint; simply return the minimum value. 37 return RmsLevel::kMinLevelDb; 38 } 39 // Normalize by the max level. 40 const float mean_square_norm = mean_square / kMaxSquaredLevel; 41 RTC_DCHECK_GT(mean_square_norm, kMinLevel); 42 // 20log_10(x^0.5) = 10log_10(x) 43 const float rms = 10.f * std::log10(mean_square_norm); 44 RTC_DCHECK_LE(rms, 0.f); 45 RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb); 46 // Return the negated value. 47 return static_cast<int>(-rms + 0.5f); 48 } 49 } // namespace 50 51 RmsLevel::RmsLevel() { 52 Reset(); 53 } 54 55 RmsLevel::~RmsLevel() = default; 56 57 void RmsLevel::Reset() { 58 sum_square_ = 0.f; 59 sample_count_ = 0; 60 max_sum_square_ = 0.f; 61 block_size_ = std::nullopt; 62 } 63 64 void RmsLevel::Analyze(ArrayView<const int16_t> data) { 65 if (data.empty()) { 66 return; 67 } 68 69 CheckBlockSize(data.size()); 70 71 const float sum_square = 72 std::accumulate(data.begin(), data.end(), 0.f, 73 [](float a, int16_t b) { return a + b * b; }); 74 RTC_DCHECK_GE(sum_square, 0.f); 75 sum_square_ += sum_square; 76 sample_count_ += data.size(); 77 78 max_sum_square_ = std::max(max_sum_square_, sum_square); 79 } 80 81 void RmsLevel::Analyze(ArrayView<const float> data) { 82 if (data.empty()) { 83 return; 84 } 85 86 CheckBlockSize(data.size()); 87 88 float sum_square = 0.f; 89 90 for (float data_k : data) { 91 int16_t tmp = 92 static_cast<int16_t>(std::min(std::max(data_k, -32768.f), 32767.f)); 93 sum_square += tmp * tmp; 94 } 95 RTC_DCHECK_GE(sum_square, 0.f); 96 sum_square_ += sum_square; 97 sample_count_ += data.size(); 98 99 max_sum_square_ = std::max(max_sum_square_, sum_square); 100 } 101 102 void RmsLevel::AnalyzeMuted(size_t length) { 103 CheckBlockSize(length); 104 sample_count_ += length; 105 } 106 107 int RmsLevel::Average() { 108 const bool have_samples = (sample_count_ != 0); 109 int rms = have_samples ? ComputeRms(sum_square_ / sample_count_) 110 : RmsLevel::kMinLevelDb; 111 112 // To ensure that kMinLevelDb represents digital silence (muted audio 113 // sources) we'll check here if the sum_square is actually 0. If it's not 114 // we'll bump up the return value to `kInaudibleButNotMuted`. 115 // https://datatracker.ietf.org/doc/html/rfc6464 116 if (have_samples && rms == RmsLevel::kMinLevelDb && sum_square_ != 0.0f) { 117 rms = kInaudibleButNotMuted; 118 } 119 120 Reset(); 121 return rms; 122 } 123 124 RmsLevel::Levels RmsLevel::AverageAndPeak() { 125 // Note that block_size_ should by design always be non-empty when 126 // sample_count_ != 0. Also, the * operator of std::optional enforces this 127 // with a DCHECK. 128 Levels levels = 129 (sample_count_ == 0) 130 ? Levels{.average = RmsLevel::kMinLevelDb, 131 .peak = RmsLevel::kMinLevelDb} 132 : Levels{.average = ComputeRms(sum_square_ / sample_count_), 133 .peak = ComputeRms(max_sum_square_ / *block_size_)}; 134 Reset(); 135 return levels; 136 } 137 138 void RmsLevel::CheckBlockSize(size_t block_size) { 139 if (block_size_ != block_size) { 140 Reset(); 141 block_size_ = block_size; 142 } 143 } 144 } // namespace webrtc