Index: media/audio/audio_power_monitor.cc |
diff --git a/media/audio/audio_power_monitor.cc b/media/audio/audio_power_monitor.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f2fac987e84804bf226f2a5ddf357afc37425f89 |
--- /dev/null |
+++ b/media/audio/audio_power_monitor.cc |
@@ -0,0 +1,102 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/audio/audio_power_monitor.h" |
+ |
+#include <cmath> |
+#include <limits> |
+ |
+#include "base/bind.h" |
+#include "base/float_util.h" |
+#include "base/message_loop.h" |
+#include "base/time.h" |
+#include "media/base/audio_bus.h" |
+ |
+namespace media { |
+ |
+// dBFS reference level is 1.0f (the maximum possible value of a sample). |
+static const float kReferenceLevel = 1.0f; |
+ |
+const float AudioPowerMonitor::kZeroPowerDBFS = |
+ -std::numeric_limits<float>::infinity(); |
+ |
+const float AudioPowerMonitor::kMaxPowerDBFS = 0.0f; |
+ |
+AudioPowerMonitor::AudioPowerMonitor( |
+ int sample_rate, |
+ const base::TimeDelta& measurement_period, |
+ base::MessageLoop* message_loop, |
+ const PowerMeasurementCallback& callback) |
+ : num_frames_per_callback_(sample_rate * measurement_period.InSecondsF()), |
+ message_loop_(message_loop), |
+ notify_power_level_(callback) { |
+ DCHECK(message_loop_); |
+ DCHECK(!notify_power_level_.is_null()); |
+ ResetScanAccumulations(); |
+} |
+ |
+AudioPowerMonitor::~AudioPowerMonitor() { |
+} |
+ |
+void AudioPowerMonitor::Scan(const AudioBus& buffer, int num_frames) { |
+ DCHECK_LE(num_frames, buffer.frames()); |
+ const int num_channels = buffer.channels(); |
+ if (num_frames <= 0 || num_channels <= 0) |
+ return; |
+ |
+ // The overall goal here is to compute the RMS of the amplitudes over all |
+ // channels. An accumulation of sum-of-squares is maintained over one or more |
+ // calls to this method. Then, once a sufficient number of frames have been |
+ // scanned, the accumulated values are converted into dBFS units and a |
+ // callback is attempted. |
+ |
+ // Compute the sum of squared amplitudes for the audio signal in |buffer|. |
+ // |
+ // TODO(miu): Implement optimized SSE/NEON to efficiently calculate |
+ // sum-of-squares (or dot product, for more general-purpose use?) in |
+ // media/base/vector_math. |
+ float sum_of_squares = 0.0f; |
+ for (int i = 0; i < num_channels; ++i) { |
+ const float* p = buffer.channel(i); |
+ const float* const end_of_samples = p + num_frames; |
+ for (; p < end_of_samples; ++p) |
+ sum_of_squares += (*p) * (*p); |
+ } |
Chris Rogers
2013/05/16 22:39:02
Instead of computing RMS power here in this inner
miu
2013/07/02 06:10:16
Done.
|
+ if (base::IsNaN(sum_of_squares)) { |
+ sum_of_squares = 0.0f; |
+ } else { |
+ const float max_before_clipping = (1.0f * 1.0f) * num_frames * num_channels; |
+ if (sum_of_squares > max_before_clipping) |
+ sum_of_squares = max_before_clipping; |
+ } |
Chris Rogers
2013/05/16 22:39:02
For clipping, you need to check every single sampl
miu
2013/07/02 06:10:16
Good catch. Done.
|
+ |
+ // Accumulate with existing values. |
+ sum_of_squares_so_far_ += sum_of_squares / num_channels; |
+ frames_so_far_ += num_frames; |
+ |
+ // Once enough frames have been scanned, try to post a task to run the |
+ // callback with the dBFS result. The posting of the task is guaranteed to be |
+ // non-blocking, and therefore could fail. If that happens, the accumulated |
+ // values will be retained. We expect this to be a good policy since |
+ // TryPostTask() should rarely fail. Therefore, only rarely will the |
+ // measurement period be stretched, and only rarely will it be stretched by |
+ // more than one extra AudioBus' worth of frames. |
+ if (frames_so_far_ >= num_frames_per_callback_) { |
+ const float average_power = sum_of_squares_so_far_ / frames_so_far_; |
+ const float measurement_in_dbfs = |
+ 10.0f * log10(average_power / (kReferenceLevel * kReferenceLevel)); |
+ if (message_loop_->TryPostTask( |
+ FROM_HERE, |
+ base::Bind(notify_power_level_, measurement_in_dbfs))) { |
+ ResetScanAccumulations(); |
+ } |
+ } |
+} |
+ |
+void AudioPowerMonitor::ResetScanAccumulations() { |
+ sum_of_squares_so_far_ = 0.0f; |
+ frames_so_far_ = 0; |
+} |
+ |
+} // namespace media |