diff options
author | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-06 10:16:03 +0000 |
---|---|---|
committer | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-06 10:16:03 +0000 |
commit | 1a32c8fca79780edaead58a8a0906aa42c373e0e (patch) | |
tree | e190bb894a466d8973ff4dc02f6f46bdbf600fc9 | |
parent | 7ced1e5f9fbe5bf63b1b19fd9b42d6bfdccc1370 (diff) | |
download | chromium_src-1a32c8fca79780edaead58a8a0906aa42c373e0e.zip chromium_src-1a32c8fca79780edaead58a8a0906aa42c373e0e.tar.gz chromium_src-1a32c8fca79780edaead58a8a0906aa42c373e0e.tar.bz2 |
Merge 272961 "Adds volume level measurements to the AudioInputCo..."
> Adds volume level measurements to the AudioInputController for low-latency clients
>
> BUG=
> R=xians@chromium.org
>
> Review URL: https://codereview.chromium.org/287873004
TBR=henrika@chromium.org
BUG=381070
Review URL: https://codereview.chromium.org/317363002
git-svn-id: svn://svn.chromium.org/chrome/branches/1985/src@275370 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/speech/speech_recognizer_impl.h | 2 | ||||
-rw-r--r-- | media/audio/audio_input_controller.cc | 85 | ||||
-rw-r--r-- | media/audio/audio_input_controller.h | 21 | ||||
-rw-r--r-- | media/audio/audio_input_controller_unittest.cc | 3 |
4 files changed, 111 insertions, 0 deletions
diff --git a/content/browser/speech/speech_recognizer_impl.h b/content/browser/speech/speech_recognizer_impl.h index 6c8a0932..abd3ab4 100644 --- a/content/browser/speech/speech_recognizer_impl.h +++ b/content/browser/speech/speech_recognizer_impl.h @@ -133,6 +133,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl media::AudioInputController::ErrorCode error_code) OVERRIDE; virtual void OnData(media::AudioInputController* controller, const uint8* data, uint32 size) OVERRIDE; + virtual void OnLog(media::AudioInputController* controller, + const std::string& message) OVERRIDE {} // SpeechRecognitionEngineDelegate methods. virtual void OnSpeechRecognitionEngineResults( diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index 4cf948b..ccd3062 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -5,11 +5,15 @@ #include "media/audio/audio_input_controller.h" #include "base/bind.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" +#include "base/time/time.h" #include "media/base/limits.h" #include "media/base/scoped_histogram_timer.h" #include "media/base/user_input_monitor.h" +using base::TimeDelta; + namespace { const int kMaxInputChannels = 3; @@ -25,6 +29,22 @@ const int kTimerResetIntervalSeconds = 1; // Mac devices and the initial timer interval has therefore been increased // from 1 second to 5 seconds. const int kTimerInitialIntervalSeconds = 5; + +#if defined(AUDIO_POWER_MONITORING) +// Time constant for AudioPowerMonitor. +// The utilized smoothing factor (alpha) in the exponential filter is given +// by 1-exp(-1/(fs*ts)), where fs is the sample rate in Hz and ts is the time +// constant given by |kPowerMeasurementTimeConstantMilliseconds|. +// Example: fs=44100, ts=10e-3 => alpha~0.022420 +// fs=44100, ts=20e-3 => alpha~0.165903 +// A large smoothing factor corresponds to a faster filter response to input +// changes since y(n)=alpha*x(n)+(1-alpha)*y(n-1), where x(n) is the input +// and y(n) is the output. +const int kPowerMeasurementTimeConstantMilliseconds = 10; + +// Time in seconds between two successive measurements of audio power levels. +const int kPowerMonitorLogIntervalSeconds = 5; +#endif } namespace media { @@ -173,6 +193,19 @@ void AudioInputController::DoCreate(AudioManager* audio_manager, const std::string& device_id) { DCHECK(task_runner_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); + +#if defined(AUDIO_POWER_MONITORING) + // Create the audio (power) level meter given the provided audio parameters. + // An AudioBus is also needed to wrap the raw data buffer from the native + // layer to match AudioPowerMonitor::Scan(). + // TODO(henrika): Remove use of extra AudioBus. See http://crbug.com/375155. + audio_level_.reset(new media::AudioPowerMonitor( + params.sample_rate(), + TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds))); + audio_bus_ = AudioBus::Create(params); + audio_params_ = params; +#endif + // TODO(miu): See TODO at top of file. Until that's resolved, assume all // platform audio input requires the |no_data_timer_| be used to auto-detect // errors. In reality, probably only Windows needs to be treated as @@ -370,6 +403,42 @@ void AudioInputController::OnData(AudioInputStream* stream, if (SharedMemoryAndSyncSocketMode()) { sync_writer_->Write(data, size, volume, key_pressed); sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); + +#if defined(AUDIO_POWER_MONITORING) + // Only do power-level measurements if an AudioPowerMonitor object has + // been created. Done in DoCreate() but not DoCreateForStream(), hence + // logging will mainly be done for WebRTC and WebSpeech clients. + if (!audio_level_) + return; + + // Perform periodic audio (power) level measurements. + if ((base::TimeTicks::Now() - last_audio_level_log_time_).InSeconds() > + kPowerMonitorLogIntervalSeconds) { + // Wrap data into an AudioBus to match AudioPowerMonitor::Scan. + // TODO(henrika): remove this section when capture side uses AudioBus. + // See http://crbug.com/375155 for details. + audio_bus_->FromInterleaved( + data, audio_bus_->frames(), audio_params_.bits_per_sample() / 8); + audio_level_->Scan(*audio_bus_, audio_bus_->frames()); + + // Get current average power level and add it to the log. + // Possible range is given by [-inf, 0] dBFS. + std::pair<float, bool> result = audio_level_->ReadCurrentPowerAndClip(); + + // Use event handler on the audio thread to relay a message to the ARIH + // in content which does the actual logging on the IO thread. + task_runner_->PostTask( + FROM_HERE, + base::Bind( + &AudioInputController::DoLogAudioLevel, this, result.first)); + + last_audio_level_log_time_ = base::TimeTicks::Now(); + + // Reset the average power level (since we don't log continuously). + audio_level_->Reset(); + } +#endif + return; } @@ -391,6 +460,22 @@ void AudioInputController::DoOnData(scoped_ptr<uint8[]> data, uint32 size) { handler_->OnData(this, data.get(), size); } +void AudioInputController::DoLogAudioLevel(float level_dbfs) { +#if defined(AUDIO_POWER_MONITORING) + DCHECK(task_runner_->BelongsToCurrentThread()); + if (!handler_) + return; + + std::string log_string = base::StringPrintf( + "AIC::OnData: average audio level=%.2f dBFS", level_dbfs); + static const float kSilenceThresholdDBFS = -72.24719896f; + if (level_dbfs < kSilenceThresholdDBFS) + log_string += " <=> no audio input!"; + + handler_->OnLog(this, log_string); +#endif +} + void AudioInputController::OnError(AudioInputStream* stream) { // Handle error on the audio-manager thread. task_runner_->PostTask(FROM_HERE, base::Bind( diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h index ac1a690..fba0e40 100644 --- a/media/audio/audio_input_controller.h +++ b/media/audio/audio_input_controller.h @@ -16,6 +16,9 @@ #include "base/timer/timer.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager_base.h" +#include "media/audio/audio_parameters.h" +#include "media/audio/audio_power_monitor.h" +#include "media/base/audio_bus.h" // An AudioInputController controls an AudioInputStream and records data // from this input stream. The two main methods are Record() and Close() and @@ -72,6 +75,11 @@ // namespace media { +// Only do power monitoring for non-mobile platforms to save resources. +#if !defined(OS_ANDROID) && !defined(OS_IOS) +#define AUDIO_POWER_MONITORING +#endif + class UserInputMonitor; class MEDIA_EXPORT AudioInputController @@ -111,6 +119,8 @@ class MEDIA_EXPORT AudioInputController ErrorCode error_code) = 0; virtual void OnData(AudioInputController* controller, const uint8* data, uint32 size) = 0; + virtual void OnLog(AudioInputController* controller, + const std::string& message) = 0; protected: virtual ~EventHandler() {} @@ -252,6 +262,7 @@ class MEDIA_EXPORT AudioInputController void DoSetVolume(double volume); void DoSetAutomaticGainControl(bool enabled); void DoOnData(scoped_ptr<uint8[]> data, uint32 size); + void DoLogAudioLevel(float level_dbfs); // Method which ensures that OnError() is triggered when data recording // times out. Called on the audio thread. @@ -305,6 +316,16 @@ class MEDIA_EXPORT AudioInputController UserInputMonitor* user_input_monitor_; +#if defined(AUDIO_POWER_MONITORING) + // Scans audio samples from OnData() as input to compute audio levels. + scoped_ptr<AudioPowerMonitor> audio_level_; + + // We need these to be able to feed data to the AudioPowerMonitor. + scoped_ptr<AudioBus> audio_bus_; + media::AudioParameters audio_params_; + base::TimeTicks last_audio_level_log_time_; +#endif + size_t prev_key_down_count_; DISALLOW_COPY_AND_ASSIGN(AudioInputController); diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc index f101835..4e11e35 100644 --- a/media/audio/audio_input_controller_unittest.cc +++ b/media/audio/audio_input_controller_unittest.cc @@ -55,6 +55,9 @@ class MockAudioInputControllerEventHandler AudioInputController::ErrorCode error_code)); MOCK_METHOD3(OnData, void(AudioInputController* controller, const uint8* data, uint32 size)); + MOCK_METHOD2(OnLog, + void(AudioInputController* controller, + const std::string& message)); private: DISALLOW_COPY_AND_ASSIGN(MockAudioInputControllerEventHandler); |