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 @@
#include "base/debug/leak_annotations.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task/task_traits.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/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/tts_controller.h"
#include "content/public/common/content_switches.h"
#include "library_loaders/libspeechd.h"
......@@ -30,8 +35,71 @@ struct SPDChromeVoice {
std::string module;
};
using PlatformVoices = std::map<std::string, SPDChromeVoice>;
constexpr int kInvalidUtteranceId = -1;
constexpr int kInvalidMessageUid = -1;
} // 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 {
public:
TtsPlatformImplLinux(const TtsPlatformImplLinux&) = delete;
......@@ -50,8 +118,16 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
void Shutdown() override;
void OnSpeechEvent(SPDNotificationType type);
void OnInitialized(bool success, PlatformVoices voices);
void OnSpeakScheduled(base::OnceCallback<void(bool)> on_speak_finished,
bool success);
void OnSpeakFinished(int utterance_id);
base::SequenceBound<TtsPlatformImplBackgroundWorker>* worker() {
return &worker_;
}
// Get the single instance of this class.
static TtsPlatformImplLinux* GetInstance();
......@@ -60,21 +136,6 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
friend base::NoDestructor<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,
const std::string& lang,
const VoiceData& voice,
......@@ -82,49 +143,139 @@ class TtsPlatformImplLinux : public TtsPlatformImpl {
base::OnceCallback<void(bool)> on_speak_finished,
const std::string& parsed_utterance);
static SPDNotificationType current_notification_;
// Holds the platform state.
bool is_supported_ = false;
bool is_initialized_ = false;
bool is_speaking_ = false;
bool paused_ = false;
base::Lock initialization_lock_;
LibSpeechdLoader libspeechd_loader_;
SPDConnection* conn_;
// These apply to the current utterance only.
std::string utterance_;
int utterance_id_ = 0;
// The current utterance being spoke.
int utterance_id_ = kInvalidUtteranceId;
// 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_;
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() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
return;
void TtsPlatformImplBackgroundWorker::Initialize() {
PlatformVoices voices;
if (InitializeSpeechd()) {
OpenConnection();
InitializeVoices(&voices);
}
// The TTS platform is supported. The Tts platform initialisation will happen
// on a worker thread and it will be in the available state after the
// initialisation.
is_supported_ = true;
bool success = (conn_ != nullptr);
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnInitialized,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
success, std::move(voices)));
}
void TtsPlatformImplBackgroundWorker::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) {
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;
}
}
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&TtsPlatformImplLinux::Initialize,
base::Unretained(this)));
void TtsPlatformImplBackgroundWorker::Shutdown() {
CloseConnection();
}
void TtsPlatformImplLinux::Initialize() {
base::AutoLock lock(initialization_lock_);
bool TtsPlatformImplBackgroundWorker::InitializeSpeechd() {
return libspeechd_loader_.Load("libspeechd.so.2");
}
if (!libspeechd_loader_.Load("libspeechd.so.2"))
void TtsPlatformImplBackgroundWorker::InitializeVoices(PlatformVoices* voices) {
if (!conn_)
return;
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** 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.
// http://crbug.com/317360
......@@ -137,9 +288,10 @@ void TtsPlatformImplLinux::Initialize() {
// Register callbacks for all events.
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_END);
......@@ -148,12 +300,105 @@ void TtsPlatformImplLinux::Initialize() {
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
}
void TtsPlatformImplLinux::Reset() {
base::AutoLock lock(initialization_lock_);
if (conn_)
void TtsPlatformImplBackgroundWorker::CloseConnection() {
if (conn_) {
libspeechd_loader_.spd_close(conn_);
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", nullptr,
SPD_MODE_THREADED);
conn_ = nullptr;
}
}
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() {
......@@ -161,11 +406,7 @@ bool TtsPlatformImplLinux::PlatformImplSupported() {
}
bool TtsPlatformImplLinux::PlatformImplInitialized() {
if (!initialization_lock_.Try())
return false;
bool result = libspeechd_loader_.loaded() && (conn_ != nullptr);
initialization_lock_.Release();
return result;
return is_initialized_;
}
void TtsPlatformImplLinux::Speak(
......@@ -175,113 +416,74 @@ void TtsPlatformImplLinux::Speak(
const VoiceData& voice,
const UtteranceContinuousParameters& params,
base::OnceCallback<void(bool)> on_speak_finished) {
DCHECK(PlatformImplSupported());
// 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;
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
auto 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());
if (paused_ || is_speaking_) {
std::move(on_speak_finished).Run(false);
return;
}
// 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_ = parsed_utterance;
// Flag that a utterance is getting emitted. The |is_speaking_| flag will be
// set back to false when the utterance will be fully spoken, stopped or if
// the voice synthetizer was not able to emit it.
is_speaking_ = true;
utterance_id_ = utterance_id;
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, parsed_utterance.c_str()) ==
-1) {
Reset();
std::move(on_speak_finished).Run(false);
return;
}
std::move(on_speak_finished).Run(true);
// Parse SSML and process speech.
TtsController::GetInstance()->StripSSML(
utterance,
base::BindOnce(&TtsPlatformImplLinux::ProcessSpeech,
base::Unretained(this), utterance_id, lang, voice, params,
base::BindOnce(&TtsPlatformImplLinux::OnSpeakScheduled,
base::Unretained(this),
std::move(on_speak_finished))));
}
bool TtsPlatformImplLinux::StopSpeaking() {
DCHECK(PlatformImplSupported());
if (libspeechd_loader_.spd_stop(conn_) == -1) {
Reset();
return false;
}
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::StopSpeaking);
paused_ = false;
is_speaking_ = false;
utterance_id_ = kInvalidUtteranceId;
return true;
}
void TtsPlatformImplLinux::Pause() {
DCHECK(PlatformImplSupported());
libspeechd_loader_.spd_pause(conn_);
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
if (paused_ || !is_speaking_)
return;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Pause);
paused_ = true;
}
void TtsPlatformImplLinux::Resume() {
DCHECK(PlatformImplSupported());
libspeechd_loader_.spd_resume(conn_);
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
if (!paused_ || !is_speaking_)
return;
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Resume);
paused_ = false;
}
bool TtsPlatformImplLinux::IsSpeaking() {
return current_notification_ == SPD_EVENT_BEGIN;
return is_speaking_;
}
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]);
}
}
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(PlatformImplInitialized());
for (auto it = all_native_voices_->begin(); it != all_native_voices_->end();
++it) {
for (auto it = voices_.begin(); it != voices_.end(); ++it) {
out_voices->push_back(VoiceData());
VoiceData& voice = out_voices->back();
voice.native = true;
......@@ -295,71 +497,69 @@ void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
}
}
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
TtsController* controller = TtsController::GetInstance();
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;
}
void TtsPlatformImplLinux::Shutdown() {
worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Shutdown);
}
// 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;
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
type));
}
void TtsPlatformImplLinux::OnInitialized(bool success, PlatformVoices voices) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (success)
voices_ = std::move(voices);
is_initialized_ = true;
TtsController::GetInstance()->VoicesChanged();
}
// 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;
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
state));
void TtsPlatformImplLinux::OnSpeakScheduled(
base::OnceCallback<void(bool)> on_speak_finished,
bool success) {
DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(is_speaking_);
// If the utterance was not able to be emitted, stop the speaking. There
// won't be any asynchronous TTS event to confirm the end of the speech.
if (!success) {
is_speaking_ = false;
utterance_id_ = kInvalidUtteranceId;
}
// 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
......
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