1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/speech_recognition_dispatcher.h"
7 #include "base/basictypes.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "content/common/speech_recognition_messages.h"
10 #include "content/renderer/render_view_impl.h"
11 #include "third_party/WebKit/public/platform/WebString.h"
12 #include "third_party/WebKit/public/platform/WebVector.h"
13 #include "third_party/WebKit/public/web/WebSpeechGrammar.h"
14 #include "third_party/WebKit/public/web/WebSpeechRecognitionParams.h"
15 #include "third_party/WebKit/public/web/WebSpeechRecognitionResult.h"
16 #include "third_party/WebKit/public/web/WebSpeechRecognizerClient.h"
18 using blink::WebVector
;
19 using blink::WebString
;
20 using blink::WebSpeechGrammar
;
21 using blink::WebSpeechRecognitionHandle
;
22 using blink::WebSpeechRecognitionResult
;
23 using blink::WebSpeechRecognitionParams
;
24 using blink::WebSpeechRecognizerClient
;
28 SpeechRecognitionDispatcher::SpeechRecognitionDispatcher(
29 RenderViewImpl
* render_view
)
30 : RenderViewObserver(render_view
),
31 recognizer_client_(NULL
),
35 SpeechRecognitionDispatcher::~SpeechRecognitionDispatcher() {
38 void SpeechRecognitionDispatcher::AbortAllRecognitions() {
39 Send(new SpeechRecognitionHostMsg_AbortAllRequests(
43 bool SpeechRecognitionDispatcher::OnMessageReceived(
44 const IPC::Message
& message
) {
46 IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcher
, message
)
47 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_Started
, OnRecognitionStarted
)
48 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioStarted
, OnAudioStarted
)
49 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_SoundStarted
, OnSoundStarted
)
50 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_SoundEnded
, OnSoundEnded
)
51 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioEnded
, OnAudioEnded
)
52 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_ErrorOccurred
, OnErrorOccurred
)
53 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_Ended
, OnRecognitionEnded
)
54 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_ResultRetrieved
,
56 IPC_MESSAGE_UNHANDLED(handled
= false)
61 void SpeechRecognitionDispatcher::start(
62 const WebSpeechRecognitionHandle
& handle
,
63 const WebSpeechRecognitionParams
& params
,
64 WebSpeechRecognizerClient
* recognizer_client
) {
65 DCHECK(!recognizer_client_
|| recognizer_client_
== recognizer_client
);
66 recognizer_client_
= recognizer_client
;
68 SpeechRecognitionHostMsg_StartRequest_Params msg_params
;
69 for (size_t i
= 0; i
< params
.grammars().size(); ++i
) {
70 const WebSpeechGrammar
& grammar
= params
.grammars()[i
];
71 msg_params
.grammars
.push_back(
72 SpeechRecognitionGrammar(grammar
.src().spec(), grammar
.weight()));
74 msg_params
.language
= base::UTF16ToUTF8(params
.language());
75 msg_params
.max_hypotheses
= static_cast<uint32
>(params
.maxAlternatives());
76 msg_params
.continuous
= params
.continuous();
77 msg_params
.interim_results
= params
.interimResults();
78 msg_params
.origin_url
= params
.origin().toString().utf8();
79 msg_params
.render_view_id
= routing_id();
80 msg_params
.request_id
= GetOrCreateIDForHandle(handle
);
81 // The handle mapping will be removed in |OnRecognitionEnd|.
82 Send(new SpeechRecognitionHostMsg_StartRequest(msg_params
));
85 void SpeechRecognitionDispatcher::stop(
86 const WebSpeechRecognitionHandle
& handle
,
87 WebSpeechRecognizerClient
* recognizer_client
) {
88 // Ignore a |stop| issued without a matching |start|.
89 if (recognizer_client_
!= recognizer_client
|| !HandleExists(handle
))
91 Send(new SpeechRecognitionHostMsg_StopCaptureRequest(
92 routing_id(), GetOrCreateIDForHandle(handle
)));
95 void SpeechRecognitionDispatcher::abort(
96 const WebSpeechRecognitionHandle
& handle
,
97 WebSpeechRecognizerClient
* recognizer_client
) {
98 // Ignore an |abort| issued without a matching |start|.
99 if (recognizer_client_
!= recognizer_client
|| !HandleExists(handle
))
101 Send(new SpeechRecognitionHostMsg_AbortRequest(
102 routing_id(), GetOrCreateIDForHandle(handle
)));
105 void SpeechRecognitionDispatcher::OnRecognitionStarted(int request_id
) {
106 recognizer_client_
->didStart(GetHandleFromID(request_id
));
109 void SpeechRecognitionDispatcher::OnAudioStarted(int request_id
) {
110 recognizer_client_
->didStartAudio(GetHandleFromID(request_id
));
113 void SpeechRecognitionDispatcher::OnSoundStarted(int request_id
) {
114 recognizer_client_
->didStartSound(GetHandleFromID(request_id
));
117 void SpeechRecognitionDispatcher::OnSoundEnded(int request_id
) {
118 recognizer_client_
->didEndSound(GetHandleFromID(request_id
));
121 void SpeechRecognitionDispatcher::OnAudioEnded(int request_id
) {
122 recognizer_client_
->didEndAudio(GetHandleFromID(request_id
));
125 static WebSpeechRecognizerClient::ErrorCode
WebKitErrorCode(
126 SpeechRecognitionErrorCode e
) {
128 case SPEECH_RECOGNITION_ERROR_NONE
:
130 return WebSpeechRecognizerClient::OtherError
;
131 case SPEECH_RECOGNITION_ERROR_ABORTED
:
132 return WebSpeechRecognizerClient::AbortedError
;
133 case SPEECH_RECOGNITION_ERROR_AUDIO
:
134 return WebSpeechRecognizerClient::AudioCaptureError
;
135 case SPEECH_RECOGNITION_ERROR_NETWORK
:
136 return WebSpeechRecognizerClient::NetworkError
;
137 case SPEECH_RECOGNITION_ERROR_NOT_ALLOWED
:
138 return WebSpeechRecognizerClient::NotAllowedError
;
139 case SPEECH_RECOGNITION_ERROR_NO_SPEECH
:
140 return WebSpeechRecognizerClient::NoSpeechError
;
141 case SPEECH_RECOGNITION_ERROR_NO_MATCH
:
143 return WebSpeechRecognizerClient::OtherError
;
144 case SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR
:
145 return WebSpeechRecognizerClient::BadGrammarError
;
148 return WebSpeechRecognizerClient::OtherError
;
151 void SpeechRecognitionDispatcher::OnErrorOccurred(
152 int request_id
, const SpeechRecognitionError
& error
) {
153 if (error
.code
== SPEECH_RECOGNITION_ERROR_NO_MATCH
) {
154 recognizer_client_
->didReceiveNoMatch(GetHandleFromID(request_id
),
155 WebSpeechRecognitionResult());
157 recognizer_client_
->didReceiveError(
158 GetHandleFromID(request_id
),
159 WebString(), // TODO(primiano): message?
160 WebKitErrorCode(error
.code
));
164 void SpeechRecognitionDispatcher::OnRecognitionEnded(int request_id
) {
165 // TODO(tommi): It is possible that the handle isn't found in the array if
166 // the user just refreshed the page. It seems that we then get a notification
167 // for the previously loaded instance of the page.
168 HandleMap::iterator iter
= handle_map_
.find(request_id
);
169 if (iter
== handle_map_
.end()) {
170 DLOG(ERROR
) << "OnRecognitionEnded called for a handle that doesn't exist";
172 WebSpeechRecognitionHandle handle
= iter
->second
;
173 // Note: we need to erase the handle from the map *before* calling didEnd.
174 // didEnd may call back synchronously to start a new recognition session,
175 // and we don't want to delete the handle from the map after that happens.
176 handle_map_
.erase(request_id
);
177 recognizer_client_
->didEnd(handle
);
181 void SpeechRecognitionDispatcher::OnResultsRetrieved(
182 int request_id
, const SpeechRecognitionResults
& results
) {
183 size_t provisional_count
= 0;
184 SpeechRecognitionResults::const_iterator it
= results
.begin();
185 for (; it
!= results
.end(); ++it
) {
186 if (it
->is_provisional
)
190 WebVector
<WebSpeechRecognitionResult
> provisional(provisional_count
);
191 WebVector
<WebSpeechRecognitionResult
> final(
192 results
.size() - provisional_count
);
194 int provisional_index
= 0, final_index
= 0;
195 for (it
= results
.begin(); it
!= results
.end(); ++it
) {
196 const SpeechRecognitionResult
& result
= (*it
);
197 WebSpeechRecognitionResult
* webkit_result
= result
.is_provisional
?
198 &provisional
[provisional_index
++] : &final
[final_index
++];
200 const size_t num_hypotheses
= result
.hypotheses
.size();
201 WebVector
<WebString
> transcripts(num_hypotheses
);
202 WebVector
<float> confidences(num_hypotheses
);
203 for (size_t i
= 0; i
< num_hypotheses
; ++i
) {
204 transcripts
[i
] = result
.hypotheses
[i
].utterance
;
205 confidences
[i
] = static_cast<float>(result
.hypotheses
[i
].confidence
);
207 webkit_result
->assign(transcripts
, confidences
, !result
.is_provisional
);
210 recognizer_client_
->didReceiveResults(
211 GetHandleFromID(request_id
), final
, provisional
);
215 int SpeechRecognitionDispatcher::GetOrCreateIDForHandle(
216 const WebSpeechRecognitionHandle
& handle
) {
217 // Search first for an existing mapping.
218 for (HandleMap::iterator iter
= handle_map_
.begin();
219 iter
!= handle_map_
.end();
221 if (iter
->second
.equals(handle
))
224 // If no existing mapping found, create a new one.
225 const int new_id
= next_id_
;
226 handle_map_
[new_id
] = handle
;
231 bool SpeechRecognitionDispatcher::HandleExists(
232 const WebSpeechRecognitionHandle
& handle
) {
233 for (HandleMap::iterator iter
= handle_map_
.begin();
234 iter
!= handle_map_
.end();
236 if (iter
->second
.equals(handle
))
242 const WebSpeechRecognitionHandle
& SpeechRecognitionDispatcher::GetHandleFromID(
244 HandleMap::iterator iter
= handle_map_
.find(request_id
);
245 DCHECK(iter
!= handle_map_
.end());
249 } // namespace content