blob: e5610245b43c98e5e5d1239a3ea18273974da13c [file] [log] [blame]
// Copyright (c) 2012 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()
: speech_synthesizer_(NULL),
main_loop_(g_main_loop_new(NULL, FALSE)) {
}
SpeechSynthesizerService::~SpeechSynthesizerService() {
if (speech_synthesizer_)
g_object_unref(speech_synthesizer_);
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() {
if (speech_synthesizer_)
g_object_unref(speech_synthesizer_);
speech_synthesizer_ = 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.";
scoped_ptr<AudioOutput> audio_output(AudioOutput::Create());
scoped_ptr<TtsEngine> engine(new PicoTtsEngine("/usr/share/tts/pico/"));
ttsService_.reset(new TtsService(engine.get(), audio_output.get()));
utterance_options_.reset(new UtteranceOptions());
// Polling for idle shutdown.
const int kTimeoutInterval = 10000; // milliseconds
UpdateLastCallTime();
g_timeout_add(kTimeoutInterval, OnTimeout, this);
if (ttsService_->StartService()) {
SendSignal(chromium::kTTSReadySignal);
g_main_loop_run(main_loop_);
} else {
SendSignal(chromium::kTTSFailedSignal);
return false;
}
ttsService_->StopService();
return true;
}
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;
}
void SpeechSynthesizerService::UpdateLastCallTime() {
time_last_call_ = base::Time::Now();
}
void SpeechSynthesizerService::ShutdownIfIdle() {
const int kShutdownTimeout = 60000;
const base::Time shutdown_time =
time_last_call_ + base::TimeDelta::FromMilliseconds(kShutdownTimeout);
if (base::Time::Now() >= shutdown_time &&
ttsService_->GetStatus() == TTS_IDLE)
g_main_loop_quit(main_loop());
}
// static
gboolean SpeechSynthesizerService::OnTimeout(gpointer user_data) {
static_cast<SpeechSynthesizerService*>(user_data)->ShutdownIfIdle();
// TRUE means this method will be called again, repeatedly.
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// SpeechSynthesizerService commands
gboolean SpeechSynthesizerService::Speak(gchar *text,
gchar *properties,
GError **error) {
UpdateLastCallTime();
AddProperties(properties);
ttsService_->Speak(text, *utterance_options_);
utterance_options_->enqueue = false;
utterance_options_->interruptible = true;
return true;
}
gboolean SpeechSynthesizerService::Stop(GError **error) {
UpdateLastCallTime();
ttsService_->Stop();
return true;
}
gboolean SpeechSynthesizerService::IsSpeaking(
gboolean* OUT_isSpeaking_requested,
GError **error) {
UpdateLastCallTime();
*OUT_isSpeaking_requested = (ttsService_->GetStatus() == TTS_BUSY);
return true;
}
gboolean SpeechSynthesizerService::Shutdown(GError **error) {
g_main_loop_quit(main_loop());
return true;
}
} // namespace speech_synthesis