| Index: chrome/browser/speech/extension_api/tts_extension_api_linux.cc
|
| ===================================================================
|
| --- chrome/browser/speech/extension_api/tts_extension_api_linux.cc (revision 173560)
|
| +++ chrome/browser/speech/extension_api/tts_extension_api_linux.cc (working copy)
|
| @@ -2,23 +2,150 @@
|
| // 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.";
|
|
|
| -} // namespace
|
| +// 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;
|
| +
|
| class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl {
|
| public:
|
| virtual bool PlatformImplAvailable();
|
| @@ -36,26 +163,11 @@
|
| static ExtensionTtsPlatformImplLinux* GetInstance();
|
|
|
| private:
|
| - ExtensionTtsPlatformImplLinux();
|
| - virtual ~ExtensionTtsPlatformImplLinux();
|
| + ExtensionTtsPlatformImplLinux(): utterance_id_(0) {}
|
| + virtual ~ExtensionTtsPlatformImplLinux() {}
|
|
|
| - // Resets the connection with speech dispatcher.
|
| - void Reset();
|
| + SpeechDispatcherWrapper spd_;
|
|
|
| - 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);
|
| -
|
| - static SPDNotificationType current_notification_;
|
| -
|
| - LibSpeechdLoader libspeechd_loader_;
|
| - SPDConnection* conn_;
|
| -
|
| // These apply to the current utterance only.
|
| std::string utterance_;
|
| int utterance_id_;
|
| @@ -65,61 +177,157 @@
|
| DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplLinux);
|
| };
|
|
|
| -// static
|
| -SPDNotificationType ExtensionTtsPlatformImplLinux::current_notification_ =
|
| - SPD_EVENT_END;
|
| +SpeechDispatcherWrapper::SpeechDispatcherWrapper() : loaded_(false) {
|
| + library_ = dlopen("libspeechd.so", RTLD_LAZY);
|
| + if (!library_)
|
| + return;
|
|
|
| -ExtensionTtsPlatformImplLinux::ExtensionTtsPlatformImplLinux()
|
| - : utterance_id_(0) {
|
| - if (!libspeechd_loader_.Load("libspeechd.so.2"))
|
| + spd_open = reinterpret_cast<spd_open_func>(dlsym(library_, "spd_open"));
|
| + if (!spd_open)
|
| return;
|
|
|
| - conn_ = libspeechd_loader_.spd_open(
|
| - "chrome", "extension_api", NULL, SPD_MODE_THREADED);
|
| + 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 =
|
| - &NotificationCallback;
|
| + &SpeechDispatcherWrapper::NotificationCallback;
|
|
|
| - conn_->callback_im = &IndexMarkCallback;
|
| + conn_->callback_im = &SpeechDispatcherWrapper::IndexMarkCallback;
|
|
|
| - 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);
|
| + 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;
|
| }
|
|
|
| -ExtensionTtsPlatformImplLinux::~ExtensionTtsPlatformImplLinux() {
|
| +SpeechDispatcherWrapper::~SpeechDispatcherWrapper() {
|
| if (conn_) {
|
| - libspeechd_loader_.spd_close(conn_);
|
| + 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;
|
| +}
|
|
|
| -void ExtensionTtsPlatformImplLinux::Reset() {
|
| +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_)
|
| - libspeechd_loader_.spd_close(conn_);
|
| - conn_ = libspeechd_loader_.spd_open(
|
| - "chrome", "extension_api", NULL, SPD_MODE_THREADED);
|
| + 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));
|
| + }
|
| +}
|
| +
|
| bool ExtensionTtsPlatformImplLinux::PlatformImplAvailable() {
|
| - return libspeechd_loader_.loaded() && (conn_ != NULL);
|
| + return spd_.loaded();
|
| }
|
|
|
| bool ExtensionTtsPlatformImplLinux::Speak(
|
| - int utterance_id,
|
| - const std::string& utterance,
|
| - const std::string& lang,
|
| - const UtteranceContinuousParameters& params) {
|
| - if (!PlatformImplAvailable()) {
|
| + int utterance_id,
|
| + const std::string& utterance,
|
| + const std::string& lang,
|
| + const UtteranceContinuousParameters& params) {
|
| + if (!spd_.loaded()) {
|
| error_ = kNotSupportedError;
|
| return false;
|
| }
|
| @@ -133,31 +341,22 @@
|
| // Map our multiplicative range to Speech Dispatcher's linear range.
|
| // .334 = -100.
|
| // 3 = 100.
|
| - libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
|
| - libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
|
| + spd_.SetRate(100 * log10(rate) / log10(3));
|
| + spd_.SetPitch(100 * log10(pitch) / log10(3));
|
|
|
| utterance_ = utterance;
|
| utterance_id_ = utterance_id;
|
|
|
| - if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
|
| - Reset();
|
| - return false;
|
| - }
|
| + spd_.Speak(utterance.c_str());
|
| return true;
|
| }
|
|
|
| bool ExtensionTtsPlatformImplLinux::StopSpeaking() {
|
| - if (!PlatformImplAvailable())
|
| - return false;
|
| - if (libspeechd_loader_.spd_stop(conn_) == -1) {
|
| - Reset();
|
| - return false;
|
| - }
|
| - return true;
|
| + return spd_.StopSpeaking();
|
| }
|
|
|
| bool ExtensionTtsPlatformImplLinux::IsSpeaking() {
|
| - return current_notification_ == SPD_EVENT_BEGIN;
|
| + return spd_.IsSpeaking();
|
| }
|
|
|
| bool ExtensionTtsPlatformImplLinux::SendsEvent(TtsEventType event_type) {
|
| @@ -190,39 +389,6 @@
|
| }
|
|
|
| // 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();
|
|
|