| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "chromeos_speech_synthesis.h" // NOLINT |
| |
| #include <base/string_util.h> |
| #include <chromeos/dbus/dbus.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <chromeos/glib/object.h> |
| #include <dbus/dbus-glib-lowlevel.h> |
| |
| #include "marshal.glibmarshal.h" // NOLINT |
| |
| namespace chromeos { |
| |
| const int kDBusTtsTimeoutMs = 50; |
| |
| class OpaqueTTSInitConnection { |
| public: |
| explicit OpaqueTTSInitConnection(const InitStatusCallback& callback) |
| : init_callback_(callback) { |
| } |
| |
| virtual ~OpaqueTTSInitConnection() {} |
| |
| void NotifySuccess(bool success) { |
| if (init_callback_ != NULL) { |
| init_callback_(success); |
| } |
| } |
| |
| private: |
| InitStatusCallback init_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(OpaqueTTSInitConnection); |
| }; |
| |
| // A message filter to receive signals. |
| DBusHandlerResult Filter(DBusConnection* connection, |
| DBusMessage* message, |
| void* object) { |
| TTSInitConnection self = static_cast<TTSInitConnection>(object); |
| if (dbus_message_is_signal(message, chromium::kChromiumInterface, |
| chromium::kTTSReadySignal)) { |
| self->NotifySuccess(true); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } else if (dbus_message_is_signal(message, chromium::kChromiumInterface, |
| chromium::kTTSFailedSignal)) { |
| self->NotifySuccess(false); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } else { |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| } |
| |
| // Starts the speech synthesizer service registered under the name |
| // "org.chromium.SpeechSynthesizer", by sending a message over DBus. |
| bool StartTtsService() { |
| bool ret = true; |
| DBusMessage *message; |
| dbus_uint32_t flag; |
| dbus_bool_t result; |
| DBusError error; |
| dbus_error_init (&error); |
| DBusConnection* connection = dbus_g_connection_get_connection( |
| dbus::GetSystemBusConnection().g_connection()); |
| if (dbus_error_is_set(&error)) { |
| DLOG(ERROR) << "Error getting dbus connection: " << error.message; |
| return false; |
| } |
| message = dbus_message_new_method_call("org.freedesktop.DBus", |
| "/org/freedesktop/DBus", |
| "org.freedesktop.DBus", |
| "StartServiceByName"); |
| if (!message) { |
| DLOG(ERROR) << "Error creating DBus message."; |
| return false; |
| } |
| dbus_message_set_no_reply(message, TRUE); |
| dbus_message_append_args(message, DBUS_TYPE_STRING, |
| &speech_synthesis::kSpeechSynthesizerServiceName, |
| DBUS_TYPE_UINT32, &flag, DBUS_TYPE_INVALID); |
| // TODO(chaitanyag): Ideally, we want to receive a reply from DBus indicating |
| // whether it was able to start the service. |result| here just indicates |
| // whether the message was sent successfully. |
| result = dbus_connection_send(connection, message, NULL); |
| if (result == TRUE) { |
| DLOG(INFO) << "Successfully activating service " << |
| speech_synthesis::kSpeechSynthesizerServiceName; |
| } else { |
| DLOG(ERROR) << "Failed to activate service" << |
| speech_synthesis::kSpeechSynthesizerServiceName; |
| ret = false; |
| } |
| dbus_message_unref(message); |
| return ret; |
| } |
| |
| extern "C" |
| bool ChromeOSSpeak(const char* text) { |
| g_type_init(); |
| chromeos::dbus::BusConnection bus = chromeos::dbus::GetSystemBusConnection(); |
| chromeos::dbus::Proxy tts_proxy(bus, |
| speech_synthesis::kSpeechSynthesizerServiceName, |
| speech_synthesis::kSpeechSynthesizerServicePath, |
| speech_synthesis::kSpeechSynthesizerInterface); |
| DCHECK(tts_proxy.gproxy()) << "Failed to acquire proxy"; |
| ::dbus_g_proxy_call_no_reply(tts_proxy.gproxy(), |
| "Speak", |
| G_TYPE_STRING, |
| text, |
| G_TYPE_INVALID); |
| return true; |
| } |
| |
| extern "C" |
| bool ChromeOSSetSpeakProperties(const char* props) { |
| g_type_init(); |
| chromeos::dbus::BusConnection bus = chromeos::dbus::GetSystemBusConnection(); |
| chromeos::dbus::Proxy tts_proxy(bus, |
| speech_synthesis::kSpeechSynthesizerServiceName, |
| speech_synthesis::kSpeechSynthesizerServicePath, |
| speech_synthesis::kSpeechSynthesizerInterface); |
| DCHECK(tts_proxy.gproxy()) << "Failed to acquire proxy"; |
| ::dbus_g_proxy_call_no_reply(tts_proxy.gproxy(), |
| "SetProperties", |
| G_TYPE_STRING, |
| props, |
| G_TYPE_INVALID, |
| G_TYPE_INVALID); |
| return true; |
| } |
| |
| extern "C" |
| bool ChromeOSStopSpeaking() { |
| g_type_init(); |
| chromeos::dbus::BusConnection bus = chromeos::dbus::GetSystemBusConnection(); |
| chromeos::dbus::Proxy tts_proxy(bus, |
| speech_synthesis::kSpeechSynthesizerServiceName, |
| speech_synthesis::kSpeechSynthesizerServicePath, |
| speech_synthesis::kSpeechSynthesizerInterface); |
| DCHECK(tts_proxy.gproxy()) << "Failed to acquire proxy"; |
| ::dbus_g_proxy_call_no_reply(tts_proxy.gproxy(), |
| "Stop", |
| G_TYPE_INVALID, |
| G_TYPE_INVALID); |
| return true; |
| } |
| |
| extern "C" |
| bool ChromeOSIsSpeaking() { |
| g_type_init(); |
| chromeos::dbus::BusConnection bus = chromeos::dbus::GetSystemBusConnection(); |
| chromeos::dbus::Proxy tts_proxy(bus, |
| speech_synthesis::kSpeechSynthesizerServiceName, |
| speech_synthesis::kSpeechSynthesizerServicePath, |
| speech_synthesis::kSpeechSynthesizerInterface); |
| DCHECK(tts_proxy.gproxy()) << "Failed to acquire proxy"; |
| gboolean done = false; |
| chromeos::glib::ScopedError error; |
| if (!::dbus_g_proxy_call_with_timeout(tts_proxy.gproxy(), |
| "IsSpeaking", |
| kDBusTtsTimeoutMs, |
| &Resetter(&error).lvalue(), |
| G_TYPE_INVALID, |
| G_TYPE_BOOLEAN, |
| &done, |
| G_TYPE_INVALID)) { |
| LOG(WARNING) << "IsSpeaking" << " failed: " |
| << (error->message ? error->message : "Unknown Error."); |
| return false; |
| } |
| return done; |
| } |
| |
| #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") |
| |
| extern "C" |
| void |
| ChromeOSInitTts(InitStatusCallback callback) { |
| const std::string filter = StringPrintf("type='signal', interface='%s'", |
| chromium::kChromiumInterface); |
| DBusError error; |
| dbus_error_init(&error); |
| DBusConnection* connection = ::dbus_g_connection_get_connection( |
| dbus::GetSystemBusConnection().g_connection()); |
| dbus_bus_add_match(connection, filter.c_str(), &error); |
| if (dbus_error_is_set(&error)) { |
| DLOG(WARNING) << "Failed to add a filter:" << error.name << ", message=" |
| << SAFE_MESSAGE(error); |
| return; |
| } |
| TTSInitConnection result = new OpaqueTTSInitConnection(callback); |
| CHECK(dbus_connection_add_filter(connection, &Filter, result, NULL)); |
| StartTtsService(); |
| } |
| |
| } // namespace chromeos |