summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authormiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-21 01:09:21 +0000
committermiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-21 01:09:21 +0000
commitfc9ad286d40c972471e9f30a2925bb7b5214350f (patch)
treea4d235eb2a079c254812bc58c0ada13d6e627d9a /media/audio
parent1c6b12a885c3fffbae05e45d2223cc1178c25788 (diff)
downloadchromium_src-fc9ad286d40c972471e9f30a2925bb7b5214350f.zip
chromium_src-fc9ad286d40c972471e9f30a2925bb7b5214350f.tar.gz
chromium_src-fc9ad286d40c972471e9f30a2925bb7b5214350f.tar.bz2
Revert 189095 "Silence detection for audio output UI favicon ind..."
kareng@ noticed crashes. Must revert before dev channel build. :( > Silence detection for audio output UI favicon indicator. > > Added a ProbablyContainsSilence() utility method to media::AudioBus, and then added silence detection logic to media::AudioOutputController. While the detection logic will run on the "native audio thread," it should use an inconsequential amount of CPU time. AudioOutputController will invoke a new EventHandler::OnAudible() callback no more often than every 50 ms, and only when transitioning to/from periods of silence. > > Also, to avoid O(N) look-ups in AudioRendererHost for each call to any AudioOutputController::EventHandler method, the interface was simplified such that each stream/controller gets its own EventHandler instance. Changed AudioRendererHost::AudioEntry to become the class that implements the EventHandler interface. Lots of related clean-ups throughout audio_renderer_host.cc to deflate the code. > > BUG=178343 > TEST=media_unittests, content_unittests, and confirmation by running chrome manually > TBR=avi > > > Review URL: https://chromiumcodereview.appspot.com/12426002 TBR=miu@chromium.org Review URL: https://codereview.chromium.org/12927004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189479 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/audio_output_controller.cc38
-rw-r--r--media/audio/audio_output_controller.h25
-rw-r--r--media/audio/audio_output_controller_unittest.cc35
-rw-r--r--media/audio/audio_silence_detector.cc134
-rw-r--r--media/audio/audio_silence_detector.h112
-rw-r--r--media/audio/audio_silence_detector_unittest.cc227
6 files changed, 29 insertions, 542 deletions
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index d162cc2..9120706 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -10,7 +10,6 @@
#include "base/threading/platform_thread.h"
#include "base/time.h"
#include "build/build_config.h"
-#include "media/audio/audio_silence_detector.h"
#include "media/audio/audio_util.h"
#include "media/audio/shared_memory_util.h"
@@ -19,17 +18,6 @@ using base::TimeDelta;
namespace media {
-// Amount of contiguous time where all audio is silent before considering the
-// stream to have transitioned and EventHandler::OnAudible() should be called.
-static const int kQuestionableSilencePeriodMillis = 50;
-
-// Sample value range below which audio is considered indistinguishably silent.
-//
-// TODO(miu): This value should be specified in dbFS units rather than full
-// scale. See TODO in audio_silence_detector.h.
-static const float kIndistinguishableSilenceThreshold =
- 1.0f / 4096.0f; // Note: This is approximately -72 dbFS.
-
// Polling-related constants.
const int AudioOutputController::kPollNumAttempts = 3;
const int AudioOutputController::kPollPauseInMilliseconds = 3;
@@ -121,7 +109,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
state_ = kError;
// TODO(hclam): Define error types.
- handler_->OnError(0);
+ handler_->OnError(this, 0);
return;
}
@@ -130,7 +118,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
state_ = kError;
// TODO(hclam): Define error types.
- handler_->OnError(0);
+ handler_->OnError(this, 0);
return;
}
@@ -147,7 +135,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
// And then report we have been created if we haven't done so already.
if (!is_for_device_change)
- handler_->OnCreated();
+ handler_->OnCreated(this);
}
void AudioOutputController::DoPlay() {
@@ -197,20 +185,12 @@ void AudioOutputController::StartStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
state_ = kPlaying;
- silence_detector_.reset(new AudioSilenceDetector(
- params_.sample_rate(),
- TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis),
- kIndistinguishableSilenceThreshold));
-
// We start the AudioOutputStream lazily.
AllowEntryToOnMoreIOData();
stream_->Start(this);
- // Tell the event handler that we are now playing, and also start the silence
- // detection notifications.
- handler_->OnPlaying();
- silence_detector_->Start(
- base::Bind(&EventHandler::OnAudible, base::Unretained(handler_)));
+ // Tell the event handler that we are now playing.
+ handler_->OnPlaying(this);
}
void AudioOutputController::StopStream() {
@@ -223,8 +203,6 @@ void AudioOutputController::StopStream() {
} else if (state_ == kPlaying) {
stream_->Stop();
DisallowEntryToOnMoreIOData();
- silence_detector_->Stop(true);
- silence_detector_.reset();
state_ = kPaused;
}
}
@@ -240,7 +218,7 @@ void AudioOutputController::DoPause() {
// Send a special pause mark to the low-latency audio thread.
sync_reader_->UpdatePendingBytes(kPauseMark);
- handler_->OnPaused();
+ handler_->OnPaused(this);
}
void AudioOutputController::DoFlush() {
@@ -281,7 +259,7 @@ void AudioOutputController::DoSetVolume(double volume) {
void AudioOutputController::DoReportError(int code) {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ != kClosed)
- handler_->OnError(code);
+ handler_->OnError(this, code);
}
int AudioOutputController::OnMoreData(AudioBus* dest,
@@ -310,8 +288,6 @@ int AudioOutputController::OnMoreIOData(AudioBus* source,
sync_reader_->UpdatePendingBytes(
buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
- silence_detector_->Scan(dest, frames);
-
AllowEntryToOnMoreIOData();
return frames;
}
diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h
index cfb685f..e8fae22 100644
--- a/media/audio/audio_output_controller.h
+++ b/media/audio/audio_output_controller.h
@@ -8,9 +8,7 @@
#include "base/atomic_ref_count.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/timer.h"
#include "media/audio/audio_buffers_state.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
@@ -59,8 +57,6 @@ class MessageLoop;
namespace media {
-class AudioSilenceDetector;
-
class MEDIA_EXPORT AudioOutputController
: public base::RefCountedThreadSafe<AudioOutputController>,
public AudioOutputStream::AudioSourceCallback,
@@ -71,12 +67,12 @@ class MEDIA_EXPORT AudioOutputController
// following methods are called on the audio manager thread.
class MEDIA_EXPORT EventHandler {
public:
- virtual void OnCreated() = 0;
- virtual void OnPlaying() = 0;
- virtual void OnAudible(bool is_audible) = 0;
- virtual void OnPaused() = 0;
- virtual void OnError(int error_code) = 0;
- virtual void OnDeviceChange(int new_buffer_size, int new_sample_rate) = 0;
+ virtual void OnCreated(AudioOutputController* controller) = 0;
+ virtual void OnPlaying(AudioOutputController* controller) = 0;
+ virtual void OnPaused(AudioOutputController* controller) = 0;
+ virtual void OnError(AudioOutputController* controller, int error_code) = 0;
+ virtual void OnDeviceChange(AudioOutputController* controller,
+ int new_buffer_size, int new_sample_rate) = 0;
protected:
virtual ~EventHandler() {}
@@ -197,10 +193,6 @@ class MEDIA_EXPORT AudioOutputController
void DoStartDiverting(AudioOutputStream* to_stream);
void DoStopDiverting();
- // Called at regular intervals during playback to check for a change in
- // silence and call EventHandler::OnAudible() when state changes occur.
- void MaybeInvokeAudibleCallback();
-
// Helper methods that start/stop physical stream.
void StartStream();
void StopStream();
@@ -253,11 +245,6 @@ class MEDIA_EXPORT AudioOutputController
// (when starting-up a stream).
base::WeakPtrFactory<AudioOutputController> weak_this_;
- // Scans audio samples from OnMoreIOData() as input and causes
- // EventHandler::OnAudbile() to be called whenever a transition to a period of
- // silence or non-silence is detected.
- scoped_ptr<AudioSilenceDetector> silence_detector_;
-
DISALLOW_COPY_AND_ASSIGN(AudioOutputController);
};
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index 699810f..944d661 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -38,13 +38,13 @@ class MockAudioOutputControllerEventHandler
public:
MockAudioOutputControllerEventHandler() {}
- MOCK_METHOD0(OnCreated, void());
- MOCK_METHOD0(OnPlaying, void());
- MOCK_METHOD1(OnAudible, void(bool is_audible));
- MOCK_METHOD0(OnPaused, void());
- MOCK_METHOD1(OnError, void(int error_code));
- MOCK_METHOD2(OnDeviceChange, void(int new_buffer_size, int new_sample_rate));
-
+ MOCK_METHOD1(OnCreated, void(AudioOutputController* controller));
+ MOCK_METHOD1(OnPlaying, void(AudioOutputController* controller));
+ MOCK_METHOD1(OnPaused, void(AudioOutputController* controller));
+ MOCK_METHOD2(OnError, void(AudioOutputController* controller,
+ int error_code));
+ MOCK_METHOD3(OnDeviceChange, void(AudioOutputController* controller,
+ int new_buffer_size, int new_sample_rate));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
};
@@ -117,7 +117,7 @@ class AudioOutputControllerTest : public testing::Test {
kSampleRate, kBitsPerSample, samples_per_packet);
if (params_.IsValid()) {
- EXPECT_CALL(mock_event_handler_, OnCreated())
+ EXPECT_CALL(mock_event_handler_, OnCreated(NotNull()))
.WillOnce(SignalEvent(&create_event_));
}
@@ -131,17 +131,14 @@ class AudioOutputControllerTest : public testing::Test {
}
void Play() {
- // Expect the event handler to receive one OnPlaying() call and one or more
- // OnAudible() calls.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ // Expect the event handler to receive one OnPlaying() call.
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
- EXPECT_CALL(mock_event_handler_, OnAudible(_))
- .Times(AtLeast(1));
// During playback, the mock pretends to provide audio data rendered and
// sent from the render process.
EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_))
- .Times(AtLeast(1));
+ .Times(AtLeast(2));
EXPECT_CALL(mock_sync_reader_, Read(_, _))
.WillRepeatedly(DoAll(PopulateBuffer(),
SignalEvent(&read_event_),
@@ -154,7 +151,7 @@ class AudioOutputControllerTest : public testing::Test {
void Pause() {
// Expect the event handler to receive one OnPaused() call.
- EXPECT_CALL(mock_event_handler_, OnPaused())
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
.WillOnce(SignalEvent(&pause_event_));
controller_->Pause();
@@ -163,9 +160,9 @@ class AudioOutputControllerTest : public testing::Test {
void ChangeDevice() {
// Expect the event handler to receive one OnPaying() call and no OnPaused()
// call.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
- EXPECT_CALL(mock_event_handler_, OnPaused())
+ EXPECT_CALL(mock_event_handler_, OnPaused(NotNull()))
.Times(0);
// Simulate a device change event to AudioOutputController from the
@@ -179,7 +176,7 @@ class AudioOutputControllerTest : public testing::Test {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
}
@@ -211,7 +208,7 @@ class AudioOutputControllerTest : public testing::Test {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching back.
- EXPECT_CALL(mock_event_handler_, OnPlaying())
+ EXPECT_CALL(mock_event_handler_, OnPlaying(NotNull()))
.WillOnce(SignalEvent(&play_event_));
}
diff --git a/media/audio/audio_silence_detector.cc b/media/audio/audio_silence_detector.cc
deleted file mode 100644
index 4f4bc06..0000000
--- a/media/audio/audio_silence_detector.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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_silence_detector.h"
-
-#include "base/float_util.h"
-#include "base/time.h"
-#include "media/base/audio_bus.h"
-
-using base::AtomicRefCountDec;
-using base::AtomicRefCountInc;
-using base::AtomicRefCountIsOne;
-using base::AtomicRefCountIsZero;
-
-namespace media {
-
-AudioSilenceDetector::AudioSilenceDetector(
- int sample_rate,
- const base::TimeDelta& questionable_silence_period,
- float indistinguishable_silence_threshold)
- : polling_period_(questionable_silence_period),
- frames_before_observing_silence_(
- sample_rate * questionable_silence_period.InSecondsF()),
- silence_threshold_(indistinguishable_silence_threshold),
- frames_silent_so_far_(frames_before_observing_silence_),
- observing_silence_(1),
- was_audible_(false) {
-}
-
-AudioSilenceDetector::~AudioSilenceDetector() {
- DCHECK(thread_checker_.CalledOnValidThread());
- // Note: If active, ~RepeatingTimer() will StopAndAbandon().
-}
-
-void AudioSilenceDetector::Start(const AudibleCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(notify_is_audible_.is_null());
- DCHECK(!callback.is_null());
-
- notify_is_audible_ = callback;
- was_audible_ = AtomicRefCountIsZero(&observing_silence_);
- notify_is_audible_.Run(was_audible_);
- poll_timer_.Start(
- FROM_HERE, polling_period_,
- this, &AudioSilenceDetector::MaybeInvokeAudibleCallback);
-}
-
-void AudioSilenceDetector::Stop(bool notify_ending_in_silence) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!notify_is_audible_.is_null());
-
- poll_timer_.Stop();
- if (notify_ending_in_silence)
- notify_is_audible_.Run(false);
- notify_is_audible_.Reset();
-}
-
-void AudioSilenceDetector::Scan(const AudioBus* buffer, int frames) {
- // Determine whether the frames just read are probably silence. If enough
- // frames of silence have been observed, flip the |observing_silence_|
- // boolean, which will be read by another thread.
- if (ProbablyContainsSilence(buffer, frames)) {
- // Note: Prevent indefinite incrementing of |frames_silent_so_far_|, to
- // avoid eventual integer overflow.
- if (frames_silent_so_far_ < frames_before_observing_silence_) {
- frames_silent_so_far_ += frames;
- if (frames_silent_so_far_ >= frames_before_observing_silence_) {
- DCHECK(AtomicRefCountIsZero(&observing_silence_));
- AtomicRefCountInc(&observing_silence_);
- }
- }
- } else {
- if (frames_silent_so_far_ >= frames_before_observing_silence_) {
- DCHECK(AtomicRefCountIsOne(&observing_silence_));
- AtomicRefCountDec(&observing_silence_);
- }
- frames_silent_so_far_ = 0;
- }
-}
-
-void AudioSilenceDetector::MaybeInvokeAudibleCallback() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- const bool is_now_audible = AtomicRefCountIsZero(&observing_silence_);
- if (was_audible_ && !is_now_audible)
- notify_is_audible_.Run(was_audible_ = false);
- else if (!was_audible_ && is_now_audible)
- notify_is_audible_.Run(was_audible_ = true);
-}
-
-bool AudioSilenceDetector::ProbablyContainsSilence(const AudioBus* buffer,
- int num_frames) {
- if (!buffer)
- return true;
- DCHECK_LE(num_frames, buffer->frames());
- if (buffer->frames() <= 0)
- return true;
-
- // Scan the data in each channel. If any one channel contains sound whose
- // range of values exceeds |silence_threshold_|, return false immediately.
- for (int i = 0; i < buffer->channels(); ++i) {
- // Examine the 1st, 2nd (+1), 4th (+2), 7th (+3), 11th (+4), etc. samples,
- // checking whether |silence_threshold_| has been breached each time. For
- // typical AudioBus sizes, this algorithm will in the worst case examine
- // fewer than 10% of the samples.
- //
- // Note that there *is* a heavy bias in sampling at the beginning of the
- // channels, but that doesn't matter. The buffer sizes are simply too
- // small. For example, it is commonplace to use 128-sample buffers, which
- // represents ~3 ms of audio at 44.1 kHz; and this means that frequencies
- // below ~350 Hz will span more than one buffer to make a full cycle. In
- // all, the algorithm here is meant to be dirt-simple math that isn't
- // susceptible to periodic bias within a single buffer.
- const float* p = buffer->channel(i);
- const float* const end_of_samples = p + num_frames;
- int skip = 1;
- float min_value = *p;
- float max_value = *p;
- for (p += skip; p < end_of_samples; ++skip, p += skip) {
- DCHECK(base::IsFinite(*p));
- if (*p < min_value)
- min_value = *p;
- else if (max_value < *p)
- max_value = *p;
- if ((max_value - min_value) > silence_threshold_)
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace media
diff --git a/media/audio/audio_silence_detector.h b/media/audio/audio_silence_detector.h
deleted file mode 100644
index b6c52a7..0000000
--- a/media/audio/audio_silence_detector.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// 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.
-
-#ifndef MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
-#define MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
-
-#include "base/atomic_ref_count.h"
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/timer.h"
-#include "media/base/media_export.h"
-
-// An audio silence detector. It is periodically provided an AudioBus by the
-// native audio thread, where simple logic determines whether the audio samples
-// in each channel in the buffer represent silence. If a long-enough period of
-// contiguous silence is observed in all channels, a notification callback is
-// run on the thread that constructed AudioSilenceDetector.
-//
-// Note that extreme care has been taken to make the
-// AudioSilenceDetector::Scan() method safe to be called on the native audio
-// thread. The code acquires no locks, nor engages in any operation that could
-// result in an undetermined/unbounded amount of run-time. Comments in
-// audio_silence_detector.cc elaborate further on the silence detection
-// algorithm.
-
-namespace base {
-class TimeDelta;
-}
-
-namespace media {
-
-class AudioBus;
-
-class MEDIA_EXPORT AudioSilenceDetector {
- public:
- typedef base::Callback<void(bool)> AudibleCallback;
-
- // Tunable parameters: |questionable_silence_period| is the amount of time
- // where audio must remain silent before triggerring a callback.
- // |indistinguishable_silence_threshold| is the value range below which audio
- // is considered silent, in full-scale units.
- //
- // TODO(miu): |indistinguishable_silence_threshold| should be specified in
- // dbFS units rather than full-scale. We need a dbFS data type for
- // media/audio first.
- AudioSilenceDetector(int sample_rate,
- const base::TimeDelta& questionable_silence_period,
- float indistinguishable_silence_threshold);
-
- ~AudioSilenceDetector();
-
- // Start detecting silence, notifying via the given callback.
- void Start(const AudibleCallback& notify_is_audible);
-
- // Stop detecting silence. If |notify_ending_in_silence| is true, a final
- // notify_is_audible(false) call will be made here.
- void Stop(bool notify_ending_in_silence);
-
- // Scan more |frames| of audio data from |buffer|. This is usually called
- // within the native audio thread's "more data" callback.
- void Scan(const AudioBus* buffer, int frames);
-
- private:
- // Called by |poll_timer_| at regular intervals to determine whether to invoke
- // the callback due to a silence state change.
- void MaybeInvokeAudibleCallback();
-
- // Returns true if the first |num_frames| frames in all channels in |buffer|
- // probably contain silence. A simple heuristic is used to quickly examine a
- // subset of the samples in each channel, hence the name of this method.
- // "Silence" means that the range of the sample values examined does not
- // exceed |silence_threshold_|.
- bool ProbablyContainsSilence(const AudioBus* buffer, int num_frames);
-
- // Time between polls for changes in state.
- const base::TimeDelta polling_period_;
-
- // Number of frames of contiguous silence to be observed before setting
- // |observing_silence_| to false.
- const int frames_before_observing_silence_;
-
- // Threshold below which audio should be considered indistinguishably silent.
- const float silence_threshold_;
-
- // Number of frames of contiguous silence observed thus far on the native
- // audio thread.
- int frames_silent_so_far_;
-
- // Boolean state (0 or 1) set by the native audio thread. This is polled
- // regularly by the thread that invokes the callback.
- base::AtomicRefCount observing_silence_;
-
- // Callback for notifying of a detected transition to silence or non-silence.
- AudibleCallback notify_is_audible_;
-
- // Last reported audible state, used for de-duping callback invocations.
- bool was_audible_;
-
- // Fires regularly, calling MaybeInvokeAudibleCallback().
- base::RepeatingTimer<AudioSilenceDetector> poll_timer_;
-
- // Constructor, destructor and most methods must be called on the same thread.
- base::ThreadChecker thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioSilenceDetector);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_SILENCE_DETECTOR_H_
diff --git a/media/audio/audio_silence_detector_unittest.cc b/media/audio/audio_silence_detector_unittest.cc
deleted file mode 100644
index 52e6958..0000000
--- a/media/audio/audio_silence_detector_unittest.cc
+++ /dev/null
@@ -1,227 +0,0 @@
-// 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_silence_detector.h"
-
-#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::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(OnAudible, void(bool));
-};
-
-struct TestScenario {
- const float* data;
- int num_channels;
- int num_frames;
- float value_range;
-
- TestScenario(const float* d, int c, int f, float v)
- : data(d), num_channels(c), num_frames(f), value_range(v) {}
-};
-
-} // namespace
-
-class AudioSilenceDetectorTest : public TestWithParam<TestScenario> {
- public:
- AudioSilenceDetectorTest()
- : 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 ~AudioSilenceDetectorTest() {
- SyncWithAudioThread();
- }
-
- AudioSilenceDetector* silence_detector() const {
- return silence_detector_.get();
- }
-
- void StartSilenceDetector(float threshold, MockObserver* observer) {
- audio_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AudioSilenceDetectorTest::StartDetectorOnAudioThread,
- base::Unretained(this), threshold, observer));
- SyncWithAudioThread();
- }
-
- void StopSilenceDetector() {
- audio_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AudioSilenceDetectorTest::StopDetectorOnAudioThread,
- 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();
- }
-
- 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 StartDetectorOnAudioThread(float threshold, MockObserver* observer) {
- const AudioSilenceDetector::AudibleCallback notify_is_audible =
- base::Bind(&MockObserver::OnAudible, base::Unretained(observer));
- silence_detector_.reset(new AudioSilenceDetector(
- kSampleRate, base::TimeDelta::FromMilliseconds(1), threshold));
- silence_detector_->Start(notify_is_audible);
- }
-
- void StopDetectorOnAudioThread() {
- silence_detector_->Stop(true);
- silence_detector_.reset();
- }
-
- scoped_ptr<base::Thread> audio_manager_thread_;
- scoped_refptr<base::MessageLoopProxy> audio_message_loop_;
-
- base::WaitableEvent notification_received_;
-
- scoped_ptr<AudioSilenceDetector> silence_detector_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioSilenceDetectorTest);
-};
-
-TEST_P(AudioSilenceDetectorTest, TriggersAtVariousThresholds) {
- static const float kThresholdsToTry[] =
- { 0.0f, 0.125f, 0.25f, 0.5f, 0.75f, 1.0f };
-
- const TestScenario& scenario = GetParam();
-
- for (size_t i = 0; i < arraysize(kThresholdsToTry); ++i) {
- MockObserver observer;
-
- // Start the detector. This should cause a single callback to indicate
- // we're starting out in silence.
- EXPECT_CALL(observer, OnAudible(false))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- StartSilenceDetector(kThresholdsToTry[i], &observer);
- WaitForNotificationReceived();
-
- // Send more data to the silence detector. For some test scenarios, the
- // sound data will trigger a callback.
- const bool expect_a_callback = (kThresholdsToTry[i] < scenario.value_range);
- if (expect_a_callback) {
- EXPECT_CALL(observer, OnAudible(true))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- }
- scoped_ptr<AudioBus> bus = CreatePopulatedBuffer(
- scenario.data, scenario.num_channels, scenario.num_frames);
- silence_detector()->Scan(bus.get(), bus->frames());
- if (expect_a_callback)
- WaitForNotificationReceived();
-
- // Stop the detector. This should cause another callback to indicate we're
- // ending in silence.
- EXPECT_CALL(observer, OnAudible(false))
- .WillOnce(InvokeWithoutArgs(
- this, &AudioSilenceDetectorTest::SignalNotificationReceived))
- .RetiresOnSaturation();
- StopSilenceDetector();
- WaitForNotificationReceived();
-
- SyncWithAudioThread();
- }
-}
-
-static const float kAllZeros[] = {
- // left channel
- 0.0f,
- // right channel
- 0.0f
-};
-
-static const float kAllOnes[] = {
- // left channel
- 1.0f,
- // right channel
- 1.0f
-};
-
-static const float kSilentLeftChannel[] = {
- // left channel
- 0.5f, 0.5f, 0.5f, 0.5f,
- // right channel
- 0.0f, 0.25f, 0.0f, 0.0f
-};
-
-static const float kSilentRightChannel[] = {
- // left channel
- 1.0f, 1.0f, 0.75f, 0.5f, 1.0f,
- // right channel
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
-};
-
-static const float kAtDifferentVolumesAndBias[] = {
- // left channel
- 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f,
- // right channel
- 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f, 0.0f, 0.2f, 0.4f, 0.6f, 0.4f, 0.2f
-};
-
-INSTANTIATE_TEST_CASE_P(
- Scenarios, AudioSilenceDetectorTest,
- Values(
- TestScenario(kAllZeros, 2, 1, 0.0f),
- TestScenario(kAllOnes, 2, 1, 0.0f),
- TestScenario(kSilentLeftChannel, 2, 4, 0.25f),
- TestScenario(kSilentRightChannel, 2, 5, 0.5f),
- TestScenario(kAtDifferentVolumesAndBias, 2, 12, 0.6f)));
-
-} // namespace media