| // 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 |