diff options
-rw-r--r-- | build/linux/system.gyp | 57 | ||||
-rw-r--r-- | chrome/browser/speech/extension_api/tts_extension_api_linux.cc | 358 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 1 | ||||
-rwxr-xr-x | tools/generate_library_loader/generate_library_loader.py | 2 |
4 files changed, 156 insertions, 262 deletions
diff --git a/build/linux/system.gyp b/build/linux/system.gyp index 6c4488a..1bb764f 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -13,6 +13,7 @@ ], 'linux_link_libpci%': 0, + 'linux_link_libspeechd%': 0, }, 'conditions': [ [ 'os_posix==1 and OS!="mac"', { @@ -383,6 +384,62 @@ ], }, { + 'target_name': 'libspeechd', + 'type': 'static_library', + 'dependencies': [ + '../../base/base.gyp:base', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'conditions': [ + ['linux_link_libspeechd==1', { + 'link_settings': { + 'libraries': [ + '-lspeechd', + ], + } + }], + ], + }, + 'hard_dependency': 1, + 'actions': [ + { + 'variables': { + 'output_h': '<(SHARED_INTERMEDIATE_DIR)/library_loaders/libspeechd.h', + 'output_cc': '<(INTERMEDIATE_DIR)/libspeechd_loader.cc', + 'generator': '../../tools/generate_library_loader/generate_library_loader.py', + }, + 'action_name': 'generate_libspeechd_loader', + 'inputs': [ + '<(generator)', + ], + 'outputs': [ + '<(output_h)', + '<(output_cc)', + ], + 'action': ['python', + '<(generator)', + '--name', 'LibSpeechdLoader', + '--output-h', '<(output_h)', + '--output-cc', '<(output_cc)', + '--header', '<libspeechd.h>', + '--link-directly=<(linux_link_libspeechd)', + 'spd_open', + 'spd_say', + 'spd_stop', + 'spd_close', + 'spd_set_notification_on', + 'spd_set_voice_rate', + 'spd_set_voice_pitch', + ], + 'message': 'Generating libspeechd library loader.', + 'process_outputs_as_sources': 1, + }, + ], + }, + { 'target_name': 'x11', 'type': 'none', 'toolsets': ['host', 'target'], 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 698c03e..9d1181e 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api_linux.cc +++ b/chrome/browser/speech/extension_api/tts_extension_api_linux.cc @@ -2,149 +2,22 @@ // 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" +#include "library_loaders/libspeechd.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 { - 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 SpeechDispatcherWrapper { - public: - 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; +} // namespace class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { public: @@ -163,10 +36,25 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { static ExtensionTtsPlatformImplLinux* GetInstance(); private: - ExtensionTtsPlatformImplLinux(): utterance_id_(0) {} - virtual ~ExtensionTtsPlatformImplLinux() {} + ExtensionTtsPlatformImplLinux(); + virtual ~ExtensionTtsPlatformImplLinux(); + + // Resets the connection with speech dispatcher. + void Reset(); + + static void NotificationCallback(size_t msg_id, + size_t client_id, + SPDNotificationType type); - SpeechDispatcherWrapper spd_; + static void IndexMarkCallback(size_t msg_id, + size_t client_id, + SPDNotificationType state, + char* index_mark); + + static SPDNotificationType current_notification_; + + LibSpeechdLoader libspeechd_loader_; + SPDConnection* conn_; // These apply to the current utterance only. std::string utterance_; @@ -177,157 +65,61 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplLinux); }; -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; +// static +SPDNotificationType ExtensionTtsPlatformImplLinux::current_notification_ = + SPD_EVENT_END; - spd_close = reinterpret_cast<spd_close_func>(dlsym(library_, "spd_close")); - if (!spd_close) +ExtensionTtsPlatformImplLinux::ExtensionTtsPlatformImplLinux() + : utterance_id_(0) { + if (!libspeechd_loader_.Load("libspeechd.so.2")) return; - conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED); + conn_ = libspeechd_loader_.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; + &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); + conn_->callback_im = &IndexMarkCallback; - loaded_ = true; + libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN); + libspeechd_loader_.spd_set_notification_on(conn_, SPD_END); + libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL); + libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE); + libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME); } -SpeechDispatcherWrapper::~SpeechDispatcherWrapper() { +ExtensionTtsPlatformImplLinux::~ExtensionTtsPlatformImplLinux() { if (conn_) { - spd_close(conn_); + libspeechd_loader_.spd_close(conn_); conn_ = NULL; } - - if (library_) { - dlclose(library_); - library_ = NULL; - } -} -bool SpeechDispatcherWrapper::Speak(const char* text) { - if (!loaded()) - 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() { +void ExtensionTtsPlatformImplLinux::Reset() { if (conn_) - spd_close(conn_); - conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED); -} - -// 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)); - } + libspeechd_loader_.spd_close(conn_); + conn_ = libspeechd_loader_.spd_open( + "chrome", "extension_api", NULL, SPD_MODE_THREADED); } bool ExtensionTtsPlatformImplLinux::PlatformImplAvailable() { - return spd_.loaded(); + return libspeechd_loader_.loaded() && (conn_ != NULL); } bool ExtensionTtsPlatformImplLinux::Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const UtteranceContinuousParameters& params) { - if (!spd_.loaded()) { + int utterance_id, + const std::string& utterance, + const std::string& lang, + const UtteranceContinuousParameters& params) { + if (!PlatformImplAvailable()) { error_ = kNotSupportedError; return false; } @@ -341,22 +133,31 @@ bool ExtensionTtsPlatformImplLinux::Speak( // 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)); + libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3)); + libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3)); utterance_ = utterance; utterance_id_ = utterance_id; - spd_.Speak(utterance.c_str()); + if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) { + Reset(); + return false; + } return true; } bool ExtensionTtsPlatformImplLinux::StopSpeaking() { - return spd_.StopSpeaking(); + if (!PlatformImplAvailable()) + return false; + if (libspeechd_loader_.spd_stop(conn_) == -1) { + Reset(); + return false; + } + return true; } bool ExtensionTtsPlatformImplLinux::IsSpeaking() { - return spd_.IsSpeaking(); + return current_notification_ == SPD_EVENT_BEGIN; } bool ExtensionTtsPlatformImplLinux::SendsEvent(TtsEventType event_type) { @@ -389,6 +190,39 @@ void ExtensionTtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) { } // static +void ExtensionTtsPlatformImplLinux::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)) { + current_notification_ = type; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent, + base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()), + type)); + } +} + +// static +void ExtensionTtsPlatformImplLinux::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)) { + current_notification_ = state; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ExtensionTtsPlatformImplLinux::OnSpeechEvent, + base::Unretained(ExtensionTtsPlatformImplLinux::GetInstance()), + state)); + } +} + +// static ExtensionTtsPlatformImplLinux* ExtensionTtsPlatformImplLinux::GetInstance() { return Singleton<ExtensionTtsPlatformImplLinux, LeakySingletonTraits<ExtensionTtsPlatformImplLinux> >::get(); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index f8f61cd..7d8a702 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2428,6 +2428,7 @@ 'dependencies': [ 'mtp_file_entry_proto', 'mtp_storage_info_proto', + '../build/linux/system.gyp:libspeechd', '../build/linux/system.gyp:udev', ], 'sources': [ diff --git a/tools/generate_library_loader/generate_library_loader.py b/tools/generate_library_loader/generate_library_loader.py index 0f5e051..5629562 100755 --- a/tools/generate_library_loader/generate_library_loader.py +++ b/tools/generate_library_loader/generate_library_loader.py @@ -44,6 +44,8 @@ class %(class_name)s { bool Load(const std::string& library_name) WARN_UNUSED_RESULT; + bool loaded() const { return loaded_; } + %(member_decls)s private: |