| 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();
 | 
| 
 |