Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 } | |
| OLD | NEW |