1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <math.h>
6 #include <objbase.h>
7 #include <sapi.h>
8 #include <stdint.h>
9 #include <wrl/client.h>
10 #include <wrl/implements.h>
11 
12 #include <algorithm>
13 
14 #include "base/bind.h"
15 #include "base/macros.h"
16 #include "base/no_destructor.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/synchronization/lock.h"
22 #include "base/task/task_traits.h"
23 #include "base/task/thread_pool.h"
24 #include "base/task_runner.h"
25 #include "base/thread_annotations.h"
26 #include "base/threading/sequence_bound.h"
27 #include "base/values.h"
28 #include "base/win/scoped_co_mem.h"
29 #include "base/win/sphelper.h"
30 #include "content/browser/speech/tts_platform_impl.h"
31 #include "content/public/browser/browser_task_traits.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/tts_controller.h"
34 
35 namespace content {
36 
37 namespace {
38 
39 class TtsPlatformImplWin;
40 class TtsPlatformImplBackgroundWorker;
41 
42 constexpr int kInvalidUtteranceId = -1;
43 
44 // ISpObjectToken key and value names.
45 const wchar_t kAttributesKey[] = L"Attributes";
46 const wchar_t kLanguageValue[] = L"Language";
47 
48 // This COM interface is receiving the TTS events on the ISpVoice asynchronous
49 // worker thread and is emitting a notification task
50 // TtsPlatformImplBackgroundWorker::SendTtsEvent(...) on the worker sequence.
51 class TtsEventSink
52     : public Microsoft::WRL::RuntimeClass<
53           Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
54           ISpNotifySink> {
55  public:
TtsEventSink(TtsPlatformImplBackgroundWorker * worker,scoped_refptr<base::TaskRunner> worker_task_runner)56   TtsEventSink(TtsPlatformImplBackgroundWorker* worker,
57                scoped_refptr<base::TaskRunner> worker_task_runner)
58       : worker_(worker), worker_task_runner_(std::move(worker_task_runner)) {}
59 
60   // ISpNotifySink:
61   IFACEMETHODIMP Notify(void) override;
62 
GetUtteranceId()63   int GetUtteranceId() {
64     base::AutoLock lock(lock_);
65     return utterance_id_;
66   }
67 
SetUtteranceId(int utterance_id)68   void SetUtteranceId(int utterance_id) {
69     base::AutoLock lock(lock_);
70     utterance_id_ = utterance_id;
71   }
72 
73  private:
74   // |worker_| is leaky and must never deleted because TtsEventSink posts
75   // asynchronous tasks to it.
76   TtsPlatformImplBackgroundWorker* worker_;
77   scoped_refptr<base::TaskRunner> worker_task_runner_;
78 
79   base::Lock lock_;
80   int utterance_id_ GUARDED_BY(lock_);
81 };
82 
83 class TtsPlatformImplBackgroundWorker {
84  public:
TtsPlatformImplBackgroundWorker(scoped_refptr<base::TaskRunner> task_runner)85   explicit TtsPlatformImplBackgroundWorker(
86       scoped_refptr<base::TaskRunner> task_runner)
87       : tts_event_sink_(
88             Microsoft::WRL::Make<TtsEventSink>(this, std::move(task_runner))) {}
89   TtsPlatformImplBackgroundWorker(const TtsPlatformImplBackgroundWorker&) =
90       delete;
91   TtsPlatformImplBackgroundWorker& operator=(
92       const TtsPlatformImplBackgroundWorker&) = delete;
93   ~TtsPlatformImplBackgroundWorker() = default;
94 
95   void Initialize();
96 
97   void ProcessSpeech(int utterance_id,
98                      const std::string& lang,
99                      const VoiceData& voice,
100                      const UtteranceContinuousParameters& params,
101                      base::OnceCallback<void(bool)> on_speak_finished,
102                      const std::string& parsed_utterance);
103 
104   void StopSpeaking(bool paused);
105   void Pause();
106   void Resume();
107   void Shutdown();
108 
109   // This function is called after being notified by the speech synthetizer that
110   // there are TTS notifications are available and should be they should be
111   // processed.
112   void OnSpeechEvent(int utterance_id);
113 
114   // Send an TTS event notification to the TTS controller.
115   void SendTtsEvent(int utterance_id,
116                     TtsEventType event_type,
117                     int char_index,
118                     int length = -1);
119 
120  private:
121   void GetVoices(std::vector<VoiceData>* voices);
122 
123   void SetVoiceFromName(const std::string& name);
124 
125   // These apply to the current utterance only that is currently being processed
126   // on the worker thread. TTS events are dispatched by TtsEventSink to this
127   // class and update the current speaking state of the utterance.
128   std::string last_voice_name_;
129   ULONG stream_number_ = 0u;
130   int utterance_id_ = kInvalidUtteranceId;
131   size_t utterance_char_position_ = 0u;
132   size_t utterance_prefix_length_ = 0u;
133   size_t utterance_length_ = 0u;
134 
135   // The COM class ISpVoice lives within the COM MTA apartment (worker pool).
136   // This interface can not be called on the UI thread since UI thread is
137   // COM STA.
138   Microsoft::WRL::ComPtr<ISpVoice> speech_synthesizer_;
139   Microsoft::WRL::ComPtr<TtsEventSink> tts_event_sink_;
140 };
141 
142 class TtsPlatformImplWin : public TtsPlatformImpl {
143  public:
144   TtsPlatformImplWin(const TtsPlatformImplWin&) = delete;
145   TtsPlatformImplWin& operator=(const TtsPlatformImplWin&) = delete;
146 
PlatformImplSupported()147   bool PlatformImplSupported() override { return true; }
148   bool PlatformImplInitialized() override;
149 
150   void Speak(int utterance_id,
151              const std::string& utterance,
152              const std::string& lang,
153              const VoiceData& voice,
154              const UtteranceContinuousParameters& params,
155              base::OnceCallback<void(bool)> on_speak_finished) override;
156 
157   bool StopSpeaking() override;
158 
159   void Pause() override;
160 
161   void Resume() override;
162 
163   bool IsSpeaking() override;
164 
165   void GetVoices(std::vector<VoiceData>* out_voices) override;
166 
167   void Shutdown() override;
168 
169   void OnInitializeComplete(bool success, std::vector<VoiceData> voices);
170   void OnSpeakScheduled(base::OnceCallback<void(bool)> on_speak_finished,
171                         bool success);
172   void OnSpeakFinished(int utterance_id);
173 
174   // Get the single instance of this class.
175   static TtsPlatformImplWin* GetInstance();
176 
177  private:
178   friend base::NoDestructor<TtsPlatformImplWin>;
179   TtsPlatformImplWin();
180 
181   void ProcessSpeech(int utterance_id,
182                      const std::string& lang,
183                      const VoiceData& voice,
184                      const UtteranceContinuousParameters& params,
185                      base::OnceCallback<void(bool)> on_speak_finished,
186                      const std::string& parsed_utterance);
187 
188   void FinishCurrentUtterance();
189 
190   // These variables hold the platform state.
191   bool paused_ = false;
192   bool is_speaking_ = false;
193   int utterance_id_ = kInvalidUtteranceId;
194   bool platform_initialized_ = false;
195   std::vector<VoiceData> voices_;
196 
197   // Hold the state and the code of the background implementation.
198   scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
199   base::SequenceBound<TtsPlatformImplBackgroundWorker> worker_;
200 };
201 
Notify()202 HRESULT TtsEventSink::Notify() {
203   worker_task_runner_->PostTask(
204       FROM_HERE, base::BindOnce(&TtsPlatformImplBackgroundWorker::OnSpeechEvent,
205                                 base::Unretained(worker_), GetUtteranceId()));
206   return S_OK;
207 }
208 
209 //
210 // TtsPlatformImplBackgroundWorker
211 //
212 
Initialize()213 void TtsPlatformImplBackgroundWorker::Initialize() {
214   bool success = false;
215   std::vector<VoiceData> voices;
216 
217   ::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL,
218                      IID_PPV_ARGS(&speech_synthesizer_));
219   if (speech_synthesizer_.Get()) {
220     ULONGLONG event_mask =
221         SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_TTS_BOOKMARK) |
222         SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_SENTENCE_BOUNDARY) |
223         SPFEI(SPEI_END_INPUT_STREAM);
224     speech_synthesizer_->SetInterest(event_mask, event_mask);
225     speech_synthesizer_->SetNotifySink(tts_event_sink_.Get());
226 
227     GetVoices(&voices);
228 
229     success = true;
230   }
231 
232   GetUIThreadTaskRunner({})->PostTask(
233       FROM_HERE,
234       base::BindOnce(&TtsPlatformImplWin::OnInitializeComplete,
235                      base::Unretained(TtsPlatformImplWin::GetInstance()),
236                      success, std::move(voices)));
237 }
238 
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)239 void TtsPlatformImplBackgroundWorker::ProcessSpeech(
240     int utterance_id,
241     const std::string& lang,
242     const VoiceData& voice,
243     const UtteranceContinuousParameters& params,
244     base::OnceCallback<void(bool)> on_speak_finished,
245     const std::string& parsed_utterance) {
246   DCHECK(speech_synthesizer_.Get());
247 
248   SetVoiceFromName(voice.name);
249 
250   if (params.rate >= 0.0) {
251     // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
252     // linear range of -10 to 10:
253     //   0.1 -> -10
254     //   1.0 -> 0
255     //  10.0 -> 10
256     speech_synthesizer_->SetRate(static_cast<int32_t>(10 * log10(params.rate)));
257   }
258 
259   std::wstring prefix;
260   std::wstring suffix;
261   if (params.pitch >= 0.0) {
262     // The TTS api allows a range of -10 to 10 for speech pitch:
263     // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms720500(v%3Dvs.85)
264     // Note that the API requires an integer value, so be sure to cast the pitch
265     // value to an int before calling NumberToString16. TODO(dtseng): cleanup if
266     // we ever use any other properties that require xml.
267     double adjusted_pitch =
268         std::max<double>(-10, std::min<double>(params.pitch * 10 - 10, 10));
269     std::wstring adjusted_pitch_string =
270         base::NumberToString16(static_cast<int>(adjusted_pitch));
271     prefix = L"<pitch absmiddle=\"" + adjusted_pitch_string + L"\">";
272     suffix = L"</pitch>";
273   }
274 
275   if (params.volume >= 0.0) {
276     // The TTS api allows a range of 0 to 100 for speech volume.
277     speech_synthesizer_->SetVolume(static_cast<uint16_t>(params.volume * 100));
278   }
279 
280   // TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
281 
282   std::wstring utterance = base::UTF8ToWide(parsed_utterance);
283   std::wstring merged_utterance = prefix + utterance + suffix;
284 
285   utterance_id_ = utterance_id;
286   utterance_char_position_ = 0;
287   utterance_length_ = utterance.size();
288   utterance_prefix_length_ = prefix.size();
289 
290   tts_event_sink_->SetUtteranceId(utterance_id);
291 
292   HRESULT result = speech_synthesizer_->Speak(merged_utterance.c_str(),
293                                               SPF_ASYNC, &stream_number_);
294   bool success = (result == S_OK);
295   GetUIThreadTaskRunner({})->PostTask(
296       FROM_HERE, base::BindOnce(std::move(on_speak_finished), success));
297 }
298 
FinishCurrentUtterance()299 void TtsPlatformImplWin::FinishCurrentUtterance() {
300   if (paused_)
301     Resume();
302 
303   DCHECK(is_speaking_);
304   DCHECK_NE(utterance_id_, kInvalidUtteranceId);
305   is_speaking_ = false;
306   utterance_id_ = kInvalidUtteranceId;
307 }
308 
StopSpeaking(bool paused)309 void TtsPlatformImplBackgroundWorker::StopSpeaking(bool paused) {
310   if (speech_synthesizer_.Get()) {
311     // Block notifications from the current utterance.
312     tts_event_sink_->SetUtteranceId(kInvalidUtteranceId);
313     utterance_id_ = kInvalidUtteranceId;
314 
315     // Stop speech by speaking nullptr with the purge flag.
316     speech_synthesizer_->Speak(nullptr, SPF_PURGEBEFORESPEAK, nullptr);
317 
318     // Ensures the synthesizer is not paused after a stop.
319     if (paused)
320       speech_synthesizer_->Resume();
321   }
322 }
323 
Pause()324 void TtsPlatformImplBackgroundWorker::Pause() {
325   if (speech_synthesizer_.Get()) {
326     speech_synthesizer_->Pause();
327     SendTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_char_position_);
328   }
329 }
330 
Resume()331 void TtsPlatformImplBackgroundWorker::Resume() {
332   if (speech_synthesizer_.Get()) {
333     speech_synthesizer_->Resume();
334     SendTtsEvent(utterance_id_, TTS_EVENT_RESUME, utterance_char_position_);
335   }
336 }
337 
Shutdown()338 void TtsPlatformImplBackgroundWorker::Shutdown() {
339   if (speech_synthesizer_)
340     speech_synthesizer_->SetNotifySink(nullptr);
341   if (tts_event_sink_) {
342     tts_event_sink_->SetUtteranceId(kInvalidUtteranceId);
343     utterance_id_ = kInvalidUtteranceId;
344   }
345 
346   tts_event_sink_ = nullptr;
347   speech_synthesizer_ = nullptr;
348 }
349 
OnSpeechEvent(int utterance_id)350 void TtsPlatformImplBackgroundWorker::OnSpeechEvent(int utterance_id) {
351   if (!speech_synthesizer_.Get())
352     return;
353 
354   SPEVENT event;
355   while (S_OK == speech_synthesizer_->GetEvents(1, &event, nullptr)) {
356     // Ignore notifications that are not related to the current utterance.
357     if (event.ulStreamNum != stream_number_ ||
358         utterance_id_ == kInvalidUtteranceId || utterance_id != utterance_id_) {
359       continue;
360     }
361 
362     switch (event.eEventId) {
363       case SPEI_START_INPUT_STREAM:
364         utterance_char_position_ = 0;
365         SendTtsEvent(utterance_id_, TTS_EVENT_START, utterance_char_position_);
366         break;
367       case SPEI_END_INPUT_STREAM:
368         GetUIThreadTaskRunner({})->PostTask(
369             FROM_HERE,
370             base::BindOnce(&TtsPlatformImplWin::OnSpeakFinished,
371                            base::Unretained(TtsPlatformImplWin::GetInstance()),
372                            utterance_id_));
373 
374         utterance_char_position_ = utterance_length_;
375         SendTtsEvent(utterance_id_, TTS_EVENT_END, utterance_char_position_);
376         break;
377       case SPEI_TTS_BOOKMARK:
378         SendTtsEvent(utterance_id_, TTS_EVENT_MARKER, utterance_char_position_);
379         break;
380       case SPEI_WORD_BOUNDARY:
381         utterance_char_position_ =
382             static_cast<size_t>(event.lParam) - utterance_prefix_length_;
383         SendTtsEvent(utterance_id_, TTS_EVENT_WORD, utterance_char_position_,
384                      static_cast<ULONG>(event.wParam));
385 
386         break;
387       case SPEI_SENTENCE_BOUNDARY:
388         utterance_char_position_ =
389             static_cast<size_t>(event.lParam) - utterance_prefix_length_;
390         SendTtsEvent(utterance_id_, TTS_EVENT_SENTENCE,
391                      utterance_char_position_);
392         break;
393       default:
394         break;
395     }
396   }
397 }
398 
SendTtsEvent(int utterance_id,TtsEventType event_type,int char_index,int length)399 void TtsPlatformImplBackgroundWorker::SendTtsEvent(int utterance_id,
400                                                    TtsEventType event_type,
401                                                    int char_index,
402                                                    int length) {
403   GetUIThreadTaskRunner({})->PostTask(
404       FROM_HERE, base::BindOnce(&TtsController::OnTtsEvent,
405                                 base::Unretained(TtsController::GetInstance()),
406                                 utterance_id, event_type, char_index, length,
407                                 std::string()));
408 }
409 
GetVoices(std::vector<VoiceData> * out_voices)410 void TtsPlatformImplBackgroundWorker::GetVoices(
411     std::vector<VoiceData>* out_voices) {
412   if (!speech_synthesizer_.Get())
413     return;
414 
415   Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
416   unsigned long voice_count;
417   if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voice_tokens))
418     return;
419   if (S_OK != voice_tokens->GetCount(&voice_count))
420     return;
421 
422   for (unsigned i = 0; i < voice_count; i++) {
423     VoiceData voice;
424 
425     Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
426     if (S_OK != voice_tokens->Next(1, &voice_token, NULL))
427       return;
428 
429     base::win::ScopedCoMem<WCHAR> description;
430     if (S_OK != SpGetDescription(voice_token.Get(), &description))
431       continue;
432     voice.name = base::WideToUTF8(description.get());
433 
434     Microsoft::WRL::ComPtr<ISpDataKey> attributes;
435     if (S_OK != voice_token->OpenKey(kAttributesKey, &attributes))
436       continue;
437 
438     base::win::ScopedCoMem<WCHAR> language;
439     if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) {
440       int lcid_value;
441       base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value);
442       LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT);
443       WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0};
444       LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0);
445       voice.lang = base::WideToUTF8(locale_name);
446     }
447 
448     voice.native = true;
449     voice.events.insert(TTS_EVENT_START);
450     voice.events.insert(TTS_EVENT_END);
451     voice.events.insert(TTS_EVENT_MARKER);
452     voice.events.insert(TTS_EVENT_WORD);
453     voice.events.insert(TTS_EVENT_SENTENCE);
454     voice.events.insert(TTS_EVENT_PAUSE);
455     voice.events.insert(TTS_EVENT_RESUME);
456     out_voices->push_back(voice);
457   }
458 }
459 
SetVoiceFromName(const std::string & name)460 void TtsPlatformImplBackgroundWorker::SetVoiceFromName(
461     const std::string& name) {
462   if (name.empty() || name == last_voice_name_)
463     return;
464 
465   last_voice_name_ = name;
466 
467   Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
468   unsigned long voice_count;
469   if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voice_tokens))
470     return;
471   if (S_OK != voice_tokens->GetCount(&voice_count))
472     return;
473 
474   for (unsigned i = 0; i < voice_count; i++) {
475     Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
476     if (S_OK != voice_tokens->Next(1, &voice_token, NULL))
477       return;
478 
479     base::win::ScopedCoMem<WCHAR> description;
480     if (S_OK != SpGetDescription(voice_token.Get(), &description))
481       continue;
482     if (name == base::WideToUTF8(description.get())) {
483       speech_synthesizer_->SetVoice(voice_token.Get());
484       break;
485     }
486   }
487 }
488 
489 //
490 // TtsPlatformImplWin
491 //
492 
PlatformImplInitialized()493 bool TtsPlatformImplWin::PlatformImplInitialized() {
494   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
495   return platform_initialized_;
496 }
497 
Speak(int utterance_id,const std::string & utterance,const std::string & lang,const VoiceData & voice,const UtteranceContinuousParameters & params,base::OnceCallback<void (bool)> on_speak_finished)498 void TtsPlatformImplWin::Speak(
499     int utterance_id,
500     const std::string& utterance,
501     const std::string& lang,
502     const VoiceData& voice,
503     const UtteranceContinuousParameters& params,
504     base::OnceCallback<void(bool)> on_speak_finished) {
505   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
506   DCHECK(platform_initialized_);
507 
508   // Do not emit utterance if the platform is not ready.
509   if (paused_ || is_speaking_) {
510     std::move(on_speak_finished).Run(false);
511     return;
512   }
513 
514   // Flag that a utterance is getting emitted. The |is_speaking_| flag will be
515   // set back to false when the utterance will be fully spoken, stopped or if
516   // the voice synthetizer was not able to emit it.
517   is_speaking_ = true;
518   utterance_id_ = utterance_id;
519 
520   // Parse SSML and process speech.
521   TtsController::GetInstance()->StripSSML(
522       utterance,
523       base::BindOnce(&TtsPlatformImplWin::ProcessSpeech, base::Unretained(this),
524                      utterance_id, lang, voice, params,
525                      base::BindOnce(&TtsPlatformImplWin::OnSpeakScheduled,
526                                     base::Unretained(this),
527                                     std::move(on_speak_finished))));
528 }
529 
StopSpeaking()530 bool TtsPlatformImplWin::StopSpeaking() {
531   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
532 
533   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::StopSpeaking,
534                paused_);
535   paused_ = false;
536 
537   is_speaking_ = false;
538   utterance_id_ = kInvalidUtteranceId;
539 
540   return true;
541 }
542 
Pause()543 void TtsPlatformImplWin::Pause() {
544   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
545   DCHECK(platform_initialized_);
546 
547   if (paused_ || !is_speaking_)
548     return;
549   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Pause);
550   paused_ = true;
551 }
552 
Resume()553 void TtsPlatformImplWin::Resume() {
554   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
555   DCHECK(platform_initialized_);
556 
557   if (!paused_)
558     return;
559 
560   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Resume);
561   paused_ = false;
562 }
563 
IsSpeaking()564 bool TtsPlatformImplWin::IsSpeaking() {
565   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
566   DCHECK(platform_initialized_);
567   return is_speaking_ && !paused_;
568 }
569 
GetVoices(std::vector<VoiceData> * out_voices)570 void TtsPlatformImplWin::GetVoices(std::vector<VoiceData>* out_voices) {
571   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
572   DCHECK(platform_initialized_);
573   out_voices->insert(out_voices->end(), voices_.begin(), voices_.end());
574 }
575 
Shutdown()576 void TtsPlatformImplWin::Shutdown() {
577   // This is required to ensures the object is released before the COM is
578   // uninitialized. Otherwise, this is causing shutdown hangs.
579   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Shutdown);
580 }
581 
OnInitializeComplete(bool success,std::vector<VoiceData> voices)582 void TtsPlatformImplWin::OnInitializeComplete(bool success,
583                                               std::vector<VoiceData> voices) {
584   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
585 
586   if (success)
587     voices_ = std::move(voices);
588 
589   platform_initialized_ = true;
590   TtsController::GetInstance()->VoicesChanged();
591 }
592 
OnSpeakScheduled(base::OnceCallback<void (bool)> on_speak_finished,bool success)593 void TtsPlatformImplWin::OnSpeakScheduled(
594     base::OnceCallback<void(bool)> on_speak_finished,
595     bool success) {
596   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
597   DCHECK(is_speaking_);
598 
599   // If the utterance was not able to be emitted, stop the speaking. There
600   // won't be any asynchronous TTS event to confirm the end of the speech.
601   if (!success)
602     FinishCurrentUtterance();
603 
604   // Pass the results to our caller.
605   std::move(on_speak_finished).Run(success);
606 }
607 
OnSpeakFinished(int utterance_id)608 void TtsPlatformImplWin::OnSpeakFinished(int utterance_id) {
609   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
610   if (utterance_id != utterance_id_)
611     return;
612 
613   FinishCurrentUtterance();
614 }
615 
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)616 void TtsPlatformImplWin::ProcessSpeech(
617     int utterance_id,
618     const std::string& lang,
619     const VoiceData& voice,
620     const UtteranceContinuousParameters& params,
621     base::OnceCallback<void(bool)> on_speak_finished,
622     const std::string& parsed_utterance) {
623   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
624 
625   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::ProcessSpeech,
626                utterance_id, lang, voice, params, std::move(on_speak_finished),
627                parsed_utterance);
628 }
629 
TtsPlatformImplWin()630 TtsPlatformImplWin::TtsPlatformImplWin()
631     : worker_task_runner_(
632           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
633       worker_(worker_task_runner_, worker_task_runner_) {
634   DCHECK(BrowserThread::CurrentlyOn(content::BrowserThread::UI));
635   worker_.Post(FROM_HERE, &TtsPlatformImplBackgroundWorker::Initialize);
636 }
637 
638 // static
GetInstance()639 TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
640   static base::NoDestructor<TtsPlatformImplWin> tts_platform;
641   return tts_platform.get();
642 }
643 
644 }  // namespace
645 
646 // static
GetInstance()647 TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
648   return TtsPlatformImplWin::GetInstance();
649 }
650 
651 }  // namespace content
652