1 // Copyright 2019 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 "content/browser/speech/speech_synthesis_impl.h"
6 
7 namespace content {
8 namespace {
9 
10 // The lifetime of instances of this class is manually bound to the lifetime of
11 // the associated TtsUtterance. See OnTtsEvent.
12 class EventThunk : public UtteranceEventDelegate {
13  public:
EventThunk(mojo::PendingRemote<blink::mojom::SpeechSynthesisClient> client)14   explicit EventThunk(
15       mojo::PendingRemote<blink::mojom::SpeechSynthesisClient> client)
16       : client_(std::move(client)) {}
17   ~EventThunk() override = default;
18 
19   // UtteranceEventDelegate methods:
OnTtsEvent(TtsUtterance * utterance,TtsEventType event_type,int char_index,int char_length,const std::string & error_message)20   void OnTtsEvent(TtsUtterance* utterance,
21                   TtsEventType event_type,
22                   int char_index,
23                   int char_length,
24                   const std::string& error_message) override {
25     // These values are unsigned in the web speech API, so -1 cannot be used as
26     // a sentinel value. Use 0 instead to match web standards.
27     char_index = std::max(char_index, 0);
28     char_length = std::max(char_length, 0);
29 
30     switch (event_type) {
31       case TTS_EVENT_START:
32         client_->OnStartedSpeaking();
33         break;
34       case TTS_EVENT_END:
35       case TTS_EVENT_INTERRUPTED:
36       case TTS_EVENT_CANCELLED:
37         // The web platform API does not differentiate these events.
38         client_->OnFinishedSpeaking();
39         break;
40       case TTS_EVENT_WORD:
41         client_->OnEncounteredWordBoundary(char_index, char_length);
42         break;
43       case TTS_EVENT_SENTENCE:
44         client_->OnEncounteredSentenceBoundary(char_index, 0);
45         break;
46       case TTS_EVENT_MARKER:
47         // The web platform API does not support this event.
48         break;
49       case TTS_EVENT_ERROR:
50         // The web platform API does not support error text.
51         client_->OnEncounteredSpeakingError();
52         break;
53       case TTS_EVENT_PAUSE:
54         client_->OnPausedSpeaking();
55         break;
56       case TTS_EVENT_RESUME:
57         client_->OnResumedSpeaking();
58         break;
59     }
60 
61     if (utterance->IsFinished())
62       delete this;
63   }
64 
65  private:
66   mojo::Remote<blink::mojom::SpeechSynthesisClient> client_;
67 };
68 
SendVoiceListToObserver(blink::mojom::SpeechSynthesisVoiceListObserver * observer,const std::vector<VoiceData> & voices)69 void SendVoiceListToObserver(
70     blink::mojom::SpeechSynthesisVoiceListObserver* observer,
71     const std::vector<VoiceData>& voices) {
72   std::vector<blink::mojom::SpeechSynthesisVoicePtr> out_voices;
73   out_voices.resize(voices.size());
74   for (size_t i = 0; i < voices.size(); ++i) {
75     blink::mojom::SpeechSynthesisVoicePtr& out_voice = out_voices[i];
76     out_voice = blink::mojom::SpeechSynthesisVoice::New();
77     out_voice->voice_uri = voices[i].name;
78     out_voice->name = voices[i].name;
79     out_voice->lang = voices[i].lang;
80     out_voice->is_local_service = !voices[i].remote;
81     out_voice->is_default = (i == 0);
82   }
83   observer->OnSetVoiceList(std::move(out_voices));
84 }
85 
86 }  // namespace
87 
SpeechSynthesisImpl(BrowserContext * browser_context)88 SpeechSynthesisImpl::SpeechSynthesisImpl(BrowserContext* browser_context)
89     : browser_context_(browser_context) {
90   DCHECK(browser_context_);
91   TtsController::GetInstance()->AddVoicesChangedDelegate(this);
92 }
93 
~SpeechSynthesisImpl()94 SpeechSynthesisImpl::~SpeechSynthesisImpl() {
95   TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
96 
97   // NOTE: Some EventThunk instances may outlive this class, and that's okay.
98   // They have their lifetime bound to their associated TtsUtterance instance,
99   // and the TtsController manages the lifetime of those.
100 }
101 
AddReceiver(mojo::PendingReceiver<blink::mojom::SpeechSynthesis> receiver)102 void SpeechSynthesisImpl::AddReceiver(
103     mojo::PendingReceiver<blink::mojom::SpeechSynthesis> receiver) {
104   receiver_set_.Add(this, std::move(receiver));
105 }
106 
AddVoiceListObserver(mojo::PendingRemote<blink::mojom::SpeechSynthesisVoiceListObserver> pending_observer)107 void SpeechSynthesisImpl::AddVoiceListObserver(
108     mojo::PendingRemote<blink::mojom::SpeechSynthesisVoiceListObserver>
109         pending_observer) {
110   mojo::Remote<blink::mojom::SpeechSynthesisVoiceListObserver> observer(
111       std::move(pending_observer));
112 
113   std::vector<VoiceData> voices;
114   TtsController::GetInstance()->GetVoices(browser_context_, &voices);
115   SendVoiceListToObserver(observer.get(), voices);
116 
117   observer_set_.Add(std::move(observer));
118 }
119 
Speak(blink::mojom::SpeechSynthesisUtterancePtr utterance,mojo::PendingRemote<blink::mojom::SpeechSynthesisClient> client)120 void SpeechSynthesisImpl::Speak(
121     blink::mojom::SpeechSynthesisUtterancePtr utterance,
122     mojo::PendingRemote<blink::mojom::SpeechSynthesisClient> client) {
123   std::unique_ptr<TtsUtterance> tts_utterance(
124       TtsUtterance::Create((browser_context_)));
125   tts_utterance->SetText(utterance->text);
126   tts_utterance->SetLang(utterance->lang);
127   tts_utterance->SetVoiceName(utterance->voice);
128   tts_utterance->SetCanEnqueue(true);
129   tts_utterance->SetContinuousParameters(utterance->rate, utterance->pitch,
130                                          utterance->volume);
131 
132   // See comments on EventThunk about how lifetime of this instance is managed.
133   tts_utterance->SetEventDelegate(new EventThunk(std::move(client)));
134 
135   TtsController::GetInstance()->SpeakOrEnqueue(std::move(tts_utterance));
136 }
137 
Pause()138 void SpeechSynthesisImpl::Pause() {
139   TtsController::GetInstance()->Pause();
140 }
141 
Resume()142 void SpeechSynthesisImpl::Resume() {
143   TtsController::GetInstance()->Resume();
144 }
145 
Cancel()146 void SpeechSynthesisImpl::Cancel() {
147   TtsController::GetInstance()->Stop();
148 }
149 
OnVoicesChanged()150 void SpeechSynthesisImpl::OnVoicesChanged() {
151   std::vector<VoiceData> voices;
152   TtsController::GetInstance()->GetVoices(browser_context_, &voices);
153   for (auto& observer : observer_set_)
154     SendVoiceListToObserver(observer.get(), voices);
155 }
156 
157 }  // namespace content
158