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..b592f806a062eeddc79f2c7f27dae167f0894924 100644 |
| --- a/chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| +++ b/chrome/browser/speech/extension_api/tts_extension_api_linux.cc |
| @@ -2,18 +2,164 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <dlfcn.h> |
| +#include <libspeechd.h> |
| +#include <math.h> |
| + |
| #include "base/memory/singleton.h" |
| #include "chrome/browser/speech/extension_api/tts_extension_api_platform.h" |
| namespace { |
| const char kNotSupportedError[] = |
| "Native speech synthesis not supported on this platform."; |
| + |
| +// Speech dispatcher exports. |
| +typedef SPDConnection* (*spd_open_func)(const char* client_name, |
|
dmazzoni
2012/09/09 21:40:18
Extra spaces
|
| + const char* connection_name, |
| + const char* user_name, |
| + SPDConnectionMode mode); |
| +typedef int (*spd_say_func)(SPDConnection* connection, |
| + SPDPriority priority, |
| + const char* text); |
| +typedef void (*spd_stop_func)(SPDConnection* connection); |
| +typedef void (*spd_close_func)(SPDConnection* connection); |
| +typedef int (*spd_set_notification_on_func)(SPDConnection* connection, |
| + SPDNotification notification); |
|
dmazzoni
2012/09/09 21:40:18
Fix indentation
|
| +typedef int (*spd_set_voice_rate_func)(SPDConnection* connection, int rate); |
| +typedef int (*spd_set_voice_pitch_func)(SPDConnection* connection, int pitch); |
| +}; |
| + |
| +class SpeechDispatcherWrapper { |
| + public: |
| + static SPDNotificationType current_notification; |
| + |
| + SpeechDispatcherWrapper() : loaded_(false) { |
|
dmazzoni
2012/09/09 21:40:18
This is long, so move the implementation below rat
|
| + 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; |
| + |
| + 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() { |
| + if (conn_) { |
| + spd_close(conn_); |
| + conn_ = NULL; |
| + } |
| + |
| + if (library_) { |
| + dlclose(library_); |
| + library_ = NULL; |
| + } |
| + } |
| + |
| + bool Speak(const char* text) { |
| + if (!loaded()) |
| + return false; |
| + |
| + spd_say(conn_, SPD_TEXT, text); |
| + return true; |
| + } |
| + |
| + bool IsSpeaking() { |
| + return SpeechDispatcherWrapper::current_notification == SPD_EVENT_BEGIN; |
| + } |
| + |
| + bool StopSpeaking() { |
| + if (!loaded()) |
| + return false; |
| + spd_stop(conn_); |
| + return true; |
| + } |
| + |
| + void SetRate(int rate) { |
| + spd_set_voice_rate(conn_, rate); |
| + } |
| + |
| + void SetPitch(int pitch) { |
| + spd_set_voice_pitch(conn_, pitch); |
| + } |
| + |
| + // States whether Speech Dispatcher loaded successfully. |
| + bool loaded() { |
| + return loaded_; |
| + } |
| + |
| + private: |
| + static void NotificationCallback(size_t msg_id, |
| + size_t client_id, |
| + SPDNotificationType type); |
| + |
| + // 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); |
| }; |
| +SPDNotificationType SpeechDispatcherWrapper::current_notification = |
|
dmazzoni
2012/09/09 21:40:18
Add a newline before this, and add a comment that
|
| + SPD_EVENT_END; |
| class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| public: |
| virtual bool PlatformImplAvailable() { |
| - return false; |
| + return spd_.loaded(); |
| } |
| virtual bool Speak( |
| @@ -21,22 +167,33 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| const std::string& utterance, |
| const std::string& lang, |
| const UtteranceContinuousParameters& params) { |
| - error_ = kNotSupportedError; |
| - return false; |
| + if (!spd_.loaded()) { |
| + error_ = kNotSupportedError; |
| + return false; |
| + } |
| + |
| + if (params.rate >= 0.0 && params.rate <= 10.0) |
| + spd_.SetRate(100 * log10(params.rate)); |
|
dmazzoni
2012/09/09 21:40:18
Documentation for how you came up with this mappin
|
| + if (params.pitch >= 0.0 && params.pitch <= 10.0) |
| + spd_.SetPitch(100 * log10(params.pitch)); |
| + |
| + utterance_ = utterance; |
| + utterance_id_ = utterance_id; |
| + |
| + spd_.Speak(utterance.c_str()); |
| + return true; |
| } |
| virtual bool StopSpeaking() { |
| - error_ = kNotSupportedError; |
| - return false; |
| + return spd_.StopSpeaking(); |
| } |
| virtual bool IsSpeaking() { |
| - error_ = kNotSupportedError; |
| - return false; |
| + return spd_.IsSpeaking(); |
| } |
| virtual bool SendsEvent(TtsEventType event_type) { |
| - return false; |
| + return (event_type == TTS_EVENT_START || event_type == TTS_EVENT_END); |
| } |
| // Get the single instance of this class. |
| @@ -44,10 +201,36 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| return Singleton<ExtensionTtsPlatformImplLinux>::get(); |
| } |
| + void OnSpeechEvent(SPDNotificationType type) { |
| + ExtensionTtsController* controller = ExtensionTtsController::GetInstance(); |
| + switch (type) { |
| + case SPD_EVENT_BEGIN: |
| + controller->OnTtsEvent( |
| + utterance_id_, TTS_EVENT_START, 0, std::string()); |
| + break; |
| + case SPD_EVENT_END: |
| + controller->OnTtsEvent( |
| + utterance_id_, TTS_EVENT_END, utterance_.size(), std::string()); |
| + break; |
| + // TODO(dtseng): Unimplemented. |
|
dmazzoni
2012/09/09 21:40:18
Can you implement these as part of this CL? They s
|
| + case SPD_EVENT_INDEX_MARK: |
| + case SPD_EVENT_CANCEL: |
| + case SPD_EVENT_PAUSE: |
| + case SPD_EVENT_RESUME: |
| + break; |
| + } |
| + } |
| + |
| 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 +240,10 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { |
| ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() { |
| return ExtensionTtsPlatformImplLinux::GetInstance(); |
| } |
| + |
| +// static |
| +void SpeechDispatcherWrapper::NotificationCallback( |
| + size_t msg_id, size_t client_id, SPDNotificationType type) { |
| + SpeechDispatcherWrapper::current_notification = type; |
| + ExtensionTtsPlatformImplLinux::GetInstance()->OnSpeechEvent(type); |
| +} |