diff options
author | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-21 01:09:21 +0000 |
---|---|---|
committer | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-21 01:09:21 +0000 |
commit | fc9ad286d40c972471e9f30a2925bb7b5214350f (patch) | |
tree | a4d235eb2a079c254812bc58c0ada13d6e627d9a /media/audio | |
parent | 1c6b12a885c3fffbae05e45d2223cc1178c25788 (diff) | |
download | chromium_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.cc | 38 | ||||
-rw-r--r-- | media/audio/audio_output_controller.h | 25 | ||||
-rw-r--r-- | media/audio/audio_output_controller_unittest.cc | 35 | ||||
-rw-r--r-- | media/audio/audio_silence_detector.cc | 134 | ||||
-rw-r--r-- | media/audio/audio_silence_detector.h | 112 | ||||
-rw-r--r-- | media/audio/audio_silence_detector_unittest.cc | 227 |
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 |