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..6aaa1db92799479c2753c7efb575e611fd971c44 |
--- /dev/null |
+++ b/media/audio/audio_power_monitor_unittest.cc |
@@ -0,0 +1,287 @@ |
+// 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/bind_helpers.h" |
+#include "base/message_loop.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/threading/thread.h" |
+#include "base/time.h" |
+#include "media/base/audio_bus.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::_; |
+using ::testing::FloatEq; |
+using ::testing::InvokeWithoutArgs; |
+using ::testing::TestWithParam; |
+using ::testing::Values; |
+ |
+namespace media { |
+ |
+static const int kSampleRate = 48000; |
+static const int kFramesPerBuffer = 128; |
+ |
+namespace { |
+ |
+class MockObserver { |
+ public: |
+ MOCK_METHOD1(OnPowerMeasured, void(float)); |
+}; |
+ |
+struct TestScenario { |
+ const float* data; |
+ int num_channels; |
+ int num_frames; |
+ float power_in_dbfs; |
+ |
+ TestScenario(const float* d, int c, int f, float p) |
+ : data(d), num_channels(c), num_frames(f), power_in_dbfs(p) {} |
+}; |
+ |
+} // namespace |
+ |
+class AudioPowerMonitorTest : public TestWithParam<TestScenario> { |
+ public: |
+ AudioPowerMonitorTest() |
+ : audio_manager_thread_(new base::Thread("AudioThread")), |
+ notification_received_(false, false) { |
+ audio_manager_thread_->Start(); |
+ audio_message_loop_ = audio_manager_thread_->message_loop_proxy(); |
+ } |
+ |
+ virtual ~AudioPowerMonitorTest() { |
+ SyncWithAudioThread(); |
+ } |
+ |
+ AudioPowerMonitor* power_monitor() const { |
+ return power_monitor_.get(); |
+ } |
+ |
+ void StartPowerMonitor(MockObserver* observer) { |
+ audio_message_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&AudioPowerMonitorTest::StartMonitorOnAudioThread, |
+ base::Unretained(this), observer)); |
+ SyncWithAudioThread(); |
+ } |
+ |
+ void StopPowerMonitor() { |
+ audio_message_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&AudioPowerMonitorTest::StopMonitorOnAudioThread, |
+ base::Unretained(this))); |
+ SyncWithAudioThread(); |
+ } |
+ |
+ // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of |
+ // data. The given test |data| is repeated to fill the buffer. |
+ scoped_ptr<AudioBus> CreatePopulatedBuffer( |
+ const float* data, int num_channels, int num_frames) { |
+ scoped_ptr<AudioBus> 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); |
+ } |
+ } |
+ return bus.Pass(); |
+ } |
+ |
+ scoped_ptr<AudioBus> CreateZeroedBuffer(int num_channels) { |
+ scoped_ptr<AudioBus> bus = AudioBus::Create(num_channels, kFramesPerBuffer); |
+ bus->Zero(); |
+ return bus.Pass(); |
+ } |
+ |
+ |
+ void SignalNotificationReceived() { |
+ notification_received_.Signal(); |
+ } |
+ |
+ void WaitForNotificationReceived() { |
+ notification_received_.Wait(); |
+ } |
+ |
+ // Post a task on the audio thread and block until it is executed. This |
+ // provides a barrier: All previous tasks pending on the audio thread have |
+ // completed before this method returns. |
+ void SyncWithAudioThread() { |
+ base::WaitableEvent done(false, false); |
+ audio_message_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); |
+ done.Wait(); |
+ } |
+ |
+ private: |
+ void StartMonitorOnAudioThread(MockObserver* observer) { |
+ const AudioPowerMonitor::PowerMeasurementCallback callback = |
+ base::Bind(&MockObserver::OnPowerMeasured, base::Unretained(observer)); |
+ power_monitor_.reset(new AudioPowerMonitor( |
+ kSampleRate, base::TimeDelta::FromMilliseconds(1), |
+ MessageLoop::current(), callback)); |
+ } |
+ |
+ void StopMonitorOnAudioThread() { |
+ power_monitor_.reset(); |
+ } |
+ |
+ scoped_ptr<base::Thread> audio_manager_thread_; |
+ scoped_refptr<base::MessageLoopProxy> audio_message_loop_; |
+ |
+ base::WaitableEvent notification_received_; |
+ |
+ scoped_ptr<AudioPowerMonitor> power_monitor_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest); |
+}; |
+ |
+TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) { |
+ const TestScenario& scenario = GetParam(); |
+ |
+ MockObserver observer; |
+ StartPowerMonitor(&observer); |
+ |
+ // Send silence and expect a callback with a "zero power" result. |
+ EXPECT_CALL(observer, |
+ OnPowerMeasured(FloatEq(AudioPowerMonitor::kZeroPowerDBFS))) |
+ .WillOnce(InvokeWithoutArgs( |
+ this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
+ .RetiresOnSaturation(); |
+ scoped_ptr<AudioBus> bus = CreateZeroedBuffer(scenario.num_channels); |
+ power_monitor()->Scan(*bus, bus->frames()); |
+ WaitForNotificationReceived(); |
+ |
+ // Send audio signal data to the power monitor and expect a callback with the |
+ // correct result. |
+ EXPECT_CALL(observer, OnPowerMeasured(FloatEq(scenario.power_in_dbfs))) |
+ .WillOnce(InvokeWithoutArgs( |
+ this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
+ .RetiresOnSaturation(); |
+ bus = CreatePopulatedBuffer( |
+ scenario.data, scenario.num_channels, scenario.num_frames); |
+ power_monitor()->Scan(*bus, bus->frames()); |
+ WaitForNotificationReceived(); |
+ |
+ // Send silence again and expect a callback with a "zero power" result again. |
+ EXPECT_CALL(observer, |
+ OnPowerMeasured(FloatEq(AudioPowerMonitor::kZeroPowerDBFS))) |
+ .WillOnce(InvokeWithoutArgs( |
+ this, &AudioPowerMonitorTest::SignalNotificationReceived)) |
+ .RetiresOnSaturation(); |
+ bus = CreateZeroedBuffer(scenario.num_channels); |
+ power_monitor()->Scan(*bus, bus->frames()); |
+ WaitForNotificationReceived(); |
+ |
+ StopPowerMonitor(); |
+} |
+ |
+static const float kMonoSilence[] = { |
+ 0.0f |
+}; |
+ |
+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 kMonoNeedsClipping[] = { |
+ 5.0f, -0.5, 0.5f, -0.5f |
+}; |
+ |
+static const float kMonoContainsInfinity[] = { |
+ 0.0f, 0.0f, 0.0f, std::numeric_limits<float>::infinity() |
+}; |
+ |
+static const float kMonoContainsNaN[] = { |
+ 0.5f, -0.5f, 0.5f, std::numeric_limits<float>::quiet_NaN() |
+}; |
+ |
+static const float kStereoSilence[] = { |
+ // left channel |
+ 0.0f, |
+ // right channel |
+ 0.0f |
+}; |
+ |
+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.5f, -0.5f, 0.25f, -0.25f, 0.125f, -0.125f, |
+ // right channel |
+ 0.125f, -0.125f, 0.25f, -0.25f, 0.5f, -0.5f, 1.0f, -1.0f |
+}; |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ Scenarios, AudioPowerMonitorTest, |
+ Values( |
+ TestScenario(kMonoSilence, 1, 1, |
+ AudioPowerMonitor::kZeroPowerDBFS), |
+ TestScenario(kMonoMaxAmplitude, 1, 1, |
+ AudioPowerMonitor::kMaxPowerDBFS), |
+ TestScenario(kMonoMaxAmplitude2, 1, 2, |
+ AudioPowerMonitor::kMaxPowerDBFS), |
+ TestScenario(kMonoHalfMaxAmplitude, 1, 4, |
+ -6.0206f), |
+ TestScenario(kMonoNeedsClipping, 1, 4, |
+ AudioPowerMonitor::kMaxPowerDBFS), |
+ TestScenario(kMonoContainsInfinity, 1, 4, |
+ AudioPowerMonitor::kMaxPowerDBFS), |
+ TestScenario(kMonoContainsNaN, 1, 4, |
+ AudioPowerMonitor::kZeroPowerDBFS), |
+ TestScenario(kStereoSilence, 2, 1, |
+ AudioPowerMonitor::kZeroPowerDBFS), |
+ TestScenario(kStereoMaxAmplitude, 2, 2, |
+ AudioPowerMonitor::kMaxPowerDBFS), |
+ TestScenario(kRightChannelMaxAmplitude, 2, 4, |
+ -3.0103f), |
+ TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, |
+ -9.0309f), |
+ TestScenario(kStereoMixed, 2, 4, |
+ -2.0412f), |
+ TestScenario(kStereoMixed2, 2, 8, |
+ -4.78821f))); |
+ |
+} // namespace media |