Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: chrome/browser/speech/extension_api/tts_extension_api_linux.cc

Issue 10915164: Initial support for native TTS on linux through tts extension API. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Export all libspeechd.h structures and remove header include. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <dlfcn.h>
6 #include <math.h>
7
5 #include "base/memory/singleton.h" 8 #include "base/memory/singleton.h"
6 #include "chrome/browser/speech/extension_api/tts_extension_api_platform.h" 9 #include "chrome/browser/speech/extension_api/tts_extension_api_platform.h"
10 #include "content/public/browser/browser_thread.h"
11
12 using content::BrowserThread;
7 13
8 namespace { 14 namespace {
9 const char kNotSupportedError[] = 15 const char kNotSupportedError[] =
10 "Native speech synthesis not supported on this platform."; 16 "Native speech synthesis not supported on this platform.";
17
18 // Speech dispatcher exports.
19 // The following types come from the libspeechd-dev package/libspeechd.h.
20 typedef enum{
dmazzoni 2012/09/10 21:42:05 Fix the spacing and indentation to match Chromium
21 SPD_MODE_SINGLE = 0,
22 SPD_MODE_THREADED = 1
23 }SPDConnectionMode;
24
25 typedef enum{
26 SPD_IMPORTANT = 1,
27 SPD_MESSAGE = 2,
28 SPD_TEXT = 3,
29 SPD_NOTIFICATION = 4,
30 SPD_PROGRESS = 5
31 }SPDPriority;
32
33 typedef enum{
34 SPD_EVENT_BEGIN,
35 SPD_EVENT_END,
36 SPD_EVENT_CANCEL,
37 SPD_EVENT_PAUSE,
38 SPD_EVENT_RESUME,
39 SPD_EVENT_INDEX_MARK
40 }SPDNotificationType;
41
42 typedef enum{
43 SPD_BEGIN = 1,
44 SPD_END = 2,
45 SPD_INDEX_MARKS = 4,
46 SPD_CANCEL = 8,
47 SPD_PAUSE = 16,
48 SPD_RESUME = 32
49 }SPDNotification;
50
51 typedef void (*SPDCallback)(
52 size_t msg_id, size_t client_id, SPDNotificationType state);
53 typedef void (*SPDCallbackIM)(size_t msg_id,
54 size_t client_id,
55 SPDNotificationType state,
56 char *index_mark);
57
58 typedef struct{
59
60 /* PUBLIC */
61 SPDCallback callback_begin;
62 SPDCallback callback_end;
63 SPDCallback callback_cancel;
64 SPDCallback callback_pause;
65 SPDCallback callback_resume;
66 SPDCallbackIM callback_im;
67
68 /* PRIVATE */
69 int socket;
70 FILE *stream;
71 SPDConnectionMode mode;
72
73 pthread_mutex_t *ssip_mutex;
74
75 pthread_t *events_thread;
76 pthread_mutex_t *comm_mutex;
77 pthread_cond_t *cond_reply_ready;
78 pthread_mutex_t *mutex_reply_ready;
79 pthread_cond_t *cond_reply_ack;
80 pthread_mutex_t *mutex_reply_ack;
81
82 char *reply;
83
84 }SPDConnection;
85
86 typedef SPDConnection* (*spd_open_func)(const char* client_name,
87 const char* connection_name,
88 const char* user_name,
89 SPDConnectionMode mode);
90 typedef int (*spd_say_func)(SPDConnection* connection,
91 SPDPriority priority,
92 const char* text);
93 typedef int (*spd_stop_func)(SPDConnection* connection);
94 typedef void (*spd_close_func)(SPDConnection* connection);
95 typedef int (*spd_set_notification_on_func)(SPDConnection* connection,
96 SPDNotification notification);
97 typedef int (*spd_set_voice_rate_func)(SPDConnection* connection, int rate);
98 typedef int (*spd_set_voice_pitch_func)(SPDConnection* connection, int pitch);
11 }; 99 };
12 100
101 class SpeechDispatcherWrapper {
102 public:
103 static SPDNotificationType current_notification_;
104
105 SpeechDispatcherWrapper();
106 ~SpeechDispatcherWrapper();
107
108 bool Speak(const char* text);
109 bool IsSpeaking();
110 bool StopSpeaking();
111 void SetRate(int rate);
112 void SetPitch(int pitch);
113
114 // Resets the connection with speech dispatcher.
115 void Reset();
116
117 // States whether Speech Dispatcher loaded successfully.
118 bool loaded() {
119 return loaded_;
120 }
121
122 private:
123 static void NotificationCallback(size_t msg_id,
124 size_t client_id,
125 SPDNotificationType type);
126
127 static void IndexMarkCallback(size_t msg_id,
128 size_t client_id,
129 SPDNotificationType state,
130 char *index_mark);
131
132 // Interface bindings.
133 spd_open_func spd_open;
134 spd_say_func spd_say;
135 spd_stop_func spd_stop;
136 spd_close_func spd_close;
137 spd_set_notification_on_func spd_set_notification_on;
138 spd_set_voice_rate_func spd_set_voice_rate;
139 spd_set_voice_pitch_func spd_set_voice_pitch;
140
141 bool loaded_;
142 void* library_;
143 SPDConnection* conn_;
144 DISALLOW_COPY_AND_ASSIGN(SpeechDispatcherWrapper);
145 };
146
147 // static
148 SPDNotificationType SpeechDispatcherWrapper::current_notification_ =
149 SPD_EVENT_END;
150
13 class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { 151 class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl {
14 public: 152 public:
15 virtual bool PlatformImplAvailable() { 153 virtual bool PlatformImplAvailable();
16 return false;
17 }
18
19 virtual bool Speak( 154 virtual bool Speak(
20 int utterance_id, 155 int utterance_id,
21 const std::string& utterance, 156 const std::string& utterance,
22 const std::string& lang, 157 const std::string& lang,
23 const UtteranceContinuousParameters& params) { 158 const UtteranceContinuousParameters& params);
24 error_ = kNotSupportedError; 159 virtual bool StopSpeaking();
25 return false; 160 virtual bool IsSpeaking();
26 } 161 virtual bool SendsEvent(TtsEventType event_type);
27 162 void OnSpeechEvent(SPDNotificationType type);
28 virtual bool StopSpeaking() {
29 error_ = kNotSupportedError;
30 return false;
31 }
32
33 virtual bool IsSpeaking() {
34 error_ = kNotSupportedError;
35 return false;
36 }
37
38 virtual bool SendsEvent(TtsEventType event_type) {
39 return false;
40 }
41 163
42 // Get the single instance of this class. 164 // Get the single instance of this class.
43 static ExtensionTtsPlatformImplLinux* GetInstance() { 165 static ExtensionTtsPlatformImplLinux* GetInstance();
44 return Singleton<ExtensionTtsPlatformImplLinux>::get();
45 }
46 166
47 private: 167 private:
48 ExtensionTtsPlatformImplLinux() {} 168 ExtensionTtsPlatformImplLinux(): utterance_id_(0) {}
49 virtual ~ExtensionTtsPlatformImplLinux() {} 169 virtual ~ExtensionTtsPlatformImplLinux() {}
50 170
171 SpeechDispatcherWrapper spd_;
172
173 // These apply to the current utterance only.
174 std::string utterance_;
175 int utterance_id_;
176
51 friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplLinux>; 177 friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplLinux>;
52 178
53 DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplLinux); 179 DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplLinux);
54 }; 180 };
55 181
56 // static 182 // static
57 ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() { 183 ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() {
58 return ExtensionTtsPlatformImplLinux::GetInstance(); 184 return ExtensionTtsPlatformImplLinux::GetInstance();
59 } 185 }
186
187 // static
188 void SpeechDispatcherWrapper::NotificationCallback(
189 size_t msg_id, size_t client_id, SPDNotificationType type) {
190 // We run Speech Dispatcher in threaded mode, so these callbacks should always
191 // be in a separate thread.
192 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
193 SpeechDispatcherWrapper::current_notification_ = type;
194 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
195 base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent,
196 base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()),
197 type));
198 }
199 }
200
201 // static
202 void SpeechDispatcherWrapper::IndexMarkCallback(size_t msg_id,
203 size_t client_id,
204 SPDNotificationType state,
205 char *index_mark) {
206 // TODO(dtseng): index_mark appears to specify an index type supplied by a
207 // client. Need to explore how this is used before hooking it up with existing
208 // word, sentence events.
209 // We run Speech Dispatcher in threaded mode, so these callbacks should always
210 // be in a separate thread.
211 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
212 SpeechDispatcherWrapper::current_notification_ = state;
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
214 base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent,
215 base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()),
216 state));
217 }
218 }
219
220 // static
221 ExtensionTtsPlatformImplLinux* ExtensionTtsPlatformImplLinux::GetInstance() {
dmazzoni 2012/09/10 21:42:05 Please re-organize the method implementations so t
222 return Singleton<ExtensionTtsPlatformImplLinux>::get();
223 }
224
225 SpeechDispatcherWrapper::SpeechDispatcherWrapper() : loaded_(false) {
226 library_ = dlopen("libspeechd.so", RTLD_LAZY);
227 if (!library_)
228 return;
229
230 spd_open = reinterpret_cast<spd_open_func>(dlsym(library_, "spd_open"));
231 if (!spd_open)
232 return;
233
234 spd_say = reinterpret_cast<spd_say_func>(dlsym(library_, "spd_say"));
235 if (!spd_say)
236 return;
237
238 spd_stop = reinterpret_cast<spd_stop_func>(dlsym(library_, "spd_stop"));
239 if (!spd_stop)
240 return;
241
242 spd_close = reinterpret_cast<spd_close_func>(dlsym(library_, "spd_close"));
243 if (!spd_close)
244 return;
245
246 conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED);
247 if (!conn_)
248 return;
249
250 spd_set_notification_on = reinterpret_cast<spd_set_notification_on_func>(
251 dlsym(library_, "spd_set_notification_on"));
252 if (!spd_set_notification_on)
253 return;
254
255 spd_set_voice_rate = reinterpret_cast<spd_set_voice_rate_func>(
256 dlsym(library_, "spd_set_voice_rate"));
257 if (!spd_set_voice_rate)
258 return;
259
260 spd_set_voice_pitch = reinterpret_cast<spd_set_voice_pitch_func>(
261 dlsym(library_, "spd_set_voice_pitch"));
262 if (!spd_set_voice_pitch)
263 return;
264
265 // Register callbacks for all events.
266 conn_->callback_begin =
267 conn_->callback_end =
268 conn_->callback_cancel =
269 conn_->callback_pause =
270 conn_->callback_resume =
271 &SpeechDispatcherWrapper::NotificationCallback;
272
273 conn_->callback_im = &SpeechDispatcherWrapper::IndexMarkCallback;
274
275 spd_set_notification_on(conn_, SPD_BEGIN);
276 spd_set_notification_on(conn_, SPD_END);
277 spd_set_notification_on(conn_, SPD_CANCEL);
278 spd_set_notification_on(conn_, SPD_PAUSE);
279 spd_set_notification_on(conn_, SPD_RESUME);
280
281 loaded_ = true;
282 }
283
284 SpeechDispatcherWrapper::~SpeechDispatcherWrapper() {
285 if (conn_) {
286 spd_close(conn_);
287 conn_ = NULL;
288 }
289
290 if (library_) {
291 dlclose(library_);
292 library_ = NULL;
293 }
294 }
295 bool SpeechDispatcherWrapper::Speak(const char* text) {
296 if (!loaded())
dmazzoni 2012/09/10 21:42:05 Indentation
297 return false;
298 if (spd_say(conn_, SPD_TEXT, text) == -1) {
299 Reset();
300 return false;
301 }
302 return true;
303 }
304
305 bool SpeechDispatcherWrapper::IsSpeaking() {
306 return SpeechDispatcherWrapper::current_notification_ == SPD_EVENT_BEGIN;
307 }
308
309 bool SpeechDispatcherWrapper::StopSpeaking() {
310 if (!loaded())
311 return false;
312 if (spd_stop(conn_) == -1) {
313 Reset();
314 return false;
315 }
316 return true;
317 }
318 void SpeechDispatcherWrapper::SetRate(int rate) {
319 spd_set_voice_rate(conn_, rate);
320 }
321
322 void SpeechDispatcherWrapper::SetPitch(int pitch) {
323 spd_set_voice_pitch(conn_, pitch);
324 }
325
326 // Resets the connection with speech dispatcher.
327 void SpeechDispatcherWrapper::Reset() {
328 if (conn_)
329 spd_close(conn_);
330 conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED);
331 }
332
333 bool ExtensionTtsPlatformImplLinux::PlatformImplAvailable() {
334 return spd_.loaded();
335 }
336
337 bool ExtensionTtsPlatformImplLinux::Speak(
338 int utterance_id,
339 const std::string& utterance,
340 const std::string& lang,
341 const UtteranceContinuousParameters& params) {
342 if (!spd_.loaded()) {
343 error_ = kNotSupportedError;
344 return false;
345 }
346
347 // Speech dispatcher's speech params are around 3x at either limit.
348 float rate = params.rate > 3 ? 3 : params.rate;
349 rate = params.rate < 0.334 ? 0.334 : rate;
350 float pitch = params.pitch > 3 ? 3 : params.pitch;
351 pitch = params.pitch < 0.334 ? 0.334 : pitch;
352
353 // Map our multiplicative range to Speech Dispatcher's linear range.
354 // .334 = -100.
355 // 3 = 100.
356 spd_.SetRate(100 * log10(rate) / log10(3));
357 spd_.SetPitch(100 * log10(pitch) / log10(3));
358
359 utterance_ = utterance;
360 utterance_id_ = utterance_id;
361
362 spd_.Speak(utterance.c_str());
363 return true;
364 }
365
366 bool ExtensionTtsPlatformImplLinux::StopSpeaking() {
367 return spd_.StopSpeaking();
368 }
369
370 bool ExtensionTtsPlatformImplLinux::IsSpeaking() {
371 return spd_.IsSpeaking();
372 }
373
374 bool ExtensionTtsPlatformImplLinux::SendsEvent(TtsEventType event_type) {
375 return (event_type == TTS_EVENT_START ||
376 event_type == TTS_EVENT_END ||
377 event_type == TTS_EVENT_CANCELLED ||
378 event_type == TTS_EVENT_MARKER);
379 }
380
381 void ExtensionTtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
382 ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
383 switch (type) {
384 case SPD_EVENT_BEGIN:
385 case SPD_EVENT_RESUME:
386 controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
387 break;
388 case SPD_EVENT_END:
389 case SPD_EVENT_PAUSE:
390 controller->OnTtsEvent(
391 utterance_id_, TTS_EVENT_END, utterance_.size(), std::string());
392 break;
393 case SPD_EVENT_CANCEL:
394 controller->OnTtsEvent(
395 utterance_id_, TTS_EVENT_CANCELLED, 0, std::string());
396 break;
397 case SPD_EVENT_INDEX_MARK:
398 controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
399 break;
400 }
401 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698