| // 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(); |
| |
| // Wait 3 seconds before attempting, to let other audio initialization |
| // finish before we open the device. |
| // TODO(dmazzoni): revert this and replace with logic in Chrome to |
| // not call the speech synthesizer until the mixer is initialized. |
| // http://crosbug.com/19067 |
| usleep(3000000); |
| |
| 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 |