Add linux tts implementation from Chrome

This commit is contained in:
Cheng Zhao 2014-10-08 02:14:12 +00:00
Родитель b2741a8316
Коммит 3a177d55f8
3 изменённых файлов: 581 добавлений и 1 удалений

Просмотреть файл

@ -282,6 +282,7 @@
'chromium_src/chrome/browser/speech/tts_controller.h',
'chromium_src/chrome/browser/speech/tts_controller_impl.cc',
'chromium_src/chrome/browser/speech/tts_controller_impl.h',
'chromium_src/chrome/browser/speech/tts_linux.cc',
'chromium_src/chrome/browser/speech/tts_mac.mm',
'chromium_src/chrome/browser/speech/tts_message_filter.cc',
'chromium_src/chrome/browser/speech/tts_message_filter.h',
@ -308,6 +309,7 @@
'chromium_src/chrome/renderer/printing/print_web_view_helper.h',
'chromium_src/chrome/renderer/tts_dispatcher.cc',
'chromium_src/chrome/renderer/tts_dispatcher.h',
'chromium_src/library_loaders/libspeechd_loader.cc',
'<@(native_mate_files)',
],
'framework_sources': [
@ -563,7 +565,7 @@
'vendor/breakpad/src',
],
'cflags': [
'-Wno-deprecated-declarations',
'-Wno-deprecated-register',
'-Wno-empty-body',
],
'dependencies': [

Просмотреть файл

@ -0,0 +1,347 @@
// Copyright (c) 2012 The Chromium 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 <math.h>
#include <map>
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/speech/tts_platform.h"
#include "content/public/browser/browser_thread.h"
#include "library_loaders/libspeechd.h"
using content::BrowserThread;
namespace {
const char kNotSupportedError[] =
"Native speech synthesis not supported on this platform.";
struct SPDChromeVoice {
std::string name;
std::string module;
};
} // namespace
class TtsPlatformImplLinux : public TtsPlatformImpl {
public:
virtual bool PlatformImplAvailable() OVERRIDE;
virtual bool Speak(
int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) OVERRIDE;
virtual bool StopSpeaking() OVERRIDE;
virtual void Pause() OVERRIDE;
virtual void Resume() OVERRIDE;
virtual bool IsSpeaking() OVERRIDE;
virtual void GetVoices(std::vector<VoiceData>* out_voices) OVERRIDE;
void OnSpeechEvent(SPDNotificationType type);
// Get the single instance of this class.
static TtsPlatformImplLinux* GetInstance();
private:
TtsPlatformImplLinux();
virtual ~TtsPlatformImplLinux();
// Initiate the connection with the speech dispatcher.
void Initialize();
// Resets the connection with speech dispatcher.
void Reset();
static void NotificationCallback(size_t msg_id,
size_t client_id,
SPDNotificationType type);
static void IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark);
static SPDNotificationType current_notification_;
base::Lock initialization_lock_;
LibSpeechdLoader libspeechd_loader_;
SPDConnection* conn_;
// These apply to the current utterance only.
std::string utterance_;
int utterance_id_;
// Map a string composed of a voicename and module to the voicename. Used to
// uniquely identify a voice across all available modules.
scoped_ptr<std::map<std::string, SPDChromeVoice> > all_native_voices_;
friend struct DefaultSingletonTraits<TtsPlatformImplLinux>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
};
// static
SPDNotificationType TtsPlatformImplLinux::current_notification_ =
SPD_EVENT_END;
TtsPlatformImplLinux::TtsPlatformImplLinux()
: utterance_id_(0) {
BrowserThread::PostTask(BrowserThread::FILE,
FROM_HERE,
base::Bind(&TtsPlatformImplLinux::Initialize,
base::Unretained(this)));
}
void TtsPlatformImplLinux::Initialize() {
base::AutoLock lock(initialization_lock_);
if (!libspeechd_loader_.Load("libspeechd.so.2"))
return;
{
// spd_open has memory leaks which are hard to suppress.
// http://crbug.com/317360
ANNOTATE_SCOPED_MEMORY_LEAK;
conn_ = libspeechd_loader_.spd_open(
"chrome", "extension_api", NULL, SPD_MODE_SINGLE);
}
if (!conn_)
return;
// Register callbacks for all events.
conn_->callback_begin =
conn_->callback_end =
conn_->callback_cancel =
conn_->callback_pause =
conn_->callback_resume =
&NotificationCallback;
conn_->callback_im = &IndexMarkCallback;
libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
}
TtsPlatformImplLinux::~TtsPlatformImplLinux() {
base::AutoLock lock(initialization_lock_);
if (conn_) {
libspeechd_loader_.spd_close(conn_);
conn_ = NULL;
}
}
void TtsPlatformImplLinux::Reset() {
base::AutoLock lock(initialization_lock_);
if (conn_)
libspeechd_loader_.spd_close(conn_);
conn_ = libspeechd_loader_.spd_open(
"chrome", "extension_api", NULL, SPD_MODE_SINGLE);
}
bool TtsPlatformImplLinux::PlatformImplAvailable() {
if (!initialization_lock_.Try())
return false;
bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
initialization_lock_.Release();
return result;
}
bool TtsPlatformImplLinux::Speak(
int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
if (!PlatformImplAvailable()) {
error_ = kNotSupportedError;
return false;
}
// Speech dispatcher's speech params are around 3x at either limit.
float rate = params.rate > 3 ? 3 : params.rate;
rate = params.rate < 0.334 ? 0.334 : rate;
float pitch = params.pitch > 3 ? 3 : params.pitch;
pitch = params.pitch < 0.334 ? 0.334 : pitch;
std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->find(voice.name);
if (it != all_native_voices_->end()) {
libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
}
// Map our multiplicative range to Speech Dispatcher's linear range.
// .334 = -100.
// 3 = 100.
libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
utterance_ = utterance;
utterance_id_ = utterance_id;
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
Reset();
return false;
}
return true;
}
bool TtsPlatformImplLinux::StopSpeaking() {
if (!PlatformImplAvailable())
return false;
if (libspeechd_loader_.spd_stop(conn_) == -1) {
Reset();
return false;
}
return true;
}
void TtsPlatformImplLinux::Pause() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_pause(conn_);
}
void TtsPlatformImplLinux::Resume() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_resume(conn_);
}
bool TtsPlatformImplLinux::IsSpeaking() {
return current_notification_ == SPD_EVENT_BEGIN;
}
void TtsPlatformImplLinux::GetVoices(
std::vector<VoiceData>* out_voices) {
if (!all_native_voices_.get()) {
all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
char** modules = libspeechd_loader_.spd_list_modules(conn_);
if (!modules)
return;
for (int i = 0; modules[i]; i++) {
char* module = modules[i];
libspeechd_loader_.spd_set_output_module(conn_, module);
SPDVoice** native_voices =
libspeechd_loader_.spd_list_synthesis_voices(conn_);
if (!native_voices) {
free(module);
continue;
}
for (int j = 0; native_voices[j]; j++) {
SPDVoice* native_voice = native_voices[j];
SPDChromeVoice native_data;
native_data.name = native_voice->name;
native_data.module = module;
std::string key;
key.append(native_data.name);
key.append(" ");
key.append(native_data.module);
all_native_voices_->insert(
std::pair<std::string, SPDChromeVoice>(key, native_data));
free(native_voices[j]);
}
free(modules[i]);
}
}
for (std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->begin();
it != all_native_voices_->end();
it++) {
out_voices->push_back(VoiceData());
VoiceData& voice = out_voices->back();
voice.native = true;
voice.name = it->first;
voice.events.insert(TTS_EVENT_START);
voice.events.insert(TTS_EVENT_END);
voice.events.insert(TTS_EVENT_CANCELLED);
voice.events.insert(TTS_EVENT_MARKER);
voice.events.insert(TTS_EVENT_PAUSE);
voice.events.insert(TTS_EVENT_RESUME);
}
}
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
TtsController* controller = TtsController::GetInstance();
switch (type) {
case SPD_EVENT_BEGIN:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
break;
case SPD_EVENT_RESUME:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
break;
case SPD_EVENT_END:
controller->OnTtsEvent(
utterance_id_, TTS_EVENT_END, utterance_.size(), std::string());
break;
case SPD_EVENT_PAUSE:
controller->OnTtsEvent(
utterance_id_, TTS_EVENT_PAUSE, utterance_.size(), std::string());
break;
case SPD_EVENT_CANCEL:
controller->OnTtsEvent(
utterance_id_, TTS_EVENT_CANCELLED, 0, std::string());
break;
case SPD_EVENT_INDEX_MARK:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
break;
}
}
// static
void TtsPlatformImplLinux::NotificationCallback(
size_t msg_id, size_t client_id, SPDNotificationType type) {
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = type;
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
type));
}
}
// static
void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark) {
// TODO(dtseng): index_mark appears to specify an index type supplied by a
// client. Need to explore how this is used before hooking it up with existing
// word, sentence events.
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = state;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
state));
}
}
// static
TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
return Singleton<TtsPlatformImplLinux,
LeakySingletonTraits<TtsPlatformImplLinux> >::get();
}
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplLinux::GetInstance();
}

Просмотреть файл

@ -0,0 +1,231 @@
// This is generated file. Do not modify directly.
// Path to the code generator: tools/generate_library_loader/generate_library_loader.py .
#include "library_loaders/libspeechd.h"
#include <dlfcn.h>
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && !defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error neither LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN nor LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error both LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN and LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
LibSpeechdLoader::LibSpeechdLoader() : loaded_(false) {
}
LibSpeechdLoader::~LibSpeechdLoader() {
CleanUp(loaded_);
}
bool LibSpeechdLoader::Load(const std::string& library_name) {
if (loaded_)
return false;
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_)
return false;
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_open =
reinterpret_cast<typeof(this->spd_open)>(
dlsym(library_, "spd_open"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_open = &::spd_open;
#endif
if (!spd_open) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_say =
reinterpret_cast<typeof(this->spd_say)>(
dlsym(library_, "spd_say"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_say = &::spd_say;
#endif
if (!spd_say) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_stop =
reinterpret_cast<typeof(this->spd_stop)>(
dlsym(library_, "spd_stop"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_stop = &::spd_stop;
#endif
if (!spd_stop) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_close =
reinterpret_cast<typeof(this->spd_close)>(
dlsym(library_, "spd_close"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_close = &::spd_close;
#endif
if (!spd_close) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_pause =
reinterpret_cast<typeof(this->spd_pause)>(
dlsym(library_, "spd_pause"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_pause = &::spd_pause;
#endif
if (!spd_pause) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_resume =
reinterpret_cast<typeof(this->spd_resume)>(
dlsym(library_, "spd_resume"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_resume = &::spd_resume;
#endif
if (!spd_resume) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_notification_on =
reinterpret_cast<typeof(this->spd_set_notification_on)>(
dlsym(library_, "spd_set_notification_on"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_notification_on = &::spd_set_notification_on;
#endif
if (!spd_set_notification_on) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_rate =
reinterpret_cast<typeof(this->spd_set_voice_rate)>(
dlsym(library_, "spd_set_voice_rate"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_rate = &::spd_set_voice_rate;
#endif
if (!spd_set_voice_rate) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_pitch =
reinterpret_cast<typeof(this->spd_set_voice_pitch)>(
dlsym(library_, "spd_set_voice_pitch"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_pitch = &::spd_set_voice_pitch;
#endif
if (!spd_set_voice_pitch) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_synthesis_voices =
reinterpret_cast<typeof(this->spd_list_synthesis_voices)>(
dlsym(library_, "spd_list_synthesis_voices"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_synthesis_voices = &::spd_list_synthesis_voices;
#endif
if (!spd_list_synthesis_voices) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_synthesis_voice =
reinterpret_cast<typeof(this->spd_set_synthesis_voice)>(
dlsym(library_, "spd_set_synthesis_voice"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_synthesis_voice = &::spd_set_synthesis_voice;
#endif
if (!spd_set_synthesis_voice) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_modules =
reinterpret_cast<typeof(this->spd_list_modules)>(
dlsym(library_, "spd_list_modules"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_modules = &::spd_list_modules;
#endif
if (!spd_list_modules) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_output_module =
reinterpret_cast<typeof(this->spd_set_output_module)>(
dlsym(library_, "spd_set_output_module"));
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_output_module = &::spd_set_output_module;
#endif
if (!spd_set_output_module) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void LibSpeechdLoader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
spd_open = NULL;
spd_say = NULL;
spd_stop = NULL;
spd_close = NULL;
spd_pause = NULL;
spd_resume = NULL;
spd_set_notification_on = NULL;
spd_set_voice_rate = NULL;
spd_set_voice_pitch = NULL;
spd_list_synthesis_voices = NULL;
spd_set_synthesis_voice = NULL;
spd_list_modules = NULL;
spd_set_output_module = NULL;
}