123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- // 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 <memory>
- #include "base/command_line.h"
- #include "base/debug/leak_annotations.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 "content/public/common/content_switches.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:
- bool PlatformImplAvailable() override;
- bool Speak(int utterance_id,
- const std::string& utterance,
- const std::string& lang,
- const VoiceData& voice,
- const UtteranceContinuousParameters& params) override;
- bool StopSpeaking() override;
- void Pause() override;
- void Resume() override;
- bool IsSpeaking() override;
- void GetVoices(std::vector<VoiceData>* out_voices) override;
- void OnSpeechEvent(SPDNotificationType type);
- // Get the single instance of this class.
- static TtsPlatformImplLinux* GetInstance();
- private:
- TtsPlatformImplLinux();
- ~TtsPlatformImplLinux() override;
- // 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.
- std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_;
- friend struct base::DefaultSingletonTraits<TtsPlatformImplLinux>;
- DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
- };
- // static
- SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END;
- TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
- return;
- 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_THREADED);
- }
- 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_THREADED);
- }
- 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));
- // Support languages other than the default
- if (!lang.empty())
- libspeechd_loader_.spd_set_language(conn_, lang.c_str());
- 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 base::Singleton<
- TtsPlatformImplLinux,
- base::LeakySingletonTraits<TtsPlatformImplLinux>>::get();
- }
- // static
- TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
- return TtsPlatformImplLinux::GetInstance();
- }
|