blob: 9ff7197dca68977101072697e4704926e62e5d2c [file] [log] [blame]
// 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 <iostream>
#include <base/basictypes.h>
#include <base/command_line.h>
#include <chromeos/dbus/dbus.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/glib/object.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "base/logging.h"
#include "interface.h"
// Forcibly namespace the dbus-bindings generated server bindings instead of
// modifying the files afterward.
namespace speech_synthesis { // NOLINT
namespace gobject { // NOLINT
#include "bindings/server.h"
} // namespace gobject
} // namespace speech_Synthesis
namespace speech_synthesis {
SpeechSynthesizerService::SpeechSynthesizerService()
: main_loop_(g_main_loop_new(NULL, FALSE)) {
}
SpeechSynthesizerService::~SpeechSynthesizerService() {
g_main_loop_unref(main_loop_);
}
bool SpeechSynthesizerService::Initialize() {
// Install the type-info for the service with dbus.
dbus_g_object_type_install_info(
gobject::speech_synthesizer_get_type(),
&gobject::dbus_glib_speech_synthesizer_object_info);
return Reset();
}
bool SpeechSynthesizerService::Reset() {
speech_synthesizer_.reset(NULL); // Make sure the destructor is run first.
speech_synthesizer_.reset(
reinterpret_cast<gobject::SpeechSynthesizer*>(
g_object_new(gobject::speech_synthesizer_get_type(), NULL)));
// Allow references to this instance.
speech_synthesizer_->service = this;
if (main_loop_) {
::g_main_loop_unref(main_loop_);
}
main_loop_ = g_main_loop_new(NULL, false);
if (!main_loop_) {
LOG(ERROR) << "Failed to create main loop";
return false;
}
return true;
}
// A utility function to send a signal to the browser.
void SendSignal(const char* signal_name) {
chromeos::dbus::Proxy proxy(chromeos::dbus::GetSystemBusConnection(),
"/",
chromium::kChromiumInterface);
DBusMessage* signal = ::dbus_message_new_signal(
"/",
chromium::kChromiumInterface,
signal_name);
DCHECK(signal);
::dbus_g_proxy_send(proxy.gproxy(), signal, NULL);
::dbus_message_unref(signal);
}
bool SpeechSynthesizerService::Run() {
if (!main_loop_) {
LOG(ERROR) << "You must have a main loop to call Run.";
return false;
}
LOG(INFO) << "Running speech_synthesizer main_loop.";
Threading threading;
audio_output = AudioOutput::Create(&threading);
engine = new PicoTtsEngine("/usr/share/tts/pico/");
ttsService = new TtsService(engine, audio_output, &threading);
utterance_options = new UtteranceOptions();
utterance_options->voice_options = new TtsVoice();
bool started = false;
// Attempt to start TTS service multiple times. This is required
// since pulseaudio might not be ready when the speech synthesis
// service starts. We basically bet that pulseaudio will be up and ready
// in 10 seconds, otherwise we fail.
// TODO(chaitanyag): Change if we find a better way to wait for
// pulseaudio to be ready outside of upstart.
for (int i = 0; i < 100; i++) {
if (ttsService->StartService()) {
started = true;
break;
} else {
LOG(ERROR) << "Failed to start speech_synthesizer. Attempt: " << i;
usleep(100000);
}
}
if (started) {
SendSignal(chromium::kTTSReadySignal);
g_main_loop_run(main_loop_);
} else {
SendSignal(chromium::kTTSFailedSignal);
}
return false;
}
gboolean SpeechSynthesizerService::AddProperties(gchar *properties) {
if (properties == NULL) {
return false;
}
string props(properties);
if (props.compare("") == 0) {
return true;
}
string token = "";
while(1) {
unsigned int sep = props.find(";");
if (sep == string::npos) {
token = props;
} else {
token = props.substr(0, sep);
props = props.substr(sep + 1);
}
if (token.empty() || token.compare("") == 0) {
break;
}
int eq = token.find("=");
string key = token.substr(0, eq);
string value = token.substr(eq + 1);
if (key.compare("rate") == 0) {
utterance_options->rate = atof(value.c_str());
} else if (key.compare("pitch") == 0) {
utterance_options->pitch = atof(value.c_str());
} else if (key.compare("volume") == 0) {
utterance_options->volume = atof(value.c_str());
} else if (key.compare("name") == 0) {
utterance_options->voice_options->name = value;
} else if (key.compare("language") == 0) {
utterance_options->voice_options->language = value;
} else if (key.compare("sample_rate") == 0) {
utterance_options->voice_options->sample_rate = atoi(value.c_str());
} else if (key.compare("quality") == 0) {
utterance_options->voice_options->quality =
(tts_quality) atoi(value.c_str());
} else if (key.compare("region") == 0) {
utterance_options->voice_options->region = value;
} else if (key.compare("gender") == 0) {
utterance_options->voice_options->gender =
(tts_gender) atoi(value.c_str());
} else if (key.compare("age") == 0) {
utterance_options->voice_options->age = atoi(value.c_str());
} else if (key.compare("enqueue") == 0) {
utterance_options->enqueue = atoi(value.c_str()) == 1;
} else if (key.compare("interruptible") == 0) {
utterance_options->interruptible = atoi(value.c_str()) == 1;
}
if (sep == string::npos) {
break;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// SpeechSynthesizerService commands
gboolean SpeechSynthesizerService::Speak(gchar *text,
GError **error) {
UtteranceOptions *options = new UtteranceOptions(*utterance_options);
ttsService->Speak(text, options);
utterance_options->enqueue = false;
utterance_options->interruptible = true;
return true;
}
gboolean SpeechSynthesizerService::SetProperties(gchar *properties,
GError **error) {
return AddProperties(properties);
}
gboolean SpeechSynthesizerService::Stop(GError **error) {
ttsService->Stop();
return true;
}
gboolean SpeechSynthesizerService::IsSpeaking(
gboolean* OUT_isSpeaking_requested,
GError **error) {
*OUT_isSpeaking_requested = (ttsService->GetStatus() == TTS_BUSY);
return true;
}
} // namespace speech_synthesis