diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc index df830c421dfa9f..c3a66460a36afe 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc @@ -10,6 +10,7 @@ #include "base/json/json_writer.h" #include "base/strings/utf_string_conversions.h" +#include "base/system/sys_info.h" #include "base/values.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -366,7 +367,12 @@ bool TtsExtensionEngine::IsBuiltInTtsEngineInitialized( saw_espeak |= voice.engine_id == extension_misc::kEspeakSpeechSynthesisExtensionId; } - return saw_google_tts && saw_espeak; + + // When running on a real Chrome OS environment, require both Google tts and + // Espeak to be initialized; otherwise, only check for Espeak (i.e. on a + // non-Chrome OS linux system running the CHrome OS variant of Chrome). + return base::SysInfo::IsRunningOnChromeOS() ? (saw_google_tts && saw_espeak) + : saw_espeak; #else // Vacuously; no built in engines on other platforms yet. TODO: network tts? return true; diff --git a/chromeos/services/tts/tts_service.cc b/chromeos/services/tts/tts_service.cc index 3c56c398844e3a..25abdb68c75a2b 100644 --- a/chromeos/services/tts/tts_service.cc +++ b/chromeos/services/tts/tts_service.cc @@ -22,7 +22,9 @@ constexpr int kDefaultBufferSize = 512; } // namespace TtsService::TtsService(mojo::PendingReceiver receiver) - : service_receiver_(this, std::move(receiver)), tts_stream_factory_(this) { + : service_receiver_(this, std::move(receiver)), + tts_stream_factory_(this), + task_runner_(base::ThreadTaskRunnerHandle::Get()) { if (setpriority(PRIO_PROCESS, 0, -10 /* real time audio */) != 0) { PLOG(ERROR) << "Unable to request real time priority; performance will be " "impacted."; @@ -122,7 +124,6 @@ int TtsService::Render(base::TimeDelta delay, int prior_frames_skipped, media::AudioBus* dest) { size_t frames_in_buf = 0; - int32_t status = -1; { base::AutoLock al(state_lock_); if (buffers_.empty()) @@ -130,34 +131,6 @@ int TtsService::Render(base::TimeDelta delay, const AudioBuffer& buf = buffers_.front(); - status = buf.status; - // Done, 0, or error, -1. - if (status <= 0) { - if (status == -1) - tts_event_observer_->OnError(); - else - tts_event_observer_->OnEnd(); - - StopLocked(); - return 0; - } - - if (buf.is_first_buffer) { - start_playback_time_ = base::Time::Now(); - tts_event_observer_->OnStart(); - } - - // Implicit timepoint. - if (buf.char_index != -1) - tts_event_observer_->OnTimepoint(buf.char_index); - - // Explicit timepoint(s). - base::TimeDelta start_to_now = base::Time::Now() - start_playback_time_; - while (!timepoints_.empty() && timepoints_.front().second <= start_to_now) { - tts_event_observer_->OnTimepoint(timepoints_.front().first); - timepoints_.pop(); - } - frames_in_buf = buf.frames.size(); const float* frames = nullptr; if (!buf.frames.empty()) @@ -165,7 +138,16 @@ int TtsService::Render(base::TimeDelta delay, float* channel = dest->channel(0); for (size_t i = 0; i < frames_in_buf; i++) channel[i] = frames[i]; + + rendered_buffers_.push(std::move(buffers_.front())); buffers_.pop(); + + if (!process_rendered_buffers_posted_) { + process_rendered_buffers_posted_ = true; + task_runner_->PostTask(FROM_HERE, + base::BindOnce(&TtsService::ProcessRenderedBuffers, + weak_factory_.GetWeakPtr())); + } } return frames_in_buf; @@ -175,6 +157,7 @@ void TtsService::OnRenderError() {} void TtsService::StopLocked(bool clear_buffers) { output_device_->Pause(); + rendered_buffers_ = std::queue(); if (clear_buffers) { buffers_ = std::queue(); timepoints_ = std::queue(); @@ -191,6 +174,41 @@ void TtsService::ProcessPendingTtsStreamFactories() { tts_stream_factory_.Bind(std::move(factory)); } +void TtsService::ProcessRenderedBuffers() { + base::AutoLock al(state_lock_); + process_rendered_buffers_posted_ = false; + for (; !rendered_buffers_.empty(); rendered_buffers_.pop()) { + const auto& buf = rendered_buffers_.front(); + int status = buf.status; + // Done, 0, or error, -1. + if (status <= 0) { + if (status == -1) + tts_event_observer_->OnError(); + else + tts_event_observer_->OnEnd(); + + StopLocked(); + return; + } + + if (buf.is_first_buffer) { + start_playback_time_ = base::Time::Now(); + tts_event_observer_->OnStart(); + } + + // Implicit timepoint. + if (buf.char_index != -1) + tts_event_observer_->OnTimepoint(buf.char_index); + } + + // Explicit timepoint(s). + base::TimeDelta start_to_now = base::Time::Now() - start_playback_time_; + while (!timepoints_.empty() && timepoints_.front().second <= start_to_now) { + tts_event_observer_->OnTimepoint(timepoints_.front().first); + timepoints_.pop(); + } +} + TtsService::AudioBuffer::AudioBuffer() = default; TtsService::AudioBuffer::~AudioBuffer() = default; diff --git a/chromeos/services/tts/tts_service.h b/chromeos/services/tts/tts_service.h index 3c945ccf6a6220..0f62fd15f114e2 100644 --- a/chromeos/services/tts/tts_service.h +++ b/chromeos/services/tts/tts_service.h @@ -92,6 +92,10 @@ class TtsService : public mojom::TtsService, // Satisfies any pending tts stream factory receivers. void ProcessPendingTtsStreamFactories(); + // Do any processing (e.g. sending start/end events) on buffers that have just + // been rendered on the audio thread. + void ProcessRenderedBuffers(); + // Connection to tts in the browser. mojo::Receiver service_receiver_; @@ -117,6 +121,7 @@ class TtsService : public mojom::TtsService, // The queue of audio buffers to be played by the audio thread. std::queue buffers_ GUARDED_BY(state_lock_); + std::queue rendered_buffers_; // An explicit list of increasing time delta sorted timepoints to be fired // while rendering audio at the specified |delay| from start of audio @@ -126,6 +131,14 @@ class TtsService : public mojom::TtsService, // The time at which playback of the current utterance started. base::Time start_playback_time_; + + // Whether a task to process rendered audio buffers has been posted. + bool process_rendered_buffers_posted_ GUARDED_BY(state_lock_) = false; + + // The main thread's task runner handle. + scoped_refptr task_runner_; + + base::WeakPtrFactory weak_factory_{this}; }; } // namespace tts