Commit 4ce7ba8f authored by Etienne Bergeron's avatar Etienne Bergeron Committed by Commit Bot

Move Linux TTS platform code to a worker thread

This CL is moving the libspeech synthesizer code to a worker thread.

This CL is splitting the logic in two parts. The first part is the
control logic which stay on the UI Thread. The second part is the
synthesizer logic which is running on a worker thread.

Bug: 869399, 1133813
Change-Id: I212f58ff94e5deac7950e206e4aac484d285f0f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2488045
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820302}
parent 99875d4c
...@@ -13,11 +13,16 @@ ...@@ -13,11 +13,16 @@
#include "base/debug/leak_annotations.h" #include "base/debug/leak_annotations.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "base/task_runner.h"
#include "base/threading/sequence_bound.h"
#include "content/browser/speech/tts_platform_impl.h" #include "content/browser/speech/tts_platform_impl.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/tts_controller.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "library_loaders/libspeechd.h" #include "library_loaders/libspeechd.h"
...@@ -30,8 +35,71 @@ struct SPDChromeVoice { ...@@ -30,8 +35,71 @@ struct SPDChromeVoice {
std::string module; std::string module;
}; };
using PlatformVoices = std::map<std::string, SPDChromeVoice>;
constexpr int kInvalidUtteranceId = -1;
constexpr int kInvalidMessageUid = -1;
} // namespace } // namespace
class TtsPlatformImplBackgroundWorker {
public:
TtsPlatformImplBackgroundWorker() = default;
TtsPlatformImplBackgroundWorker(const TtsPlatformImplBackgroundWorker&) =
delete;
TtsPlatformImplBackgroundWorker& operator=(
const TtsPlatformImplBackgroundWorker&) = delete;
~TtsPlatformImplBackgroundWorker() = default;
void Initialize();
void ProcessSpeech(int utterance_id,
const std::string& parsed_utterance,
const std::string& lang,
float rate,
float pitch,
SPDChromeVoice voice,
base::OnceCallback<void(bool)> on_speak_finished);
void Pause();
void Resume();
void StopSpeaking();
void Shutdown();
private:
bool InitializeSpeechd();
void InitializeVoices(PlatformVoices*);
void OpenConnection();
void CloseConnection();
void OnSpeechEvent(int msg_id, SPDNotificationType type);
// Send an TTS event notification to the TTS controller.
void SendTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
int length = -1);
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);
LibSpeechdLoader libspeechd_loader_;
SPDConnection* conn_ = nullptr;
int msg_uid_ = kInvalidMessageUid;
// These apply to the current utterance only that is currently being
// processed.
int utterance_id_ = kInvalidUtteranceId;
size_t utterance_length_ = 0;
size_t utterance_char_position_ = 0;
};
class TtsPlatformImplLinux : public TtsPlatformImpl { class TtsPlatformImplLinux : public TtsPlatformImpl {
public: public:
TtsPlatformImplLinux(const TtsPlatformImplLinux&) = delete; TtsPlatformImplLinux(const TtsPlatformImplLinux&) = delete;
...@@ -50,8 +118,16 @@ class TtsPlatformImplLinux : public TtsPlatformImpl { ...@@ -50,8 +118,16 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
void Resume() override; void Resume() override;
bool IsSpeaking() override; bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override; void GetVoices(std::vector<VoiceData>* out_voices) override;
void Shutdown() override;
void OnInitialized(bool success, PlatformVoices voices);
void OnSpeakScheduled(base::OnceCallback<void(bool)> on_speak_finished,
bool success);
void OnSpeakFinished(int utterance_id);
void OnSpeechEvent(SPDNotificationType type); base::SequenceBound<TtsPlatformImplBackgroundWorker>* worker() {
return &worker_;
}
// Get the single instance of this class. // Get the single instance of this class.
static TtsPlatformImplLinux* GetInstance(); static TtsPlatformImplLinux* GetInstance();
...@@ -60,21 +136,6 @@ class TtsPlatformImplLinux : public TtsPlatformImpl { ...@@ -60,21 +136,6 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
friend base::NoDestructor<TtsPlatformImplLinux>; friend base::NoDestructor<TtsPlatformImplLinux>;
TtsPlatformImplLinux(); 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);
void ProcessSpeech(int utterance_id, void ProcessSpeech(int utterance_id,
const std::string& lang, const std::string& lang,
const VoiceData& voice, const VoiceData& voice,
...@@ -82,49 +143,139 @@ class TtsPlatformImplLinux : public TtsPlatformImpl { ...@@ -82,49 +143,139 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
base::OnceCallback<void(bool)> on_speak_finished, base::OnceCallback<void(bool)> on_speak_finished,
const std::string& parsed_utterance); const std::string& parsed_utterance);
static SPDNotificationType current_notification_; // Holds the platform state.
bool is_supported_ = false; bool is_supported_ = false;
bool is_initialized_ = false;
bool is_speaking_ = false;
bool paused_ = false;
base::Lock initialization_lock_; // The current utterance being spoke.
LibSpeechdLoader libspeechd_loader_; int utterance_id_ = kInvalidUtteranceId;
SPDConnection* conn_;
// These apply to the current utterance only.
std::string utterance_;
int utterance_id_ = 0;
// Map a string composed of a voicename and module to the voicename. Used to // Map a string composed of a voicename and module to the voicename. Used to
// uniquely identify a voice across all available modules. // uniquely identify a voice across all available modules.
std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_; PlatformVoices voices_;
// Hold the state and the code of the background implementation.
base::SequenceBound<TtsPlatformImplBackgroundWorker> worker_;
}; };
// static //
SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END; // TtsPlatformImplBackgroundWorker
//
TtsPlatformImplLinux::TtsPlatformImplLinux() { void TtsPlatformImplBackgroundWorker::Initialize() {
const base::CommandLine& command_line = PlatformVoices voices;
*base::CommandLine::ForCurrentProcess(); if (InitializeSpeechd()) {
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher)) OpenConnection();
return; InitializeVoices(&voices);
}
// The TTS platform is supported. The Tts platform initialisation will happen bool success = (conn_ != nullptr);
// on a worker thread and it will be in the available state after the GetUIThreadTaskRunner({})->PostTask(
// initialisation. FROM_HERE,
is_supported_ = true; base::BindOnce(&TtsPlatformImplLinux::OnInitialized,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
success, std::move(voices)));
}
base::ThreadPool::PostTask( void TtsPlatformImplBackgroundWorker::ProcessSpeech(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, int utterance_id,
base::BindOnce(&TtsPlatformImplLinux::Initialize, const std::string& parsed_utterance,
base::Unretained(this))); const std::string& lang,
float rate,
float pitch,
SPDChromeVoice voice,
base::OnceCallback<void(bool)> on_speak_finished) {
libspeechd_loader_.spd_set_output_module(conn_, voice.module.c_str());
libspeechd_loader_.spd_set_synthesis_voice(conn_, voice.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_id_ = utterance_id;
utterance_char_position_ = 0;
utterance_length_ = parsed_utterance.size();
// spd_say(...) returns msg_uid on success, -1 otherwise. Each call to spd_say
// returns a different msg_uid.
msg_uid_ =
libspeechd_loader_.spd_say(conn_, SPD_TEXT, parsed_utterance.c_str());
bool success = (msg_uid_ != kInvalidMessageUid);
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(on_speak_finished), success));
}
void TtsPlatformImplBackgroundWorker::Pause() {
if (msg_uid_ != kInvalidMessageUid)
libspeechd_loader_.spd_pause(conn_);
}
void TtsPlatformImplBackgroundWorker::Resume() {
if (msg_uid_ != kInvalidMessageUid)
libspeechd_loader_.spd_resume(conn_);
}
void TtsPlatformImplBackgroundWorker::StopSpeaking() {
if (msg_uid_ != kInvalidMessageUid) {
int result = libspeechd_loader_.spd_stop(conn_);
if (result == -1) {
CloseConnection();
OpenConnection();
}
msg_uid_ = kInvalidMessageUid;
utterance_id_ = kInvalidUtteranceId;
}
}
void TtsPlatformImplBackgroundWorker::Shutdown() {
CloseConnection();
}
bool TtsPlatformImplBackgroundWorker::InitializeSpeechd() {
return libspeechd_loader_.Load("libspeechd.so.2");
} }
void TtsPlatformImplLinux::Initialize() { void TtsPlatformImplBackgroundWorker::InitializeVoices(PlatformVoices* voices) {
base::AutoLock lock(initialization_lock_); if (!conn_)
return;
if (!libspeechd_loader_.Load("libspeechd.so.2")) char** modules = libspeechd_loader_.spd_list_modules(conn_);
if (!modules)
return; return;
for (int i = 0; modules[i]; i++) {
char* module = modules[i];
libspeechd_loader_.spd_set_output_module(conn_, module);
SPDVoice** spd_voices = libspeechd_loader_.spd_list_synthesis_voices(conn_);
if (!spd_voices) {
free(module);
continue;
}
for (int j = 0; spd_voices[j]; j++) {
SPDVoice* spd_voice = spd_voices[j];
SPDChromeVoice spd_data;
spd_data.name = spd_voice->name;
spd_data.module = module;
std::string key;
key.append(spd_data.name);
key.append(" ");
key.append(spd_data.module);
voices->insert(std::pair<std::string, SPDChromeVoice>(key, spd_data));
free(spd_voices[j]);
}
free(modules[i]);
}
}
void TtsPlatformImplBackgroundWorker::OpenConnection() {
{ {
// spd_open has memory leaks which are hard to suppress. // spd_open has memory leaks which are hard to suppress.
// http://crbug.com/317360 // http://crbug.com/317360
...@@ -137,9 +288,10 @@ void TtsPlatformImplLinux::Initialize() { ...@@ -137,9 +288,10 @@ void TtsPlatformImplLinux::Initialize() {
// Register callbacks for all events. // Register callbacks for all events.
conn_->callback_begin = conn_->callback_end = conn_->callback_cancel = conn_->callback_begin = conn_->callback_end = conn_->callback_cancel =
conn_->callback_pause = conn_->callback_resume = &NotificationCallback; conn_->callback_pause = conn_->callback_resume =
&TtsPlatformImplBackgroundWorker::NotificationCallback;
conn_->callback_im = &IndexMarkCallback; conn_->callback_im = &TtsPlatformImplBackgroundWorker::IndexMarkCallback;
libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN); 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_END);
...@@ -148,12 +300,105 @@ void TtsPlatformImplLinux::Initialize() { ...@@ -148,12 +300,105 @@ void TtsPlatformImplLinux::Initialize() {
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME); libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
} }
void TtsPlatformImplLinux::Reset() { void TtsPlatformImplBackgroundWorker::CloseConnection() {
base::AutoLock lock(initialization_lock_); if (conn_) {
if (conn_)
libspeechd_loader_.spd_close(conn_); libspeechd_loader_.spd_close(conn_);
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", nullptr, conn_ = nullptr;
SPD_MODE_THREADED); }
}
void TtsPlatformImplBackgroundWorker::OnSpeechEvent(int msg_id,
SPDNotificationType type) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (msg_id != msg_uid_)
return;
switch (type) {
case SPD_EVENT_BEGIN:
utterance_char_position_ = 0;
SendTtsEvent(utterance_id_, TTS_EVENT_START, utterance_char_position_,
-1);
break;
case SPD_EVENT_RESUME:
SendTtsEvent(utterance_id_, TTS_EVENT_RESUME, utterance_char_position_,
-1);
break;
case SPD_EVENT_END:
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnSpeakFinished,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
utterance_id_));
utterance_char_position_ = utterance_length_;
SendTtsEvent(utterance_id_, TTS_EVENT_END, utterance_char_position_, 0);
break;
case SPD_EVENT_PAUSE:
SendTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_char_position_,
-1);
break;
case SPD_EVENT_CANCEL:
SendTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, utterance_char_position_,
-1);
break;
case SPD_EVENT_INDEX_MARK:
// TODO: Can we get length from linux? If so, update
// utterance_char_position_.
SendTtsEvent(utterance_id_, TTS_EVENT_MARKER, utterance_char_position_,
-1);
break;
}
}
void TtsPlatformImplBackgroundWorker::SendTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
int length) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&TtsController::OnTtsEvent,
base::Unretained(TtsController::GetInstance()),
utterance_id, event_type, char_index, length,
std::string()));
}
// static
void TtsPlatformImplBackgroundWorker::NotificationCallback(
size_t msg_id,
size_t client_id,
SPDNotificationType type) {
TtsPlatformImplLinux::GetInstance()->worker()->Post(
FROM_HERE, &TtsPlatformImplBackgroundWorker::OnSpeechEvent, msg_id, type);
}
// static
void TtsPlatformImplBackgroundWorker::IndexMarkCallback(
size_t msg_id,
size_t client_id,
SPDNotificationType type,
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.
TtsPlatformImplLinux::GetInstance()->worker()->Post(
FROM_HERE, &TtsPlatformImplBackgroundWorker::OnSpeechEvent, msg_id, type);
}
//
// TtsPlatformImplLinux
//
TtsPlatformImplLinux::TtsPlatformImplLinux()
: worker_(base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
return;
// The TTS platform is supported. The Tts platform initialisation will happen
// on a worker thread and it will become initialized.
is_supported_ = true;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Initialize);
} }
bool TtsPlatformImplLinux::PlatformImplSupported() { bool TtsPlatformImplLinux::PlatformImplSupported() {
...@@ -161,11 +406,7 @@ bool TtsPlatformImplLinux::PlatformImplSupported() { ...@@ -161,11 +406,7 @@ bool TtsPlatformImplLinux::PlatformImplSupported() {
} }
bool TtsPlatformImplLinux::PlatformImplInitialized() { bool TtsPlatformImplLinux::PlatformImplInitialized() {
if (!initialization_lock_.Try()) return is_initialized_;
return false;
bool result = libspeechd_loader_.loaded() && (conn_ != nullptr);
initialization_lock_.Release();
return result;
} }
void TtsPlatformImplLinux::Speak( void TtsPlatformImplLinux::Speak(
...@@ -175,113 +416,74 @@ void TtsPlatformImplLinux::Speak( ...@@ -175,113 +416,74 @@ void TtsPlatformImplLinux::Speak(
const VoiceData& voice, const VoiceData& voice,
const UtteranceContinuousParameters& params, const UtteranceContinuousParameters& params,
base::OnceCallback<void(bool)> on_speak_finished) { base::OnceCallback<void(bool)> on_speak_finished) {
DCHECK(PlatformImplSupported()); DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
// Parse SSML and process speech.
TtsController::GetInstance()->StripSSML(
utterance, base::BindOnce(&TtsPlatformImplLinux::ProcessSpeech,
base::Unretained(this), utterance_id, lang,
voice, params, std::move(on_speak_finished)));
}
void TtsPlatformImplLinux::ProcessSpeech(
int utterance_id,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params,
base::OnceCallback<void(bool)> on_speak_finished,
const std::string& parsed_utterance) {
// 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;
auto it = all_native_voices_->find(voice.name); if (paused_ || is_speaking_) {
if (it != all_native_voices_->end()) { std::move(on_speak_finished).Run(false);
libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str()); return;
libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
} }
// Map our multiplicative range to Speech Dispatcher's linear range. // Flag that a utterance is getting emitted. The |is_speaking_| flag will be
// .334 = -100. // set back to false when the utterance will be fully spoken, stopped or if
// 3 = 100. // the voice synthetizer was not able to emit it.
libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3)); is_speaking_ = true;
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_ = parsed_utterance;
utterance_id_ = utterance_id; utterance_id_ = utterance_id;
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, parsed_utterance.c_str()) == // Parse SSML and process speech.
-1) { TtsController::GetInstance()->StripSSML(
Reset(); utterance,
std::move(on_speak_finished).Run(false); base::BindOnce(&TtsPlatformImplLinux::ProcessSpeech,
return; base::Unretained(this), utterance_id, lang, voice, params,
} base::BindOnce(&TtsPlatformImplLinux::OnSpeakScheduled,
std::move(on_speak_finished).Run(true); base::Unretained(this),
std::move(on_speak_finished))));
} }
bool TtsPlatformImplLinux::StopSpeaking() { bool TtsPlatformImplLinux::StopSpeaking() {
DCHECK(PlatformImplSupported()); DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (libspeechd_loader_.spd_stop(conn_) == -1) { DCHECK(PlatformImplInitialized());
Reset();
return false; worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::StopSpeaking);
} paused_ = false;
is_speaking_ = false;
utterance_id_ = kInvalidUtteranceId;
return true; return true;
} }
void TtsPlatformImplLinux::Pause() { void TtsPlatformImplLinux::Pause() {
DCHECK(PlatformImplSupported()); DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
libspeechd_loader_.spd_pause(conn_); DCHECK(PlatformImplInitialized());
if (paused_ || !is_speaking_)
return;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Pause);
paused_ = true;
} }
void TtsPlatformImplLinux::Resume() { void TtsPlatformImplLinux::Resume() {
DCHECK(PlatformImplSupported()); DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
libspeechd_loader_.spd_resume(conn_); DCHECK(PlatformImplInitialized());
if (!paused_ || !is_speaking_)
return;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Resume);
paused_ = false;
} }
bool TtsPlatformImplLinux::IsSpeaking() { bool TtsPlatformImplLinux::IsSpeaking() {
return current_notification_ == SPD_EVENT_BEGIN; return is_speaking_;
} }
void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) { void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
if (!all_native_voices_.get()) { DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>()); DCHECK(PlatformImplInitialized());
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 (auto it = all_native_voices_->begin(); it != all_native_voices_->end(); for (auto it = voices_.begin(); it != voices_.end(); ++it) {
++it) {
out_voices->push_back(VoiceData()); out_voices->push_back(VoiceData());
VoiceData& voice = out_voices->back(); VoiceData& voice = out_voices->back();
voice.native = true; voice.native = true;
...@@ -295,71 +497,69 @@ void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) { ...@@ -295,71 +497,69 @@ void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
} }
} }
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) { void TtsPlatformImplLinux::Shutdown() {
TtsController* controller = TtsController::GetInstance(); worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Shutdown);
switch (type) {
case SPD_EVENT_BEGIN:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
utterance_.size(), std::string());
break;
case SPD_EVENT_RESUME:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, -1,
std::string());
break;
case SPD_EVENT_END:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(), 0,
std::string());
break;
case SPD_EVENT_PAUSE:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
-1, std::string());
break;
case SPD_EVENT_CANCEL:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0, -1,
std::string());
break;
case SPD_EVENT_INDEX_MARK:
// TODO: Can we get length from linux?
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, -1,
std::string());
break;
}
} }
// static void TtsPlatformImplLinux::OnInitialized(bool success, PlatformVoices voices) {
void TtsPlatformImplLinux::NotificationCallback(size_t msg_id, DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
size_t client_id, if (success)
SPDNotificationType type) { voices_ = std::move(voices);
// We run Speech Dispatcher in threaded mode, so these callbacks should always is_initialized_ = true;
// be in a separate thread. TtsController::GetInstance()->VoicesChanged();
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = type;
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
type));
}
} }
// static void TtsPlatformImplLinux::OnSpeakScheduled(
void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id, base::OnceCallback<void(bool)> on_speak_finished,
size_t client_id, bool success) {
SPDNotificationType state, DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
char* index_mark) { DCHECK(is_speaking_);
// 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 // If the utterance was not able to be emitted, stop the speaking. There
// word, sentence events. // won't be any asynchronous TTS event to confirm the end of the speech.
// We run Speech Dispatcher in threaded mode, so these callbacks should always if (!success) {
// be in a separate thread. is_speaking_ = false;
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { utterance_id_ = kInvalidUtteranceId;
current_notification_ = state;
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
state));
} }
// Pass the results to our caller.
std::move(on_speak_finished).Run(success);
}
void TtsPlatformImplLinux::OnSpeakFinished(int utterance_id) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (utterance_id != utterance_id_)
return;
DCHECK(is_speaking_);
DCHECK_NE(utterance_id_, kInvalidUtteranceId);
is_speaking_ = false;
utterance_id_ = kInvalidUtteranceId;
}
void TtsPlatformImplLinux::ProcessSpeech(
int utterance_id,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params,
base::OnceCallback<void(bool)> on_speak_finished,
const std::string& parsed_utterance) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// 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;
SPDChromeVoice matched_voice;
auto it = voices_.find(voice.name);
if (it != voices_.end())
matched_voice = it->second;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::ProcessSpeech,
utterance_id, parsed_utterance, lang, rate, pitch, matched_voice,
std::move(on_speak_finished));
} }
// static // static
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment