Chromium Code Reviews| 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 |