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

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

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

Powered by Google App Engine
This is Rietveld 408576698