Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Unified Diff: media/audio/audio_power_monitor_unittest.cc

Issue 14600025: Replace AudioSilenceDetector with an AudioPowerMonitor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Replace RMS scheme with 1st-order low-pass filter, per crogers@. Simpler, single-threaded unit tes… Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/audio/audio_power_monitor_unittest.cc
diff --git a/media/audio/audio_power_monitor_unittest.cc b/media/audio/audio_power_monitor_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..17e8e766ffbdf36d55c51bedc94286b2c4a3eb8a
--- /dev/null
+++ b/media/audio/audio_power_monitor_unittest.cc
@@ -0,0 +1,315 @@
+// 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 <limits>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop.h"
+#include "base/time/time.h"
+#include "media/base/audio_bus.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+static const int kSampleRate = 48000;
+static const int kFramesPerBuffer = 128;
+
+static const int kTimeConstantMillis = 5;
+static const int kMeasurementPeriodMillis = 20;
+
+namespace {
+
+// Container for each parameterized test's data (input and expected results).
+class TestScenario {
+ public:
+ TestScenario(const float* data, int num_channels, int num_frames,
+ float expected_power, bool expected_clipped)
+ : expected_power_(expected_power), expected_clipped_(expected_clipped) {
+ CreatePopulatedBuffer(data, num_channels, num_frames);
+ }
+
+ // Copy constructor for ::testing::Values(...).
+ TestScenario(const TestScenario& other)
+ : expected_power_(other.expected_power_),
+ expected_clipped_(other.expected_clipped_) {
+ bus_ = AudioBus::Create(other.bus_->channels(), other.bus_->frames());
+ other.bus_->CopyTo(bus_.get());
+ }
+
+ const AudioBus& data() const {
+ return *bus_;
+ }
+
+ float expected_power() const {
+ return expected_power_;
+ }
+
+ bool expected_clipped() const {
+ return expected_clipped_;
+ }
+
+ private:
+ // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of
+ // data. The given test |data| is repeated to fill the buffer.
+ void CreatePopulatedBuffer(
+ const float* data, int num_channels, int num_frames) {
+ bus_ = AudioBus::Create(num_channels, kFramesPerBuffer);
+ for (int ch = 0; ch < num_channels; ++ch) {
+ for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) {
+ const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames);
+ memcpy(bus_->channel(ch) + frames, data + num_frames * ch,
+ sizeof(float) * num_to_copy);
+ }
+ }
+ }
+
+ const float expected_power_;
+ const bool expected_clipped_;
+ scoped_ptr<AudioBus> bus_;
+
+ DISALLOW_ASSIGN(TestScenario);
+};
+
+// An observer that receives power measurements. Each power measurement should
+// should make progress towards the goal value.
+class MeasurementObserver {
+ public:
+ MeasurementObserver(float goal_power_measurement, bool goal_clipped)
+ : goal_power_measurement_(goal_power_measurement),
+ goal_clipped_(goal_clipped), measurement_count_(0) {}
+
+ int measurement_count() const {
+ return measurement_count_;
+ }
+
+ float last_power_measurement() const {
+ return last_power_measurement_;
+ }
+
+ bool last_clipped() const {
+ return last_clipped_;
+ }
+
+ void OnPowerMeasured(float cur_power_measurement, bool clipped) {
+ if (measurement_count_ == 0) {
+ measurements_should_increase_ =
+ (cur_power_measurement < goal_power_measurement_);
+ } else {
+ SCOPED_TRACE(::testing::Message()
+ << "Power: goal=" << goal_power_measurement_
+ << "; last=" << last_power_measurement_
+ << "; cur=" << cur_power_measurement);
+
+ if (last_power_measurement_ != goal_power_measurement_) {
+ if (measurements_should_increase_) {
+ EXPECT_LE(last_power_measurement_, cur_power_measurement)
+ << "Measurements should be monotonically increasing.";
+ } else {
+ EXPECT_GE(last_power_measurement_, cur_power_measurement)
+ << "Measurements should be monotonically decreasing.";
+ }
+ } else {
+ EXPECT_EQ(last_power_measurement_, cur_power_measurement)
+ << "Measurements are numerically unstable at goal value.";
+ }
+ }
+
+ last_power_measurement_ = cur_power_measurement;
+ last_clipped_ = clipped;
+ ++measurement_count_;
+ }
+
+ private:
+ const float goal_power_measurement_;
+ const bool goal_clipped_;
+ int measurement_count_;
+ bool measurements_should_increase_;
+ float last_power_measurement_;
+ bool last_clipped_;
+
+ DISALLOW_COPY_AND_ASSIGN(MeasurementObserver);
+};
+
+} // namespace
+
+class AudioPowerMonitorTest : public ::testing::TestWithParam<TestScenario> {
+ public:
+ AudioPowerMonitorTest()
+ : power_monitor_(
+ kSampleRate,
+ base::TimeDelta::FromMilliseconds(kTimeConstantMillis),
+ base::TimeDelta::FromMilliseconds(kMeasurementPeriodMillis),
+ &message_loop_,
+ base::Bind(&AudioPowerMonitorTest::OnPowerMeasured,
+ base::Unretained(this))) {}
+
+ void FeedAndCheckExpectedPowerIsMeasured(
+ const AudioBus& bus, float power, bool clipped) {
+ // Feed the AudioPowerMonitor. It should post tasks to |message_loop_|.
+ static const int kNumFeedIters = 100;
+ for (int i = 0; i < kNumFeedIters; ++i)
+ power_monitor_.Scan(bus, bus.frames());
+
+ // Set up an observer and run all the enqueued tasks.
+ MeasurementObserver observer(power, clipped);
+ current_observer_ = &observer;
+ message_loop_.RunUntilIdle();
+ current_observer_ = NULL;
+
+ // Check that the results recorded by the observer are the same whole-number
+ // dBFS.
+ if (observer.measurement_count() > 0) {
+ EXPECT_EQ(static_cast<int>(power),
+ static_cast<int>(observer.last_power_measurement()));
+ EXPECT_EQ(clipped, observer.last_clipped());
+ } else {
+ // Edge case: AudioPowerMonitor reported no measurements. This infers it
+ // decided not to report any redundant measurements, which we assume is an
+ // unclipped "zero power" result.
+ EXPECT_EQ(static_cast<int>(-std::numeric_limits<float>::infinity()),
+ static_cast<int>(power));
+ EXPECT_EQ(false, clipped);
+ }
+ }
+
+ private:
+ void OnPowerMeasured(float power, bool clipped) {
+ CHECK(current_observer_);
+ current_observer_->OnPowerMeasured(power, clipped);
+ }
+
+ base::MessageLoop message_loop_;
+ AudioPowerMonitor power_monitor_;
+ MeasurementObserver* current_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest);
+};
+
+TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) {
+ const TestScenario& scenario = GetParam();
+
+ scoped_ptr<AudioBus> zeroed_bus =
+ AudioBus::Create(scenario.data().channels(), scenario.data().frames());
+ zeroed_bus->Zero();
+
+ // Send a "zero power" audio signal, then this scenario's audio signal, then
+ // the "zero power" audio signal again; testing that the power monitor
+ // measurements match expected values.
+ FeedAndCheckExpectedPowerIsMeasured(
+ *zeroed_bus, AudioPowerMonitor::kZeroPowerDBFS, false);
+ FeedAndCheckExpectedPowerIsMeasured(
+ scenario.data(), scenario.expected_power(), scenario.expected_clipped());
+ FeedAndCheckExpectedPowerIsMeasured(
+ *zeroed_bus, AudioPowerMonitor::kZeroPowerDBFS, false);
+}
+
+static const float kMonoSilentNoise[] = {
+ 0.01f, -0.01f
+};
+
+static const float kMonoMaxAmplitude[] = {
+ 1.0f
+};
+
+static const float kMonoMaxAmplitude2[] = {
+ -1.0f, 1.0f
+};
+
+static const float kMonoHalfMaxAmplitude[] = {
+ 0.5f, -0.5f, 0.5f, -0.5f
+};
+
+static const float kMonoAmplitudeClipped[] = {
+ 2.0f, -2.0f
+};
+
+static const float kMonoMaxAmplitudeWithClip[] = {
+ 2.0f, 0.0, 0.0f, 0.0f
+};
+
+static const float kMonoMaxAmplitudeWithClip2[] = {
+ 4.0f, 0.0, 0.0f, 0.0f
+};
+
+static const float kMonoContainsInfinity[] = {
+ 0.0f, 0.0f, 0.0f, std::numeric_limits<float>::infinity()
DaleCurtis 2013/07/02 22:26:34 Static initializer.
miu 2013/07/09 00:59:56 Done.
+};
+
+static const float kMonoContainsNaN[] = {
+ 0.5f, -0.5f, 0.5f, std::numeric_limits<float>::quiet_NaN()
DaleCurtis 2013/07/02 22:26:34 Ditto.
miu 2013/07/09 00:59:56 Done.
+};
+
+static const float kStereoSilentNoise[] = {
+ // left channel
+ 0.005f, -0.005f,
+ // right channel
+ 0.005f, -0.005f
+};
+
+static const float kStereoMaxAmplitude[] = {
+ // left channel
+ 1.0f, -1.0f,
+ // right channel
+ -1.0f, 1.0f
+};
+
+static const float kRightChannelMaxAmplitude[] = {
+ // left channel
+ 0.0f, 0.0f, 0.0f, 0.0f,
+ // right channel
+ -1.0f, 1.0f, -1.0f, 1.0f
+};
+
+static const float kLeftChannelHalfMaxAmplitude[] = {
+ // left channel
+ 0.5f, -0.5f, 0.5f, -0.5f,
+ // right channel
+ 0.0f, 0.0f, 0.0f, 0.0f,
+};
+
+static const float kStereoMixed[] = {
+ // left channel
+ 0.5f, -0.5f, 0.5f, -0.5f,
+ // right channel
+ -1.0f, 1.0f, -1.0f, 1.0f
+};
+
+static const float kStereoMixed2[] = {
+ // left channel
+ 1.0f, -1.0f, 0.75f, -0.75f, 0.5f, -0.5f, 0.25f, -0.25f,
+ // right channel
+ 0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f
+};
+
+INSTANTIATE_TEST_CASE_P(
+ Scenarios, AudioPowerMonitorTest,
+ ::testing::Values(
+ TestScenario(kMonoSilentNoise, 1, 1, -40, false),
+ TestScenario(kMonoMaxAmplitude, 1, 1,
+ AudioPowerMonitor::kMaxPowerDBFS, false),
+ TestScenario(kMonoMaxAmplitude2, 1, 2,
+ AudioPowerMonitor::kMaxPowerDBFS, false),
+ TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
+ TestScenario(kMonoAmplitudeClipped, 1, 2,
+ AudioPowerMonitor::kMaxPowerDBFS, true),
+ TestScenario(kMonoMaxAmplitudeWithClip, 1, 4,
+ AudioPowerMonitor::kMaxPowerDBFS, true),
+ TestScenario(kMonoMaxAmplitudeWithClip2, 1, 4,
+ AudioPowerMonitor::kMaxPowerDBFS, true),
+ TestScenario(kMonoContainsInfinity, 1, 4,
+ AudioPowerMonitor::kZeroPowerDBFS, true),
+ TestScenario(kMonoContainsNaN, 1, 4,
+ AudioPowerMonitor::kZeroPowerDBFS, false),
+ TestScenario(kStereoSilentNoise, 2, 1, -46, false),
+ TestScenario(kStereoMaxAmplitude, 2, 2,
+ AudioPowerMonitor::kMaxPowerDBFS, false),
+ TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
+ TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
+ TestScenario(kStereoMixed, 2, 4, -2, false),
+ TestScenario(kStereoMixed2, 2, 8, -3, false)));
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698