| 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
|
|
|