Chromium Code Reviews| Index: chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| diff --git a/chrome/browser/speech/extension_api/tts_extension_api_linux.cc b/chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| index f289f50edc706e46002d5b9a240b4774a0cc2bc9..6deb0ea5c9dc8ad9c2e5c33d1c49a50e2cdca563 100644 |
| --- a/chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| +++ b/chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| @@ -2,52 +2,178 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <dlfcn.h> |
| +#include <math.h> |
| + |
| #include "base/memory/singleton.h" |
| #include "chrome/browser/speech/extension_api/tts_extension_api_platform.h" |
| +#include "content/public/browser/browser_thread.h" |
| + |
| +using content::BrowserThread; |
| namespace { |
| const char kNotSupportedError[] = |
| "Native speech synthesis not supported on this platform."; |
| + |
| +// Speech dispatcher exports. |
| +// The following types come from the libspeechd-dev package/libspeechd.h. |
| +typedef enum{ |
|
dmazzoni
2012/09/10 21:42:05
Fix the spacing and indentation to match Chromium
|
| + SPD_MODE_SINGLE = 0, |
| + SPD_MODE_THREADED = 1 |
| +}SPDConnectionMode; |
| + |
| +typedef enum{ |
| + SPD_IMPORTANT = 1, |
| + SPD_MESSAGE = 2, |
| + SPD_TEXT = 3, |
| + SPD_NOTIFICATION = 4, |
| + SPD_PROGRESS = 5 |
| +}SPDPriority; |
| + |
| +typedef enum{ |
| + SPD_EVENT_BEGIN, |
| + SPD_EVENT_END, |
| + SPD_EVENT_CANCEL, |
| + SPD_EVENT_PAUSE, |
| + SPD_EVENT_RESUME, |
| + SPD_EVENT_INDEX_MARK |
| +}SPDNotificationType; |
| + |
| +typedef enum{ |
| + SPD_BEGIN = 1, |
| + SPD_END = 2, |
| + SPD_INDEX_MARKS = 4, |
| + SPD_CANCEL = 8, |
| + SPD_PAUSE = 16, |
| + SPD_RESUME = 32 |
| +}SPDNotification; |
| + |
| +typedef void (*SPDCallback)( |
| + size_t msg_id, size_t client_id, SPDNotificationType state); |
| +typedef void (*SPDCallbackIM)(size_t msg_id, |
| + size_t client_id, |
| + SPDNotificationType state, |
| + char *index_mark); |
| + |
| +typedef struct{ |
| + |
| + /* PUBLIC */ |
| + SPDCallback callback_begin; |
| + SPDCallback callback_end; |
| + SPDCallback callback_cancel; |
| + SPDCallback callback_pause; |
| + SPDCallback callback_resume; |
| + SPDCallbackIM callback_im; |
| + |
| + /* PRIVATE */ |
| + int socket; |
| + FILE *stream; |
| + SPDConnectionMode mode; |
| + |
| + pthread_mutex_t *ssip_mutex; |
| + |
| + pthread_t *events_thread; |
| + pthread_mutex_t *comm_mutex; |
| + pthread_cond_t *cond_reply_ready; |
| + pthread_mutex_t *mutex_reply_ready; |
| + pthread_cond_t *cond_reply_ack; |
| + pthread_mutex_t *mutex_reply_ack; |
| + |
| + char *reply; |
| + |
| +}SPDConnection; |
| + |
| +typedef SPDConnection* (*spd_open_func)(const char* client_name, |
| + const char* connection_name, |
| + const char* user_name, |
| + SPDConnectionMode mode); |
| +typedef int (*spd_say_func)(SPDConnection* connection, |
| + SPDPriority priority, |
| + const char* text); |
| +typedef int (*spd_stop_func)(SPDConnection* connection); |
| +typedef void (*spd_close_func)(SPDConnection* connection); |
| +typedef int (*spd_set_notification_on_func)(SPDConnection* connection, |
| + SPDNotification notification); |
| +typedef int (*spd_set_voice_rate_func)(SPDConnection* connection, int rate); |
| +typedef int (*spd_set_voice_pitch_func)(SPDConnection* connection, int pitch); |
| }; |
| -class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| +class SpeechDispatcherWrapper { |
| public: |
| - virtual bool PlatformImplAvailable() { |
| - return false; |
| + static SPDNotificationType current_notification_; |
| + |
| + SpeechDispatcherWrapper(); |
| + ~SpeechDispatcherWrapper(); |
| + |
| + bool Speak(const char* text); |
| + bool IsSpeaking(); |
| + bool StopSpeaking(); |
| + void SetRate(int rate); |
| + void SetPitch(int pitch); |
| + |
| + // Resets the connection with speech dispatcher. |
| + void Reset(); |
| + |
| + // States whether Speech Dispatcher loaded successfully. |
| + bool loaded() { |
| + return loaded_; |
| } |
| + private: |
| + static void NotificationCallback(size_t msg_id, |
| + size_t client_id, |
| + SPDNotificationType type); |
| + |
| + static void IndexMarkCallback(size_t msg_id, |
| + size_t client_id, |
| + SPDNotificationType state, |
| + char *index_mark); |
| + |
| + // Interface bindings. |
| + spd_open_func spd_open; |
| + spd_say_func spd_say; |
| + spd_stop_func spd_stop; |
| + spd_close_func spd_close; |
| + spd_set_notification_on_func spd_set_notification_on; |
| + spd_set_voice_rate_func spd_set_voice_rate; |
| + spd_set_voice_pitch_func spd_set_voice_pitch; |
| + |
| + bool loaded_; |
| + void* library_; |
| + SPDConnection* conn_; |
| + DISALLOW_COPY_AND_ASSIGN(SpeechDispatcherWrapper); |
| +}; |
| + |
| +// static |
| +SPDNotificationType SpeechDispatcherWrapper::current_notification_ = |
| + SPD_EVENT_END; |
| + |
| +class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| + public: |
| + virtual bool PlatformImplAvailable(); |
| virtual bool Speak( |
| int utterance_id, |
| const std::string& utterance, |
| const std::string& lang, |
| - const UtteranceContinuousParameters& params) { |
| - error_ = kNotSupportedError; |
| - return false; |
| - } |
| - |
| - virtual bool StopSpeaking() { |
| - error_ = kNotSupportedError; |
| - return false; |
| - } |
| - |
| - virtual bool IsSpeaking() { |
| - error_ = kNotSupportedError; |
| - return false; |
| - } |
| - |
| - virtual bool SendsEvent(TtsEventType event_type) { |
| - return false; |
| - } |
| + const UtteranceContinuousParameters& params); |
| + virtual bool StopSpeaking(); |
| + virtual bool IsSpeaking(); |
| + virtual bool SendsEvent(TtsEventType event_type); |
| + void OnSpeechEvent(SPDNotificationType type); |
| // Get the single instance of this class. |
| - static ExtensionTtsPlatformImplLinux* GetInstance() { |
| - return Singleton<ExtensionTtsPlatformImplLinux>::get(); |
| - } |
| + static ExtensionTtsPlatformImplLinux* GetInstance(); |
| private: |
| - ExtensionTtsPlatformImplLinux() {} |
| + ExtensionTtsPlatformImplLinux(): utterance_id_(0) {} |
| virtual ~ExtensionTtsPlatformImplLinux() {} |
| + SpeechDispatcherWrapper spd_; |
| + |
| + // These apply to the current utterance only. |
| + std::string utterance_; |
| + int utterance_id_; |
| + |
| friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplLinux>; |
| DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplLinux); |
| @@ -57,3 +183,219 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() { |
| return ExtensionTtsPlatformImplLinux::GetInstance(); |
| } |
| + |
| +// static |
| +void SpeechDispatcherWrapper::NotificationCallback( |
| + size_t msg_id, size_t client_id, SPDNotificationType type) { |
| + // We run Speech Dispatcher in threaded mode, so these callbacks should always |
| + // be in a separate thread. |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| + SpeechDispatcherWrapper::current_notification_ = type; |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent, |
| + base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()), |
| + type)); |
| + } |
| +} |
| + |
| +// static |
| +void SpeechDispatcherWrapper::IndexMarkCallback(size_t msg_id, |
| + size_t client_id, |
| + SPDNotificationType state, |
| + char *index_mark) { |
| + // TODO(dtseng): index_mark appears to specify an index type supplied by a |
| + // client. Need to explore how this is used before hooking it up with existing |
| + // word, sentence events. |
| + // We run Speech Dispatcher in threaded mode, so these callbacks should always |
| + // be in a separate thread. |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| + SpeechDispatcherWrapper::current_notification_ = state; |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent, |
| + base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()), |
| + state)); |
| + } |
| +} |
| + |
| +// static |
| +ExtensionTtsPlatformImplLinux* ExtensionTtsPlatformImplLinux::GetInstance() { |
|
dmazzoni
2012/09/10 21:42:05
Please re-organize the method implementations so t
|
| + return Singleton<ExtensionTtsPlatformImplLinux>::get(); |
| +} |
| + |
| +SpeechDispatcherWrapper::SpeechDispatcherWrapper() : loaded_(false) { |
| + library_ = dlopen("libspeechd.so", RTLD_LAZY); |
| + if (!library_) |
| + return; |
| + |
| + spd_open = reinterpret_cast<spd_open_func>(dlsym(library_, "spd_open")); |
| + if (!spd_open) |
| + return; |
| + |
| + spd_say = reinterpret_cast<spd_say_func>(dlsym(library_, "spd_say")); |
| + if (!spd_say) |
| + return; |
| + |
| + spd_stop = reinterpret_cast<spd_stop_func>(dlsym(library_, "spd_stop")); |
| + if (!spd_stop) |
| + return; |
| + |
| + spd_close = reinterpret_cast<spd_close_func>(dlsym(library_, "spd_close")); |
| + if (!spd_close) |
| + return; |
| + |
| + conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED); |
| + if (!conn_) |
| + return; |
| + |
| + spd_set_notification_on = reinterpret_cast<spd_set_notification_on_func>( |
| + dlsym(library_, "spd_set_notification_on")); |
| + if (!spd_set_notification_on) |
| + return; |
| + |
| + spd_set_voice_rate = reinterpret_cast<spd_set_voice_rate_func>( |
| + dlsym(library_, "spd_set_voice_rate")); |
| + if (!spd_set_voice_rate) |
| + return; |
| + |
| + spd_set_voice_pitch = reinterpret_cast<spd_set_voice_pitch_func>( |
| + dlsym(library_, "spd_set_voice_pitch")); |
| + if (!spd_set_voice_pitch) |
| + return; |
| + |
| + // Register callbacks for all events. |
| + conn_->callback_begin = |
| + conn_->callback_end = |
| + conn_->callback_cancel = |
| + conn_->callback_pause = |
| + conn_->callback_resume = |
| + &SpeechDispatcherWrapper::NotificationCallback; |
| + |
| + conn_->callback_im = &SpeechDispatcherWrapper::IndexMarkCallback; |
| + |
| + spd_set_notification_on(conn_, SPD_BEGIN); |
| + spd_set_notification_on(conn_, SPD_END); |
| + spd_set_notification_on(conn_, SPD_CANCEL); |
| + spd_set_notification_on(conn_, SPD_PAUSE); |
| + spd_set_notification_on(conn_, SPD_RESUME); |
| + |
| + loaded_ = true; |
| +} |
| + |
| +SpeechDispatcherWrapper::~SpeechDispatcherWrapper() { |
| + if (conn_) { |
| + spd_close(conn_); |
| + conn_ = NULL; |
| + } |
| + |
| + if (library_) { |
| + dlclose(library_); |
| + library_ = NULL; |
| + } |
| +} |
| +bool SpeechDispatcherWrapper::Speak(const char* text) { |
| + if (!loaded()) |
|
dmazzoni
2012/09/10 21:42:05
Indentation
|
| + return false; |
| + if (spd_say(conn_, SPD_TEXT, text) == -1) { |
| + Reset(); |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| +bool SpeechDispatcherWrapper::IsSpeaking() { |
| + return SpeechDispatcherWrapper::current_notification_ == SPD_EVENT_BEGIN; |
| +} |
| + |
| +bool SpeechDispatcherWrapper::StopSpeaking() { |
| + if (!loaded()) |
| + return false; |
| + if (spd_stop(conn_) == -1) { |
| + Reset(); |
| + return false; |
| + } |
| + return true; |
| +} |
| +void SpeechDispatcherWrapper::SetRate(int rate) { |
| + spd_set_voice_rate(conn_, rate); |
| +} |
| + |
| +void SpeechDispatcherWrapper::SetPitch(int pitch) { |
| + spd_set_voice_pitch(conn_, pitch); |
| +} |
| + |
| +// Resets the connection with speech dispatcher. |
| +void SpeechDispatcherWrapper::Reset() { |
| + if (conn_) |
| + spd_close(conn_); |
| + conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED); |
| +} |
| + |
| +bool ExtensionTtsPlatformImplLinux::PlatformImplAvailable() { |
| + return spd_.loaded(); |
| +} |
| + |
| +bool ExtensionTtsPlatformImplLinux::Speak( |
| + int utterance_id, |
| + const std::string& utterance, |
| + const std::string& lang, |
| + const UtteranceContinuousParameters& params) { |
| + if (!spd_.loaded()) { |
| + error_ = kNotSupportedError; |
| + return false; |
| + } |
| + |
| + // Speech dispatcher's speech params are around 3x at either limit. |
| + float rate = params.rate > 3 ? 3 : params.rate; |
| + rate = params.rate < 0.334 ? 0.334 : rate; |
| + float pitch = params.pitch > 3 ? 3 : params.pitch; |
| + pitch = params.pitch < 0.334 ? 0.334 : pitch; |
| + |
| + // Map our multiplicative range to Speech Dispatcher's linear range. |
| + // .334 = -100. |
| + // 3 = 100. |
| + spd_.SetRate(100 * log10(rate) / log10(3)); |
| + spd_.SetPitch(100 * log10(pitch) / log10(3)); |
| + |
| + utterance_ = utterance; |
| + utterance_id_ = utterance_id; |
| + |
| + spd_.Speak(utterance.c_str()); |
| + return true; |
| +} |
| + |
| +bool ExtensionTtsPlatformImplLinux::StopSpeaking() { |
| + return spd_.StopSpeaking(); |
| +} |
| + |
| +bool ExtensionTtsPlatformImplLinux::IsSpeaking() { |
| + return spd_.IsSpeaking(); |
| +} |
| + |
| +bool ExtensionTtsPlatformImplLinux::SendsEvent(TtsEventType event_type) { |
| + return (event_type == TTS_EVENT_START || |
| + event_type == TTS_EVENT_END || |
| + event_type == TTS_EVENT_CANCELLED || |
| + event_type == TTS_EVENT_MARKER); |
| +} |
| + |
| +void ExtensionTtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) { |
| + ExtensionTtsController* controller = ExtensionTtsController::GetInstance(); |
| + switch (type) { |
| + case SPD_EVENT_BEGIN: |
| + case SPD_EVENT_RESUME: |
| + controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string()); |
| + break; |
| + case SPD_EVENT_END: |
| + case SPD_EVENT_PAUSE: |
| + controller->OnTtsEvent( |
| + utterance_id_, TTS_EVENT_END, utterance_.size(), std::string()); |
| + break; |
| + case SPD_EVENT_CANCEL: |
| + controller->OnTtsEvent( |
| + utterance_id_, TTS_EVENT_CANCELLED, 0, std::string()); |
| + break; |
| + case SPD_EVENT_INDEX_MARK: |
| + controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string()); |
| + break; |
| + } |
| +} |