| 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 698c03e540a35ca7f5d057f6dc1ad484b532476b..9d1181ef43d9108258f8500d208763d7e0898bc2 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();
|
|
|