1// Copyright (c) 2013 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.
4
5#include "chrome/renderer/tts_dispatcher.h"
6
7#include "base/basictypes.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/common/tts_messages.h"
10#include "chrome/common/tts_utterance_request.h"
11#include "content/public/renderer/render_thread.h"
12#include "third_party/WebKit/public/platform/WebCString.h"
13#include "third_party/WebKit/public/platform/WebSpeechSynthesisUtterance.h"
14#include "third_party/WebKit/public/platform/WebSpeechSynthesisVoice.h"
15#include "third_party/WebKit/public/platform/WebString.h"
16#include "third_party/WebKit/public/platform/WebVector.h"
17
18using content::RenderThread;
19using blink::WebSpeechSynthesizerClient;
20using blink::WebSpeechSynthesisUtterance;
21using blink::WebSpeechSynthesisVoice;
22using blink::WebString;
23using blink::WebVector;
24
25int TtsDispatcher::next_utterance_id_ = 1;
26
27TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client)
28    : synthesizer_client_(client) {
29  RenderThread::Get()->AddObserver(this);
30}
31
32TtsDispatcher::~TtsDispatcher() {
33  RenderThread::Get()->RemoveObserver(this);
34}
35
36bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) {
37  IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message)
38    IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList)
39    IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking)
40    IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking)
41    IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking)
42    IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking)
43    IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary)
44    IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary)
45    IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent)
46    IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted)
47    IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled)
48    IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred)
49  IPC_END_MESSAGE_MAP()
50
51  // Always return false because there may be multiple TtsDispatchers
52  // and we want them all to have a chance to handle this message.
53  return false;
54}
55
56void TtsDispatcher::updateVoiceList() {
57  RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList());
58}
59
60void TtsDispatcher::speak(const WebSpeechSynthesisUtterance& web_utterance) {
61  int id = next_utterance_id_++;
62
63  utterance_id_map_[id] = web_utterance;
64
65  TtsUtteranceRequest utterance;
66  utterance.id = id;
67  utterance.text = web_utterance.text().utf8();
68  utterance.lang = web_utterance.lang().utf8();
69  utterance.voice = web_utterance.voice().utf8();
70  utterance.volume = web_utterance.volume();
71  utterance.rate = web_utterance.rate();
72  utterance.pitch = web_utterance.pitch();
73  RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance));
74}
75
76void TtsDispatcher::pause() {
77  RenderThread::Get()->Send(new TtsHostMsg_Pause());
78}
79
80void TtsDispatcher::resume() {
81  RenderThread::Get()->Send(new TtsHostMsg_Resume());
82}
83
84void TtsDispatcher::cancel() {
85  RenderThread::Get()->Send(new TtsHostMsg_Cancel());
86}
87
88WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) {
89  base::hash_map<int, WebSpeechSynthesisUtterance>::const_iterator iter =
90      utterance_id_map_.find(utterance_id);
91  if (iter == utterance_id_map_.end())
92    return WebSpeechSynthesisUtterance();
93  return iter->second;
94}
95
96void TtsDispatcher::OnSetVoiceList(const std::vector<TtsVoice>& voices) {
97  WebVector<WebSpeechSynthesisVoice> out_voices(voices.size());
98  for (size_t i = 0; i < voices.size(); ++i) {
99    out_voices[i] = WebSpeechSynthesisVoice();
100    out_voices[i].setVoiceURI(WebString::fromUTF8(voices[i].voice_uri));
101    out_voices[i].setName(WebString::fromUTF8(voices[i].name));
102    out_voices[i].setLanguage(WebString::fromUTF8(voices[i].lang));
103    out_voices[i].setIsLocalService(voices[i].local_service);
104    out_voices[i].setIsDefault(voices[i].is_default);
105  }
106  synthesizer_client_->setVoiceList(out_voices);
107}
108
109void TtsDispatcher::OnDidStartSpeaking(int utterance_id) {
110  if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end())
111    return;
112
113  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
114  if (utterance.isNull())
115    return;
116
117  synthesizer_client_->didStartSpeaking(utterance);
118}
119
120void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) {
121  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
122  if (utterance.isNull())
123    return;
124
125  synthesizer_client_->didFinishSpeaking(utterance);
126  utterance_id_map_.erase(utterance_id);
127}
128
129void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) {
130  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
131  if (utterance.isNull())
132    return;
133
134  synthesizer_client_->didPauseSpeaking(utterance);
135}
136
137void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) {
138  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
139  if (utterance.isNull())
140    return;
141
142  synthesizer_client_->didResumeSpeaking(utterance);
143}
144
145void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) {
146  CHECK(char_index >= 0);
147
148  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
149  if (utterance.isNull())
150    return;
151
152  synthesizer_client_->wordBoundaryEventOccurred(
153      utterance, static_cast<unsigned>(char_index));
154}
155
156void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) {
157  CHECK(char_index >= 0);
158
159  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
160  if (utterance.isNull())
161    return;
162
163  synthesizer_client_->sentenceBoundaryEventOccurred(
164      utterance, static_cast<unsigned>(char_index));
165}
166
167void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) {
168  // Not supported yet.
169}
170
171void TtsDispatcher::OnWasInterrupted(int utterance_id) {
172  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
173  if (utterance.isNull())
174    return;
175
176  // The web speech API doesn't support "interrupted".
177  synthesizer_client_->didFinishSpeaking(utterance);
178  utterance_id_map_.erase(utterance_id);
179}
180
181void TtsDispatcher::OnWasCancelled(int utterance_id) {
182  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
183  if (utterance.isNull())
184    return;
185
186  // The web speech API doesn't support "cancelled".
187  synthesizer_client_->didFinishSpeaking(utterance);
188  utterance_id_map_.erase(utterance_id);
189}
190
191void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id,
192                                            const std::string& error_message) {
193  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
194  if (utterance.isNull())
195    return;
196
197  // The web speech API doesn't support an error message.
198  synthesizer_client_->speakingErrorOccurred(utterance);
199  utterance_id_map_.erase(utterance_id);
200}
201