1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AudioChannelService.h"
8 #include "AudioSegment.h"
9 #include "nsSpeechTask.h"
10 #include "nsSynthVoiceRegistry.h"
11 #include "nsXULAppAPI.h"
12 #include "SharedBuffer.h"
13 #include "SpeechSynthesis.h"
14 #include "nsGlobalWindowInner.h"
17 extern mozilla::LogModule
* GetSpeechSynthLog();
18 #define LOG(type, msg) MOZ_LOG(GetSpeechSynthLog(), type, msg)
22 namespace mozilla::dom
{
26 NS_IMPL_CYCLE_COLLECTION_WEAK(nsSpeechTask
, mSpeechSynthesis
, mUtterance
,
29 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSpeechTask
)
30 NS_INTERFACE_MAP_ENTRY(nsISpeechTask
)
31 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback
)
32 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
33 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsISpeechTask
)
36 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSpeechTask
)
37 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSpeechTask
)
39 nsSpeechTask::nsSpeechTask(SpeechSynthesisUtterance
* aUtterance
,
40 bool aShouldResistFingerprinting
)
41 : mUtterance(aUtterance
),
46 mShouldResistFingerprinting(aShouldResistFingerprinting
),
47 mState(STATE_PENDING
) {
48 mText
= aUtterance
->mText
;
49 mVolume
= aUtterance
->Volume();
52 nsSpeechTask::nsSpeechTask(float aVolume
, const nsAString
& aText
,
53 bool aShouldResistFingerprinting
)
54 : mUtterance(nullptr),
61 mShouldResistFingerprinting(aShouldResistFingerprinting
),
62 mState(STATE_PENDING
) {}
64 nsSpeechTask::~nsSpeechTask() { LOG(LogLevel::Debug
, ("~nsSpeechTask")); }
66 void nsSpeechTask::Init() { mInited
= true; }
68 void nsSpeechTask::SetChosenVoiceURI(const nsAString
& aUri
) {
69 mChosenVoiceURI
= aUri
;
73 nsSpeechTask::Setup(nsISpeechTaskCallback
* aCallback
) {
74 MOZ_ASSERT(XRE_IsParentProcess());
76 LOG(LogLevel::Debug
, ("nsSpeechTask::Setup"));
78 mCallback
= aCallback
;
84 nsSpeechTask::DispatchStart() {
85 nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true);
86 return DispatchStartImpl();
89 nsresult
nsSpeechTask::DispatchStartImpl() {
90 return DispatchStartImpl(mChosenVoiceURI
);
93 nsresult
nsSpeechTask::DispatchStartImpl(const nsAString
& aUri
) {
94 LOG(LogLevel::Debug
, ("nsSpeechTask::DispatchStartImpl"));
96 MOZ_ASSERT(mUtterance
);
97 if (NS_WARN_IF(mState
!= STATE_PENDING
)) {
98 return NS_ERROR_NOT_AVAILABLE
;
101 CreateAudioChannelAgent();
103 mState
= STATE_SPEAKING
;
104 mUtterance
->mChosenVoiceURI
= aUri
;
105 mUtterance
->DispatchSpeechSynthesisEvent(u
"start"_ns
, 0, nullptr, 0, u
""_ns
);
111 nsSpeechTask::DispatchEnd(float aElapsedTime
, uint32_t aCharIndex
) {
112 // After we end, no callback functions should go through.
116 nsSynthVoiceRegistry::GetInstance()->SpeakNext();
119 return DispatchEndImpl(aElapsedTime
, aCharIndex
);
122 nsresult
nsSpeechTask::DispatchEndImpl(float aElapsedTime
,
123 uint32_t aCharIndex
) {
124 LOG(LogLevel::Debug
, ("nsSpeechTask::DispatchEndImpl"));
126 DestroyAudioChannelAgent();
128 MOZ_ASSERT(mUtterance
);
129 if (NS_WARN_IF(mState
== STATE_ENDED
)) {
130 return NS_ERROR_NOT_AVAILABLE
;
133 RefPtr
<SpeechSynthesisUtterance
> utterance
= mUtterance
;
135 if (mSpeechSynthesis
) {
136 mSpeechSynthesis
->OnEnd(this);
139 mState
= STATE_ENDED
;
140 utterance
->DispatchSpeechSynthesisEvent(u
"end"_ns
, aCharIndex
, nullptr,
141 aElapsedTime
, u
""_ns
);
147 nsSpeechTask::DispatchPause(float aElapsedTime
, uint32_t aCharIndex
) {
148 return DispatchPauseImpl(aElapsedTime
, aCharIndex
);
151 nsresult
nsSpeechTask::DispatchPauseImpl(float aElapsedTime
,
152 uint32_t aCharIndex
) {
153 LOG(LogLevel::Debug
, ("nsSpeechTask::DispatchPauseImpl"));
154 MOZ_ASSERT(mUtterance
);
155 if (NS_WARN_IF(mUtterance
->mPaused
)) {
156 return NS_ERROR_NOT_AVAILABLE
;
158 if (NS_WARN_IF(mState
== STATE_ENDED
)) {
159 return NS_ERROR_NOT_AVAILABLE
;
162 mUtterance
->mPaused
= true;
163 if (mState
== STATE_SPEAKING
) {
164 mUtterance
->DispatchSpeechSynthesisEvent(u
"pause"_ns
, aCharIndex
, nullptr,
165 aElapsedTime
, u
""_ns
);
172 nsSpeechTask::DispatchResume(float aElapsedTime
, uint32_t aCharIndex
) {
173 return DispatchResumeImpl(aElapsedTime
, aCharIndex
);
176 nsresult
nsSpeechTask::DispatchResumeImpl(float aElapsedTime
,
177 uint32_t aCharIndex
) {
178 LOG(LogLevel::Debug
, ("nsSpeechTask::DispatchResumeImpl"));
179 MOZ_ASSERT(mUtterance
);
180 if (NS_WARN_IF(!(mUtterance
->mPaused
))) {
181 return NS_ERROR_NOT_AVAILABLE
;
183 if (NS_WARN_IF(mState
== STATE_ENDED
)) {
184 return NS_ERROR_NOT_AVAILABLE
;
187 mUtterance
->mPaused
= false;
188 if (mState
== STATE_SPEAKING
) {
189 mUtterance
->DispatchSpeechSynthesisEvent(u
"resume"_ns
, aCharIndex
, nullptr,
190 aElapsedTime
, u
""_ns
);
196 void nsSpeechTask::ForceError(float aElapsedTime
, uint32_t aCharIndex
) {
197 DispatchError(aElapsedTime
, aCharIndex
);
201 nsSpeechTask::DispatchError(float aElapsedTime
, uint32_t aCharIndex
) {
203 nsSynthVoiceRegistry::GetInstance()->SpeakNext();
206 return DispatchErrorImpl(aElapsedTime
, aCharIndex
);
209 nsresult
nsSpeechTask::DispatchErrorImpl(float aElapsedTime
,
210 uint32_t aCharIndex
) {
211 LOG(LogLevel::Debug
, ("nsSpeechTask::DispatchErrorImpl"));
213 DestroyAudioChannelAgent();
215 MOZ_ASSERT(mUtterance
);
216 if (NS_WARN_IF(mState
== STATE_ENDED
)) {
217 return NS_ERROR_NOT_AVAILABLE
;
220 if (mSpeechSynthesis
) {
221 mSpeechSynthesis
->OnEnd(this);
224 mState
= STATE_ENDED
;
225 mUtterance
->DispatchSpeechSynthesisEvent(u
"error"_ns
, aCharIndex
, nullptr,
226 aElapsedTime
, u
""_ns
);
231 nsSpeechTask::DispatchBoundary(const nsAString
& aName
, float aElapsedTime
,
232 uint32_t aCharIndex
, uint32_t aCharLength
,
234 return DispatchBoundaryImpl(aName
, aElapsedTime
, aCharIndex
, aCharLength
,
238 nsresult
nsSpeechTask::DispatchBoundaryImpl(const nsAString
& aName
,
241 uint32_t aCharLength
,
243 MOZ_ASSERT(mUtterance
);
244 if (NS_WARN_IF(mState
!= STATE_SPEAKING
)) {
245 return NS_ERROR_NOT_AVAILABLE
;
247 mUtterance
->DispatchSpeechSynthesisEvent(
248 u
"boundary"_ns
, aCharIndex
,
249 argc
? static_cast<Nullable
<uint32_t> >(aCharLength
) : nullptr,
250 aElapsedTime
, aName
);
256 nsSpeechTask::DispatchMark(const nsAString
& aName
, float aElapsedTime
,
257 uint32_t aCharIndex
) {
258 return DispatchMarkImpl(aName
, aElapsedTime
, aCharIndex
);
261 nsresult
nsSpeechTask::DispatchMarkImpl(const nsAString
& aName
,
263 uint32_t aCharIndex
) {
264 MOZ_ASSERT(mUtterance
);
265 if (NS_WARN_IF(mState
!= STATE_SPEAKING
)) {
266 return NS_ERROR_NOT_AVAILABLE
;
268 mUtterance
->DispatchSpeechSynthesisEvent(u
"mark"_ns
, aCharIndex
, nullptr,
269 aElapsedTime
, aName
);
273 void nsSpeechTask::Pause() {
274 MOZ_ASSERT(XRE_IsParentProcess());
277 DebugOnly
<nsresult
> rv
= mCallback
->OnPause();
278 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Unable to call onPause() callback");
286 void nsSpeechTask::Resume() {
287 MOZ_ASSERT(XRE_IsParentProcess());
290 DebugOnly
<nsresult
> rv
= mCallback
->OnResume();
291 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
292 "Unable to call onResume() callback");
297 nsSynthVoiceRegistry::GetInstance()->ResumeQueue();
301 void nsSpeechTask::Cancel() {
302 MOZ_ASSERT(XRE_IsParentProcess());
304 LOG(LogLevel::Debug
, ("nsSpeechTask::Cancel"));
307 DebugOnly
<nsresult
> rv
= mCallback
->OnCancel();
308 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
309 "Unable to call onCancel() callback");
317 void nsSpeechTask::ForceEnd() {
325 void nsSpeechTask::SetSpeechSynthesis(SpeechSynthesis
* aSpeechSynthesis
) {
326 mSpeechSynthesis
= aSpeechSynthesis
;
329 void nsSpeechTask::CreateAudioChannelAgent() {
334 if (mAudioChannelAgent
) {
335 mAudioChannelAgent
->NotifyStoppedPlaying();
338 mAudioChannelAgent
= new AudioChannelAgent();
339 mAudioChannelAgent
->InitWithWeakCallback(mUtterance
->GetOwnerWindow(), this);
341 nsresult rv
= mAudioChannelAgent
->NotifyStartedPlaying(
342 AudioChannelService::AudibleState::eAudible
);
343 if (NS_WARN_IF(NS_FAILED(rv
))) {
347 mAudioChannelAgent
->PullInitialUpdate();
350 void nsSpeechTask::DestroyAudioChannelAgent() {
351 if (mAudioChannelAgent
) {
352 mAudioChannelAgent
->NotifyStoppedPlaying();
353 mAudioChannelAgent
= nullptr;
358 nsSpeechTask::WindowVolumeChanged(float aVolume
, bool aMuted
) {
359 SetAudioOutputVolume(aMuted
? 0.0 : mVolume
* aVolume
);
364 nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend
) {
369 if (aSuspend
== nsISuspendedTypes::NONE_SUSPENDED
&& mUtterance
->mPaused
) {
371 } else if (aSuspend
!= nsISuspendedTypes::NONE_SUSPENDED
&&
372 !mUtterance
->mPaused
) {
379 nsSpeechTask::WindowAudioCaptureChanged(bool aCapture
) {
380 // This is not supported yet.
384 void nsSpeechTask::SetAudioOutputVolume(float aVolume
) {
386 mCallback
->OnVolumeChanged(aVolume
);
390 } // namespace mozilla::dom