Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2098)

Unified Diff: chrome/browser/speech/extension_api/tts_extension_api_linux.cc

Issue 10915164: Initial support for native TTS on linux through tts extension API. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Export all libspeechd.h structures and remove header include. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..6deb0ea5c9dc8ad9c2e5c33d1c49a50e2cdca563 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_linux.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_linux.cc
@@ -2,52 +2,178 @@
// 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"
+
+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{
dmazzoni 2012/09/10 21:42:05 Fix the spacing and indentation to match Chromium
+ 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 ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl {
+class SpeechDispatcherWrapper {
public:
- virtual bool PlatformImplAvailable() {
- return false;
+ 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();
virtual bool Speak(
int utterance_id,
const std::string& utterance,
const std::string& lang,
- const UtteranceContinuousParameters& params) {
- error_ = kNotSupportedError;
- return false;
- }
-
- virtual bool StopSpeaking() {
- error_ = kNotSupportedError;
- return false;
- }
-
- virtual bool IsSpeaking() {
- error_ = kNotSupportedError;
- return false;
- }
-
- virtual bool SendsEvent(TtsEventType event_type) {
- return false;
- }
+ const UtteranceContinuousParameters& params);
+ virtual bool StopSpeaking();
+ virtual bool IsSpeaking();
+ virtual bool SendsEvent(TtsEventType event_type);
+ void OnSpeechEvent(SPDNotificationType type);
// Get the single instance of this class.
- static ExtensionTtsPlatformImplLinux* GetInstance() {
- return Singleton<ExtensionTtsPlatformImplLinux>::get();
- }
+ static ExtensionTtsPlatformImplLinux* GetInstance();
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 +183,219 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl {
ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() {
return ExtensionTtsPlatformImplLinux::GetInstance();
}
+
+// 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));
+ }
+}
+
+// static
+ExtensionTtsPlatformImplLinux* ExtensionTtsPlatformImplLinux::GetInstance() {
dmazzoni 2012/09/10 21:42:05 Please re-organize the method implementations so t
+ return Singleton<ExtensionTtsPlatformImplLinux>::get();
+}
+
+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;
+
+ 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;
+
+ conn_->callback_im = &SpeechDispatcherWrapper::IndexMarkCallback;
+
+ 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::~SpeechDispatcherWrapper() {
+ if (conn_) {
+ spd_close(conn_);
+ conn_ = NULL;
+ }
+
+ if (library_) {
+ dlclose(library_);
+ library_ = NULL;
+ }
+}
+bool SpeechDispatcherWrapper::Speak(const char* text) {
+ if (!loaded())
dmazzoni 2012/09/10 21:42:05 Indentation
+ 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() {
+ if (conn_)
+ spd_close(conn_);
+ conn_ = spd_open("chrome", "extension_api", NULL, SPD_MODE_THREADED);
+}
+
+bool ExtensionTtsPlatformImplLinux::PlatformImplAvailable() {
+ return spd_.loaded();
+}
+
+bool ExtensionTtsPlatformImplLinux::Speak(
+ int utterance_id,
+ const std::string& utterance,
+ const std::string& lang,
+ const UtteranceContinuousParameters& params) {
+ if (!spd_.loaded()) {
+ error_ = kNotSupportedError;
+ return false;
+ }
+
+ // Speech dispatcher's speech params are around 3x at either limit.
+ float rate = params.rate > 3 ? 3 : params.rate;
+ rate = params.rate < 0.334 ? 0.334 : rate;
+ float pitch = params.pitch > 3 ? 3 : params.pitch;
+ pitch = params.pitch < 0.334 ? 0.334 : pitch;
+
+ // 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));
+
+ utterance_ = utterance;
+ utterance_id_ = utterance_id;
+
+ spd_.Speak(utterance.c_str());
+ return true;
+}
+
+bool ExtensionTtsPlatformImplLinux::StopSpeaking() {
+ return spd_.StopSpeaking();
+}
+
+bool ExtensionTtsPlatformImplLinux::IsSpeaking() {
+ return spd_.IsSpeaking();
+}
+
+bool ExtensionTtsPlatformImplLinux::SendsEvent(TtsEventType event_type) {
+ return (event_type == TTS_EVENT_START ||
+ event_type == TTS_EVENT_END ||
+ event_type == TTS_EVENT_CANCELLED ||
+ event_type == TTS_EVENT_MARKER);
+}
+
+void ExtensionTtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
+ ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
+ switch (type) {
+ case SPD_EVENT_BEGIN:
+ case SPD_EVENT_RESUME:
+ controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
+ break;
+ case SPD_EVENT_END:
+ case SPD_EVENT_PAUSE:
+ controller->OnTtsEvent(
+ utterance_id_, TTS_EVENT_END, utterance_.size(), std::string());
+ break;
+ case SPD_EVENT_CANCEL:
+ controller->OnTtsEvent(
+ utterance_id_, TTS_EVENT_CANCELLED, 0, std::string());
+ break;
+ case SPD_EVENT_INDEX_MARK:
+ controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
+ break;
+ }
+}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698