Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webspeech/synth/nsSpeechTask.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "AudioChannelAgent.h"
8
#include "AudioChannelService.h"
9
#include "AudioSegment.h"
10
#include "nsSpeechTask.h"
11
#include "nsSynthVoiceRegistry.h"
12
#include "SharedBuffer.h"
13
#include "SpeechSynthesis.h"
14
15
#undef LOG
16
extern mozilla::LogModule* GetSpeechSynthLog();
17
0
#define LOG(type, msg) MOZ_LOG(GetSpeechSynthLog(), type, msg)
18
19
#define AUDIO_TRACK 1
20
21
namespace mozilla {
22
namespace dom {
23
24
// nsSpeechTask
25
26
NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance, mCallback);
27
28
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSpeechTask)
29
0
  NS_INTERFACE_MAP_ENTRY(nsISpeechTask)
30
0
  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
31
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
32
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTask)
33
0
NS_INTERFACE_MAP_END
34
35
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSpeechTask)
36
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSpeechTask)
37
38
nsSpeechTask::nsSpeechTask(SpeechSynthesisUtterance* aUtterance, bool aIsChrome)
39
  : mUtterance(aUtterance)
40
  , mInited(false)
41
  , mPrePaused(false)
42
  , mPreCanceled(false)
43
  , mCallback(nullptr)
44
  , mIsChrome(aIsChrome)
45
0
{
46
0
  mText = aUtterance->mText;
47
0
  mVolume = aUtterance->Volume();
48
0
}
49
50
nsSpeechTask::nsSpeechTask(float aVolume, const nsAString& aText, bool aIsChrome)
51
  : mUtterance(nullptr)
52
  , mVolume(aVolume)
53
  , mText(aText)
54
  , mInited(false)
55
  , mPrePaused(false)
56
  , mPreCanceled(false)
57
  , mCallback(nullptr)
58
  , mIsChrome(aIsChrome)
59
0
{
60
0
}
61
62
nsSpeechTask::~nsSpeechTask()
63
0
{
64
0
  LOG(LogLevel::Debug, ("~nsSpeechTask"));
65
0
}
66
67
void
68
nsSpeechTask::Init()
69
0
{
70
0
  mInited = true;
71
0
}
72
73
void
74
nsSpeechTask::SetChosenVoiceURI(const nsAString& aUri)
75
0
{
76
0
  mChosenVoiceURI = aUri;
77
0
}
78
79
NS_IMETHODIMP
80
nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback)
81
0
{
82
0
  MOZ_ASSERT(XRE_IsParentProcess());
83
0
84
0
  LOG(LogLevel::Debug, ("nsSpeechTask::Setup"));
85
0
86
0
  mCallback = aCallback;
87
0
88
0
  return NS_OK;
89
0
}
90
91
NS_IMETHODIMP
92
nsSpeechTask::DispatchStart()
93
0
{
94
0
  nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true);
95
0
  return DispatchStartImpl();
96
0
}
97
98
nsresult
99
nsSpeechTask::DispatchStartImpl()
100
0
{
101
0
  return DispatchStartImpl(mChosenVoiceURI);
102
0
}
103
104
nsresult
105
nsSpeechTask::DispatchStartImpl(const nsAString& aUri)
106
0
{
107
0
  LOG(LogLevel::Debug, ("nsSpeechTask::DispatchStartImpl"));
108
0
109
0
  MOZ_ASSERT(mUtterance);
110
0
  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING))) {
111
0
    return NS_ERROR_NOT_AVAILABLE;
112
0
  }
113
0
114
0
  CreateAudioChannelAgent();
115
0
116
0
  mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
117
0
  mUtterance->mChosenVoiceURI = aUri;
118
0
  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0,
119
0
                                           nullptr, 0, EmptyString());
120
0
121
0
  return NS_OK;
122
0
}
123
124
NS_IMETHODIMP
125
nsSpeechTask::DispatchEnd(float aElapsedTime, uint32_t aCharIndex)
126
0
{
127
0
  // After we end, no callback functions should go through.
128
0
  mCallback = nullptr;
129
0
130
0
  if (!mPreCanceled) {
131
0
    nsSynthVoiceRegistry::GetInstance()->SpeakNext();
132
0
  }
133
0
134
0
  return DispatchEndImpl(aElapsedTime, aCharIndex);
135
0
}
136
137
nsresult
138
nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
139
0
{
140
0
  LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEndImpl"));
141
0
142
0
  DestroyAudioChannelAgent();
143
0
144
0
  MOZ_ASSERT(mUtterance);
145
0
  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
146
0
    return NS_ERROR_NOT_AVAILABLE;
147
0
  }
148
0
149
0
  RefPtr<SpeechSynthesisUtterance> utterance = mUtterance;
150
0
151
0
  if (mSpeechSynthesis) {
152
0
    mSpeechSynthesis->OnEnd(this);
153
0
  }
154
0
155
0
  if (utterance->mState == SpeechSynthesisUtterance::STATE_PENDING) {
156
0
    utterance->mState = SpeechSynthesisUtterance::STATE_NONE;
157
0
  } else {
158
0
    utterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
159
0
    utterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("end"),
160
0
                                            aCharIndex, nullptr, aElapsedTime,
161
0
                                            EmptyString());
162
0
  }
163
0
164
0
  return NS_OK;
165
0
}
166
167
NS_IMETHODIMP
168
nsSpeechTask::DispatchPause(float aElapsedTime, uint32_t aCharIndex)
169
0
{
170
0
  return DispatchPauseImpl(aElapsedTime, aCharIndex);
171
0
}
172
173
nsresult
174
nsSpeechTask::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
175
0
{
176
0
  LOG(LogLevel::Debug, ("nsSpeechTask::DispatchPauseImpl"));
177
0
  MOZ_ASSERT(mUtterance);
178
0
  if(NS_WARN_IF(mUtterance->mPaused)) {
179
0
    return NS_ERROR_NOT_AVAILABLE;
180
0
  }
181
0
  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
182
0
    return NS_ERROR_NOT_AVAILABLE;
183
0
  }
184
0
185
0
  mUtterance->mPaused = true;
186
0
  if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
187
0
    mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
188
0
                                             aCharIndex, nullptr, aElapsedTime,
189
0
                                             EmptyString());
190
0
  }
191
0
  return NS_OK;
192
0
}
193
194
NS_IMETHODIMP
195
nsSpeechTask::DispatchResume(float aElapsedTime, uint32_t aCharIndex)
196
0
{
197
0
  return DispatchResumeImpl(aElapsedTime, aCharIndex);
198
0
}
199
200
nsresult
201
nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
202
0
{
203
0
  LOG(LogLevel::Debug, ("nsSpeechTask::DispatchResumeImpl"));
204
0
  MOZ_ASSERT(mUtterance);
205
0
  if(NS_WARN_IF(!(mUtterance->mPaused))) {
206
0
    return NS_ERROR_NOT_AVAILABLE;
207
0
  }
208
0
  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
209
0
    return NS_ERROR_NOT_AVAILABLE;
210
0
  }
211
0
212
0
  mUtterance->mPaused = false;
213
0
  if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
214
0
    mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
215
0
                                             aCharIndex, nullptr, aElapsedTime,
216
0
                                             EmptyString());
217
0
  }
218
0
219
0
  return NS_OK;
220
0
}
221
222
void
223
nsSpeechTask::ForceError(float aElapsedTime, uint32_t aCharIndex)
224
0
{
225
0
  DispatchError(aElapsedTime, aCharIndex);
226
0
}
227
228
NS_IMETHODIMP
229
nsSpeechTask::DispatchError(float aElapsedTime, uint32_t aCharIndex)
230
0
{
231
0
  LOG(LogLevel::Debug, ("nsSpeechTask::DispatchError"));
232
0
233
0
  if (!mPreCanceled) {
234
0
    nsSynthVoiceRegistry::GetInstance()->SpeakNext();
235
0
  }
236
0
237
0
  return DispatchErrorImpl(aElapsedTime, aCharIndex);
238
0
}
239
240
nsresult
241
nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
242
0
{
243
0
  MOZ_ASSERT(mUtterance);
244
0
  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
245
0
    return NS_ERROR_NOT_AVAILABLE;
246
0
  }
247
0
248
0
  if (mSpeechSynthesis) {
249
0
    mSpeechSynthesis->OnEnd(this);
250
0
  }
251
0
252
0
  mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
253
0
  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
254
0
                                           aCharIndex, nullptr, aElapsedTime,
255
0
                                           EmptyString());
256
0
  return NS_OK;
257
0
}
258
259
NS_IMETHODIMP
260
nsSpeechTask::DispatchBoundary(const nsAString& aName,
261
                               float aElapsedTime, uint32_t aCharIndex,
262
                               uint32_t aCharLength, uint8_t argc)
263
0
{
264
0
  return DispatchBoundaryImpl(aName, aElapsedTime, aCharIndex, aCharLength, argc);
265
0
}
266
267
nsresult
268
nsSpeechTask::DispatchBoundaryImpl(const nsAString& aName,
269
                                   float aElapsedTime, uint32_t aCharIndex,
270
                                   uint32_t aCharLength, uint8_t argc)
271
0
{
272
0
  MOZ_ASSERT(mUtterance);
273
0
  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
274
0
    return NS_ERROR_NOT_AVAILABLE;
275
0
  }
276
0
  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("boundary"),
277
0
                                           aCharIndex,
278
0
                                           argc ? static_cast<Nullable<uint32_t> >(aCharLength) : nullptr,
279
0
                                           aElapsedTime, aName);
280
0
281
0
  return NS_OK;
282
0
}
283
284
NS_IMETHODIMP
285
nsSpeechTask::DispatchMark(const nsAString& aName,
286
                           float aElapsedTime, uint32_t aCharIndex)
287
0
{
288
0
  return DispatchMarkImpl(aName, aElapsedTime, aCharIndex);
289
0
}
290
291
nsresult
292
nsSpeechTask::DispatchMarkImpl(const nsAString& aName,
293
                               float aElapsedTime, uint32_t aCharIndex)
294
0
{
295
0
  MOZ_ASSERT(mUtterance);
296
0
  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
297
0
    return NS_ERROR_NOT_AVAILABLE;
298
0
  }
299
0
300
0
  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("mark"),
301
0
                                           aCharIndex, nullptr, aElapsedTime,
302
0
                                           aName);
303
0
  return NS_OK;
304
0
}
305
306
void
307
nsSpeechTask::Pause()
308
0
{
309
0
  MOZ_ASSERT(XRE_IsParentProcess());
310
0
311
0
  if (mCallback) {
312
0
    DebugOnly<nsresult> rv = mCallback->OnPause();
313
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to call onPause() callback");
314
0
  }
315
0
316
0
  if (!mInited) {
317
0
    mPrePaused = true;
318
0
  }
319
0
}
320
321
void
322
nsSpeechTask::Resume()
323
0
{
324
0
  MOZ_ASSERT(XRE_IsParentProcess());
325
0
326
0
  if (mCallback) {
327
0
    DebugOnly<nsresult> rv = mCallback->OnResume();
328
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
329
0
                         "Unable to call onResume() callback");
330
0
  }
331
0
332
0
  if (mPrePaused) {
333
0
    mPrePaused = false;
334
0
    nsSynthVoiceRegistry::GetInstance()->ResumeQueue();
335
0
  }
336
0
}
337
338
void
339
nsSpeechTask::Cancel()
340
0
{
341
0
  MOZ_ASSERT(XRE_IsParentProcess());
342
0
343
0
  LOG(LogLevel::Debug, ("nsSpeechTask::Cancel"));
344
0
345
0
  if (mCallback) {
346
0
    DebugOnly<nsresult> rv = mCallback->OnCancel();
347
0
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
348
0
                         "Unable to call onCancel() callback");
349
0
  }
350
0
351
0
  if (!mInited) {
352
0
    mPreCanceled = true;
353
0
  }
354
0
}
355
356
void
357
nsSpeechTask::ForceEnd()
358
0
{
359
0
  if (!mInited) {
360
0
    mPreCanceled = true;
361
0
  }
362
0
363
0
  DispatchEnd(0, 0);
364
0
}
365
366
void
367
nsSpeechTask::SetSpeechSynthesis(SpeechSynthesis* aSpeechSynthesis)
368
0
{
369
0
  mSpeechSynthesis = aSpeechSynthesis;
370
0
}
371
372
void
373
nsSpeechTask::CreateAudioChannelAgent()
374
0
{
375
0
  if (!mUtterance) {
376
0
    return;
377
0
  }
378
0
379
0
  if (mAudioChannelAgent) {
380
0
    mAudioChannelAgent->NotifyStoppedPlaying();
381
0
  }
382
0
383
0
  mAudioChannelAgent = new AudioChannelAgent();
384
0
  mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(), this);
385
0
386
0
  AudioPlaybackConfig config;
387
0
  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
388
0
                                                         AudioChannelService::AudibleState::eAudible);
389
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
390
0
    return;
391
0
  }
392
0
393
0
  WindowVolumeChanged(config.mVolume, config.mMuted);
394
0
  WindowSuspendChanged(config.mSuspend);
395
0
}
396
397
void
398
nsSpeechTask::DestroyAudioChannelAgent()
399
0
{
400
0
  if (mAudioChannelAgent) {
401
0
    mAudioChannelAgent->NotifyStoppedPlaying();
402
0
    mAudioChannelAgent = nullptr;
403
0
  }
404
0
}
405
406
NS_IMETHODIMP
407
nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted)
408
0
{
409
0
  SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
410
0
  return NS_OK;
411
0
}
412
413
NS_IMETHODIMP
414
nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend)
415
0
{
416
0
  if (!mUtterance) {
417
0
    return NS_OK;
418
0
  }
419
0
420
0
  if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED &&
421
0
      mUtterance->mPaused) {
422
0
    Resume();
423
0
  } else if (aSuspend != nsISuspendedTypes::NONE_SUSPENDED &&
424
0
             !mUtterance->mPaused) {
425
0
    Pause();
426
0
  }
427
0
  return NS_OK;
428
0
}
429
430
NS_IMETHODIMP
431
nsSpeechTask::WindowAudioCaptureChanged(bool aCapture)
432
0
{
433
0
  // This is not supported yet.
434
0
  return NS_OK;
435
0
}
436
437
void
438
nsSpeechTask::SetAudioOutputVolume(float aVolume)
439
0
{
440
0
  if (mCallback) {
441
0
    mCallback->OnVolumeChanged(aVolume);
442
0
  }
443
0
}
444
445
} // namespace dom
446
} // namespace mozilla