summaryrefslogtreecommitdiffstats
path: root/content/browser/speech/speech_recognition_manager_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/speech/speech_recognition_manager_impl.cc')
-rw-r--r--content/browser/speech/speech_recognition_manager_impl.cc353
1 files changed, 353 insertions, 0 deletions
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc
new file mode 100644
index 0000000..9ced53f
--- /dev/null
+++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2012 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 "content/browser/speech/speech_recognition_manager_impl.h"
+
+#include "base/bind.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/speech/input_tag_speech_dispatcher_host.h"
+#include "content/browser/speech/speech_recognizer_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_view_host_delegate.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/speech_recognition_manager_delegate.h"
+#include "content/public/browser/speech_recognition_preferences.h"
+#include "content/public/common/view_type.h"
+
+using content::BrowserMainLoop;
+using content::BrowserThread;
+using content::SpeechRecognitionManager;
+using content::SpeechRecognitionManagerDelegate;
+
+SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() {
+ return speech::SpeechRecognitionManagerImpl::GetInstance();
+}
+
+namespace speech {
+
+struct SpeechRecognitionManagerImpl::SpeechRecognitionParams {
+ SpeechRecognitionParams(InputTagSpeechDispatcherHost* delegate,
+ int caller_id,
+ int render_process_id,
+ int render_view_id,
+ const gfx::Rect& element_rect,
+ const std::string& language,
+ const std::string& grammar,
+ const std::string& origin_url,
+ net::URLRequestContextGetter* context_getter,
+ content::SpeechRecognitionPreferences* recognition_prefs)
+ : delegate(delegate),
+ caller_id(caller_id),
+ render_process_id(render_process_id),
+ render_view_id(render_view_id),
+ element_rect(element_rect),
+ language(language),
+ grammar(grammar),
+ origin_url(origin_url),
+ context_getter(context_getter),
+ recognition_prefs(recognition_prefs) {
+ }
+
+ InputTagSpeechDispatcherHost* delegate;
+ int caller_id;
+ int render_process_id;
+ int render_view_id;
+ gfx::Rect element_rect;
+ std::string language;
+ std::string grammar;
+ std::string origin_url;
+ net::URLRequestContextGetter* context_getter;
+ content::SpeechRecognitionPreferences* recognition_prefs;
+};
+
+SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() {
+ return Singleton<SpeechRecognitionManagerImpl>::get();
+}
+
+SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl()
+ : can_report_metrics_(false),
+ recording_caller_id_(0) {
+ delegate_ = content::GetContentClient()->browser()->
+ GetSpeechRecognitionManagerDelegate();
+}
+
+SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() {
+ while (requests_.begin() != requests_.end())
+ CancelRecognition(requests_.begin()->first);
+}
+
+bool SpeechRecognitionManagerImpl::HasAudioInputDevices() {
+ return BrowserMainLoop::GetAudioManager()->HasAudioInputDevices();
+}
+
+bool SpeechRecognitionManagerImpl::IsCapturingAudio() {
+ return BrowserMainLoop::GetAudioManager()->IsRecordingInProcess();
+}
+
+string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() {
+ return BrowserMainLoop::GetAudioManager()->GetAudioInputDeviceModel();
+}
+
+bool SpeechRecognitionManagerImpl::HasPendingRequest(int caller_id) const {
+ return requests_.find(caller_id) != requests_.end();
+}
+
+InputTagSpeechDispatcherHost* SpeechRecognitionManagerImpl::GetDelegate(
+ int caller_id) const {
+ return requests_.find(caller_id)->second.delegate;
+}
+
+void SpeechRecognitionManagerImpl::ShowAudioInputSettings() {
+ // Since AudioManager::ShowAudioInputSettings can potentially launch external
+ // processes, do that in the FILE thread to not block the calling threads.
+ if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&SpeechRecognitionManagerImpl::ShowAudioInputSettings,
+ base::Unretained(this)));
+ return;
+ }
+
+ AudioManager* audio_manager = BrowserMainLoop::GetAudioManager();
+ DCHECK(audio_manager->CanShowAudioInputSettings());
+ if (audio_manager->CanShowAudioInputSettings())
+ audio_manager->ShowAudioInputSettings();
+}
+
+void SpeechRecognitionManagerImpl::StartRecognition(
+ InputTagSpeechDispatcherHost* delegate,
+ int caller_id,
+ int render_process_id,
+ int render_view_id,
+ const gfx::Rect& element_rect,
+ const std::string& language,
+ const std::string& grammar,
+ const std::string& origin_url,
+ net::URLRequestContextGetter* context_getter,
+ content::SpeechRecognitionPreferences* recognition_prefs) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &SpeechRecognitionManagerImpl::CheckRenderViewTypeAndStartRecognition,
+ base::Unretained(this),
+ SpeechRecognitionParams(
+ delegate, caller_id, render_process_id, render_view_id,
+ element_rect, language, grammar, origin_url, context_getter,
+ recognition_prefs)));
+}
+
+void SpeechRecognitionManagerImpl::CheckRenderViewTypeAndStartRecognition(
+ const SpeechRecognitionParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RenderViewHostImpl* render_view_host = RenderViewHostImpl::FromID(
+ params.render_process_id, params.render_view_id);
+ if (!render_view_host || !render_view_host->GetDelegate())
+ return;
+
+ // For host delegates other than TabContents we can't reliably show a popup,
+ // including the speech input bubble. In these cases for privacy reasons we
+ // don't want to start recording if the user can't be properly notified.
+ // An example of this is trying to show the speech input bubble within an
+ // extension popup: http://crbug.com/92083. In these situations the speech
+ // input extension API should be used instead.
+ if (render_view_host->GetDelegate()->GetRenderViewType() ==
+ content::VIEW_TYPE_TAB_CONTENTS) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SpeechRecognitionManagerImpl::ProceedStartingRecognition,
+ base::Unretained(this), params));
+ }
+}
+
+void SpeechRecognitionManagerImpl::ProceedStartingRecognition(
+ const SpeechRecognitionParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!HasPendingRequest(params.caller_id));
+
+ if (delegate_) {
+ delegate_->ShowRecognitionRequested(
+ params.caller_id, params.render_process_id, params.render_view_id,
+ params.element_rect);
+ delegate_->GetRequestInfo(&can_report_metrics_, &request_info_);
+ }
+
+ Request* request = &requests_[params.caller_id];
+ request->delegate = params.delegate;
+ request->recognizer = new SpeechRecognizerImpl(
+ this, params.caller_id, params.language, params.grammar,
+ params.context_getter, params.recognition_prefs->FilterProfanities(),
+ request_info_, can_report_metrics_ ? params.origin_url : "");
+ request->is_active = false;
+
+ StartRecognitionForRequest(params.caller_id);
+}
+
+void SpeechRecognitionManagerImpl::StartRecognitionForRequest(int caller_id) {
+ SpeechRecognizerMap::iterator request = requests_.find(caller_id);
+ if (request == requests_.end()) {
+ NOTREACHED();
+ return;
+ }
+
+ // We should not currently be recording for the caller.
+ CHECK(recording_caller_id_ != caller_id);
+
+ // If we are currently recording audio for another caller, abort that cleanly.
+ if (recording_caller_id_)
+ CancelRecognitionAndInformDelegate(recording_caller_id_);
+
+ if (!HasAudioInputDevices()) {
+ if (delegate_) {
+ delegate_->ShowMicError(caller_id,
+ SpeechRecognitionManagerDelegate::MIC_ERROR_NO_DEVICE_AVAILABLE);
+ }
+ } else if (IsCapturingAudio()) {
+ if (delegate_) {
+ delegate_->ShowMicError(
+ caller_id, SpeechRecognitionManagerDelegate::MIC_ERROR_DEVICE_IN_USE);
+ }
+ } else {
+ recording_caller_id_ = caller_id;
+ requests_[caller_id].is_active = true;
+ requests_[caller_id].recognizer->StartRecording();
+ if (delegate_)
+ delegate_->ShowWarmUp(caller_id);
+ }
+}
+
+void SpeechRecognitionManagerImpl::CancelRecognitionForRequest(int caller_id) {
+ // Ignore if the caller id was not in our active recognizers list because the
+ // user might have clicked more than once, or recognition could have been
+ // ended due to other reasons before the user click was processed.
+ if (!HasPendingRequest(caller_id))
+ return;
+
+ CancelRecognitionAndInformDelegate(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::FocusLostForRequest(int caller_id) {
+ // See above comment.
+ if (!HasPendingRequest(caller_id))
+ return;
+
+ // If this is an ongoing recording or if we were displaying an error message
+ // to the user, abort it since user has switched focus. Otherwise
+ // recognition has started and keep that going so user can start speaking to
+ // another element while this gets the results in parallel.
+ if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active)
+ CancelRecognitionAndInformDelegate(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::CancelRecognition(int caller_id) {
+ DCHECK(HasPendingRequest(caller_id));
+ if (requests_[caller_id].is_active)
+ requests_[caller_id].recognizer->CancelRecognition();
+ requests_.erase(caller_id);
+ if (recording_caller_id_ == caller_id)
+ recording_caller_id_ = 0;
+ if (delegate_)
+ delegate_->DoClose(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::CancelAllRequestsWithDelegate(
+ InputTagSpeechDispatcherHost* delegate) {
+ SpeechRecognizerMap::iterator it = requests_.begin();
+ while (it != requests_.end()) {
+ if (it->second.delegate == delegate) {
+ CancelRecognition(it->first);
+ // This map will have very few elements so it is simpler to restart.
+ it = requests_.begin();
+ } else {
+ ++it;
+ }
+ }
+}
+
+void SpeechRecognitionManagerImpl::StopRecording(int caller_id) {
+ // No pending requests on extension popups.
+ if (!HasPendingRequest(caller_id))
+ return;
+
+ requests_[caller_id].recognizer->StopRecording();
+}
+
+void SpeechRecognitionManagerImpl::SetRecognitionResult(
+ int caller_id, const content::SpeechRecognitionResult& result) {
+ DCHECK(HasPendingRequest(caller_id));
+ GetDelegate(caller_id)->SetRecognitionResult(caller_id, result);
+}
+
+void SpeechRecognitionManagerImpl::DidCompleteRecording(int caller_id) {
+ DCHECK(recording_caller_id_ == caller_id);
+ DCHECK(HasPendingRequest(caller_id));
+ recording_caller_id_ = 0;
+ GetDelegate(caller_id)->DidCompleteRecording(caller_id);
+ if (delegate_)
+ delegate_->ShowRecognizing(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::DidCompleteRecognition(int caller_id) {
+ GetDelegate(caller_id)->DidCompleteRecognition(caller_id);
+ requests_.erase(caller_id);
+ if (delegate_)
+ delegate_->DoClose(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::DidStartReceivingSpeech(int caller_id) {
+}
+
+void SpeechRecognitionManagerImpl::DidStopReceivingSpeech(int caller_id) {
+}
+
+void SpeechRecognitionManagerImpl::OnRecognizerError(
+ int caller_id, content::SpeechRecognitionErrorCode error) {
+ if (caller_id == recording_caller_id_)
+ recording_caller_id_ = 0;
+ requests_[caller_id].is_active = false;
+ if (delegate_)
+ delegate_->ShowRecognizerError(caller_id, error);
+}
+
+void SpeechRecognitionManagerImpl::DidStartReceivingAudio(int caller_id) {
+ DCHECK(HasPendingRequest(caller_id));
+ DCHECK(recording_caller_id_ == caller_id);
+ if (delegate_)
+ delegate_->ShowRecording(caller_id);
+}
+
+void SpeechRecognitionManagerImpl::DidCompleteEnvironmentEstimation(
+ int caller_id) {
+ DCHECK(HasPendingRequest(caller_id));
+ DCHECK(recording_caller_id_ == caller_id);
+}
+
+void SpeechRecognitionManagerImpl::SetInputVolume(int caller_id, float volume,
+ float noise_volume) {
+ DCHECK(HasPendingRequest(caller_id));
+ DCHECK_EQ(recording_caller_id_, caller_id);
+ if (delegate_)
+ delegate_->ShowInputVolume(caller_id, volume, noise_volume);
+}
+
+void SpeechRecognitionManagerImpl::CancelRecognitionAndInformDelegate(
+ int caller_id) {
+ InputTagSpeechDispatcherHost* cur_delegate = GetDelegate(caller_id);
+ CancelRecognition(caller_id);
+ cur_delegate->DidCompleteRecording(caller_id);
+ cur_delegate->DidCompleteRecognition(caller_id);
+}
+
+SpeechRecognitionManagerImpl::Request::Request()
+ : delegate(NULL),
+ is_active(false) {
+}
+
+SpeechRecognitionManagerImpl::Request::~Request() {
+}
+
+} // namespace speech