LCOV - code coverage report
Current view: top level - dom/media/webspeech/synth - nsSpeechTask.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 378 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 55 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "MediaStreamListener.h"
      11             : #include "nsSpeechTask.h"
      12             : #include "nsSynthVoiceRegistry.h"
      13             : #include "SharedBuffer.h"
      14             : #include "SpeechSynthesis.h"
      15             : 
      16             : // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
      17             : // GetTickCount() and conflicts with nsSpeechTask::GetCurrentTime().
      18             : #ifdef GetCurrentTime
      19             : #undef GetCurrentTime
      20             : #endif
      21             : 
      22             : #undef LOG
      23             : extern mozilla::LogModule* GetSpeechSynthLog();
      24             : #define LOG(type, msg) MOZ_LOG(GetSpeechSynthLog(), type, msg)
      25             : 
      26             : #define AUDIO_TRACK 1
      27             : 
      28             : namespace mozilla {
      29             : namespace dom {
      30             : 
      31           0 : class SynthStreamListener : public MediaStreamListener
      32             : {
      33             : public:
      34           0 :   SynthStreamListener(nsSpeechTask* aSpeechTask,
      35             :                       MediaStream* aStream,
      36             :                       AbstractThread* aMainThread)
      37           0 :     : mSpeechTask(aSpeechTask)
      38             :     , mStream(aStream)
      39           0 :     , mStarted(false)
      40             :   {
      41           0 :   }
      42             : 
      43           0 :   void DoNotifyStarted()
      44             :   {
      45           0 :     if (mSpeechTask) {
      46           0 :       mSpeechTask->DispatchStartInner();
      47             :     }
      48           0 :   }
      49             : 
      50           0 :   void DoNotifyFinished()
      51             :   {
      52           0 :     if (mSpeechTask) {
      53           0 :       mSpeechTask->DispatchEndInner(mSpeechTask->GetCurrentTime(),
      54           0 :                                     mSpeechTask->GetCurrentCharOffset());
      55             :     }
      56           0 :   }
      57             : 
      58           0 :   void NotifyEvent(MediaStreamGraph* aGraph,
      59             :                    MediaStreamGraphEvent event) override
      60             :   {
      61           0 :     switch (event) {
      62             :       case MediaStreamGraphEvent::EVENT_FINISHED:
      63             :         {
      64           0 :           if (!mStarted) {
      65           0 :             mStarted = true;
      66           0 :             aGraph->DispatchToMainThreadAfterStreamStateUpdate(
      67           0 :               NewRunnableMethod("dom::SynthStreamListener::DoNotifyStarted",
      68             :                                 this,
      69           0 :                                 &SynthStreamListener::DoNotifyStarted));
      70             :           }
      71             : 
      72           0 :           aGraph->DispatchToMainThreadAfterStreamStateUpdate(
      73           0 :             NewRunnableMethod("dom::SynthStreamListener::DoNotifyFinished",
      74             :                               this,
      75           0 :                               &SynthStreamListener::DoNotifyFinished));
      76             :         }
      77           0 :         break;
      78             :       case MediaStreamGraphEvent::EVENT_REMOVED:
      79           0 :         mSpeechTask = nullptr;
      80             :         // Dereference MediaStream to destroy safety
      81           0 :         mStream = nullptr;
      82           0 :         break;
      83             :       default:
      84           0 :         break;
      85             :     }
      86           0 :   }
      87             : 
      88           0 :   void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) override
      89             :   {
      90           0 :     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
      91           0 :       mStarted = true;
      92           0 :       aGraph->DispatchToMainThreadAfterStreamStateUpdate(
      93           0 :         NewRunnableMethod("dom::SynthStreamListener::DoNotifyStarted",
      94             :                           this,
      95           0 :                           &SynthStreamListener::DoNotifyStarted));
      96             :     }
      97           0 :   }
      98             : 
      99             : private:
     100             :   // Raw pointer; if we exist, the stream exists,
     101             :   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
     102             :   nsSpeechTask* mSpeechTask;
     103             :   // This is KungFuDeathGrip for MediaStream
     104             :   RefPtr<MediaStream> mStream;
     105             : 
     106             :   bool mStarted;
     107             : };
     108             : 
     109             : // nsSpeechTask
     110             : 
     111           0 : NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance, mCallback);
     112             : 
     113           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSpeechTask)
     114           0 :   NS_INTERFACE_MAP_ENTRY(nsISpeechTask)
     115           0 :   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
     116           0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     117           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTask)
     118           0 : NS_INTERFACE_MAP_END
     119             : 
     120           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSpeechTask)
     121           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSpeechTask)
     122             : 
     123           0 : nsSpeechTask::nsSpeechTask(SpeechSynthesisUtterance* aUtterance)
     124             :   : mUtterance(aUtterance)
     125             :   , mInited(false)
     126             :   , mPrePaused(false)
     127             :   , mPreCanceled(false)
     128             :   , mCallback(nullptr)
     129           0 :   , mIndirectAudio(false)
     130             : {
     131           0 :   mText = aUtterance->mText;
     132           0 :   mVolume = aUtterance->Volume();
     133           0 : }
     134             : 
     135           0 : nsSpeechTask::nsSpeechTask(float aVolume, const nsAString& aText)
     136             :   : mUtterance(nullptr)
     137             :   , mVolume(aVolume)
     138             :   , mText(aText)
     139             :   , mInited(false)
     140             :   , mPrePaused(false)
     141             :   , mPreCanceled(false)
     142             :   , mCallback(nullptr)
     143           0 :   , mIndirectAudio(false)
     144             : {
     145           0 : }
     146             : 
     147           0 : nsSpeechTask::~nsSpeechTask()
     148             : {
     149           0 :   LOG(LogLevel::Debug, ("~nsSpeechTask"));
     150           0 :   if (mStream) {
     151           0 :     if (!mStream->IsDestroyed()) {
     152           0 :       mStream->Destroy();
     153             :     }
     154             : 
     155             :     // This will finally destroyed by SynthStreamListener becasue
     156             :     // MediaStream::Destroy() is async.
     157           0 :     mStream = nullptr;
     158             :   }
     159             : 
     160           0 :   if (mPort) {
     161           0 :     mPort->Destroy();
     162           0 :     mPort = nullptr;
     163             :   }
     164           0 : }
     165             : 
     166             : void
     167           0 : nsSpeechTask::InitDirectAudio()
     168             : {
     169             :   // nullptr as final argument here means that this is not tied to a window.
     170             :   // This is a global MSG.
     171             :   mStream = MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
     172             :                                           AudioChannel::Normal, nullptr)->
     173           0 :     CreateSourceStream();
     174           0 :   mIndirectAudio = false;
     175           0 :   mInited = true;
     176           0 : }
     177             : 
     178             : void
     179           0 : nsSpeechTask::InitIndirectAudio()
     180             : {
     181           0 :   mIndirectAudio = true;
     182           0 :   mInited = true;
     183           0 : }
     184             : 
     185             : void
     186           0 : nsSpeechTask::SetChosenVoiceURI(const nsAString& aUri)
     187             : {
     188           0 :   mChosenVoiceURI = aUri;
     189           0 : }
     190             : 
     191             : NS_IMETHODIMP
     192           0 : nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback,
     193             :                     uint32_t aChannels, uint32_t aRate, uint8_t argc)
     194             : {
     195           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     196             : 
     197           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::Setup"));
     198             : 
     199           0 :   mCallback = aCallback;
     200             : 
     201           0 :   if (mIndirectAudio) {
     202           0 :     MOZ_ASSERT(!mStream);
     203           0 :     if (argc > 0) {
     204           0 :       NS_WARNING("Audio info arguments in Setup() are ignored for indirect audio services.");
     205             :     }
     206           0 :     return NS_OK;
     207             :   }
     208             : 
     209             :   // mStream is set up in Init() that should be called before this.
     210           0 :   MOZ_ASSERT(mStream);
     211             : 
     212           0 :   mStream->AddListener(
     213             :     // Non DocGroup-version of AbstractThread::MainThread for the task in parent.
     214           0 :     new SynthStreamListener(this, mStream, AbstractThread::MainThread()));
     215             : 
     216             :   // XXX: Support more than one channel
     217           0 :   if(NS_WARN_IF(!(aChannels == 1))) {
     218           0 :     return NS_ERROR_FAILURE;
     219             :   }
     220             : 
     221           0 :   mChannels = aChannels;
     222             : 
     223           0 :   AudioSegment* segment = new AudioSegment();
     224           0 :   mStream->AddAudioTrack(AUDIO_TRACK, aRate, 0, segment);
     225           0 :   mStream->AddAudioOutput(this);
     226           0 :   mStream->SetAudioOutputVolume(this, mVolume);
     227             : 
     228           0 :   return NS_OK;
     229             : }
     230             : 
     231             : static RefPtr<mozilla::SharedBuffer>
     232           0 : makeSamples(int16_t* aData, uint32_t aDataLen)
     233             : {
     234             :   RefPtr<mozilla::SharedBuffer> samples =
     235           0 :     SharedBuffer::Create(aDataLen * sizeof(int16_t));
     236           0 :   int16_t* frames = static_cast<int16_t*>(samples->Data());
     237             : 
     238           0 :   for (uint32_t i = 0; i < aDataLen; i++) {
     239           0 :     frames[i] = aData[i];
     240             :   }
     241             : 
     242           0 :   return samples;
     243             : }
     244             : 
     245             : NS_IMETHODIMP
     246           0 : nsSpeechTask::SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLandmarks,
     247             :                         JSContext* aCx)
     248             : {
     249           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     250             : 
     251           0 :   if(NS_WARN_IF(!(mStream))) {
     252           0 :     return NS_ERROR_NOT_AVAILABLE;
     253             :   }
     254           0 :   if(NS_WARN_IF(mStream->IsDestroyed())) {
     255           0 :     return NS_ERROR_NOT_AVAILABLE;
     256             :   }
     257           0 :   if(NS_WARN_IF(!(mChannels))) {
     258           0 :     return NS_ERROR_FAILURE;
     259             :   }
     260           0 :   if(NS_WARN_IF(!(aData.isObject()))) {
     261           0 :     return NS_ERROR_INVALID_ARG;
     262             :   }
     263             : 
     264           0 :   if (mIndirectAudio) {
     265           0 :     NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
     266           0 :     return NS_ERROR_FAILURE;
     267             :   }
     268             : 
     269           0 :   JS::Rooted<JSObject*> darray(aCx, &aData.toObject());
     270           0 :   JSAutoCompartment ac(aCx, darray);
     271             : 
     272           0 :   JS::Rooted<JSObject*> tsrc(aCx, nullptr);
     273             : 
     274             :   // Allow either Int16Array or plain JS Array
     275           0 :   if (JS_IsInt16Array(darray)) {
     276           0 :     tsrc = darray;
     277             :   } else {
     278             :     bool isArray;
     279           0 :     if (!JS_IsArrayObject(aCx, darray, &isArray)) {
     280           0 :       return NS_ERROR_UNEXPECTED;
     281             :     }
     282           0 :     if (isArray) {
     283           0 :       tsrc = JS_NewInt16ArrayFromArray(aCx, darray);
     284             :     }
     285             :   }
     286             : 
     287           0 :   if (!tsrc) {
     288           0 :     return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
     289             :   }
     290             : 
     291           0 :   uint32_t dataLen = JS_GetTypedArrayLength(tsrc);
     292           0 :   RefPtr<mozilla::SharedBuffer> samples;
     293             :   {
     294           0 :     JS::AutoCheckCannotGC nogc;
     295             :     bool isShared;
     296           0 :     int16_t* data = JS_GetInt16ArrayData(tsrc, &isShared, nogc);
     297           0 :     if (isShared) {
     298             :       // Must opt in to using shared data.
     299           0 :       return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
     300             :     }
     301           0 :     samples = makeSamples(data, dataLen);
     302             :   }
     303           0 :   SendAudioImpl(samples, dataLen);
     304             : 
     305           0 :   return NS_OK;
     306             : }
     307             : 
     308             : NS_IMETHODIMP
     309           0 : nsSpeechTask::SendAudioNative(int16_t* aData, uint32_t aDataLen)
     310             : {
     311           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     312             : 
     313           0 :   if(NS_WARN_IF(!(mStream))) {
     314           0 :     return NS_ERROR_NOT_AVAILABLE;
     315             :   }
     316           0 :   if(NS_WARN_IF(mStream->IsDestroyed())) {
     317           0 :     return NS_ERROR_NOT_AVAILABLE;
     318             :   }
     319           0 :   if(NS_WARN_IF(!(mChannels))) {
     320           0 :     return NS_ERROR_FAILURE;
     321             :   }
     322             : 
     323           0 :   if (mIndirectAudio) {
     324           0 :     NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
     325           0 :     return NS_ERROR_FAILURE;
     326             :   }
     327             : 
     328           0 :   RefPtr<mozilla::SharedBuffer> samples = makeSamples(aData, aDataLen);
     329           0 :   SendAudioImpl(samples, aDataLen);
     330             : 
     331           0 :   return NS_OK;
     332             : }
     333             : 
     334             : void
     335           0 : nsSpeechTask::SendAudioImpl(RefPtr<mozilla::SharedBuffer>& aSamples, uint32_t aDataLen)
     336             : {
     337           0 :   if (aDataLen == 0) {
     338           0 :     mStream->EndAllTrackAndFinish();
     339           0 :     return;
     340             :   }
     341             : 
     342           0 :   AudioSegment segment;
     343           0 :   AutoTArray<const int16_t*, 1> channelData;
     344           0 :   channelData.AppendElement(static_cast<int16_t*>(aSamples->Data()));
     345           0 :   segment.AppendFrames(aSamples.forget(), channelData, aDataLen,
     346           0 :                        PRINCIPAL_HANDLE_NONE);
     347           0 :   mStream->AppendToTrack(1, &segment);
     348           0 :   mStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
     349             : }
     350             : 
     351             : NS_IMETHODIMP
     352           0 : nsSpeechTask::DispatchStart()
     353             : {
     354           0 :   if (!mIndirectAudio) {
     355           0 :     NS_WARNING("Can't call DispatchStart() from a direct audio speech service");
     356           0 :     return NS_ERROR_FAILURE;
     357             :   }
     358             : 
     359           0 :   return DispatchStartInner();
     360             : }
     361             : 
     362             : nsresult
     363           0 : nsSpeechTask::DispatchStartInner()
     364             : {
     365           0 :   nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true);
     366           0 :   return DispatchStartImpl();
     367             : }
     368             : 
     369             : nsresult
     370           0 : nsSpeechTask::DispatchStartImpl()
     371             : {
     372           0 :   return DispatchStartImpl(mChosenVoiceURI);
     373             : }
     374             : 
     375             : nsresult
     376           0 : nsSpeechTask::DispatchStartImpl(const nsAString& aUri)
     377             : {
     378           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchStart"));
     379             : 
     380           0 :   MOZ_ASSERT(mUtterance);
     381           0 :   if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING))) {
     382           0 :     return NS_ERROR_NOT_AVAILABLE;
     383             :   }
     384             : 
     385           0 :   CreateAudioChannelAgent();
     386             : 
     387           0 :   mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
     388           0 :   mUtterance->mChosenVoiceURI = aUri;
     389           0 :   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0,
     390           0 :                                            nullptr, 0, EmptyString());
     391             : 
     392           0 :   return NS_OK;
     393             : }
     394             : 
     395             : NS_IMETHODIMP
     396           0 : nsSpeechTask::DispatchEnd(float aElapsedTime, uint32_t aCharIndex)
     397             : {
     398           0 :   if (!mIndirectAudio) {
     399           0 :     NS_WARNING("Can't call DispatchEnd() from a direct audio speech service");
     400           0 :     return NS_ERROR_FAILURE;
     401             :   }
     402             : 
     403           0 :   return DispatchEndInner(aElapsedTime, aCharIndex);
     404             : }
     405             : 
     406             : nsresult
     407           0 : nsSpeechTask::DispatchEndInner(float aElapsedTime, uint32_t aCharIndex)
     408             : {
     409           0 :   if (!mPreCanceled) {
     410           0 :     nsSynthVoiceRegistry::GetInstance()->SpeakNext();
     411             :   }
     412             : 
     413           0 :   return DispatchEndImpl(aElapsedTime, aCharIndex);
     414             : }
     415             : 
     416             : nsresult
     417           0 : nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
     418             : {
     419           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n"));
     420             : 
     421           0 :   DestroyAudioChannelAgent();
     422             : 
     423           0 :   MOZ_ASSERT(mUtterance);
     424           0 :   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     425           0 :     return NS_ERROR_NOT_AVAILABLE;
     426             :   }
     427             : 
     428             :   // XXX: This should not be here, but it prevents a crash in MSG.
     429           0 :   if (mStream) {
     430           0 :     mStream->Destroy();
     431             :   }
     432             : 
     433           0 :   RefPtr<SpeechSynthesisUtterance> utterance = mUtterance;
     434             : 
     435           0 :   if (mSpeechSynthesis) {
     436           0 :     mSpeechSynthesis->OnEnd(this);
     437             :   }
     438             : 
     439           0 :   if (utterance->mState == SpeechSynthesisUtterance::STATE_PENDING) {
     440           0 :     utterance->mState = SpeechSynthesisUtterance::STATE_NONE;
     441             :   } else {
     442           0 :     utterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
     443           0 :     utterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("end"),
     444             :                                             aCharIndex, nullptr, aElapsedTime,
     445           0 :                                             EmptyString());
     446             :   }
     447             : 
     448           0 :   return NS_OK;
     449             : }
     450             : 
     451             : NS_IMETHODIMP
     452           0 : nsSpeechTask::DispatchPause(float aElapsedTime, uint32_t aCharIndex)
     453             : {
     454           0 :   if (!mIndirectAudio) {
     455           0 :     NS_WARNING("Can't call DispatchPause() from a direct audio speech service");
     456           0 :     return NS_ERROR_FAILURE;
     457             :   }
     458             : 
     459           0 :   return DispatchPauseImpl(aElapsedTime, aCharIndex);
     460             : }
     461             : 
     462             : nsresult
     463           0 : nsSpeechTask::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
     464             : {
     465           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchPause"));
     466           0 :   MOZ_ASSERT(mUtterance);
     467           0 :   if(NS_WARN_IF(mUtterance->mPaused)) {
     468           0 :     return NS_ERROR_NOT_AVAILABLE;
     469             :   }
     470           0 :   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     471           0 :     return NS_ERROR_NOT_AVAILABLE;
     472             :   }
     473             : 
     474           0 :   mUtterance->mPaused = true;
     475           0 :   if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
     476           0 :     mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
     477             :                                              aCharIndex, nullptr, aElapsedTime,
     478           0 :                                              EmptyString());
     479             :   }
     480           0 :   return NS_OK;
     481             : }
     482             : 
     483             : NS_IMETHODIMP
     484           0 : nsSpeechTask::DispatchResume(float aElapsedTime, uint32_t aCharIndex)
     485             : {
     486           0 :   if (!mIndirectAudio) {
     487           0 :     NS_WARNING("Can't call DispatchResume() from a direct audio speech service");
     488           0 :     return NS_ERROR_FAILURE;
     489             :   }
     490             : 
     491           0 :   return DispatchResumeImpl(aElapsedTime, aCharIndex);
     492             : }
     493             : 
     494             : nsresult
     495           0 : nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
     496             : {
     497           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchResume"));
     498           0 :   MOZ_ASSERT(mUtterance);
     499           0 :   if(NS_WARN_IF(!(mUtterance->mPaused))) {
     500           0 :     return NS_ERROR_NOT_AVAILABLE;
     501             :   }
     502           0 :   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     503           0 :     return NS_ERROR_NOT_AVAILABLE;
     504             :   }
     505             : 
     506           0 :   mUtterance->mPaused = false;
     507           0 :   if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
     508           0 :     mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
     509             :                                              aCharIndex, nullptr, aElapsedTime,
     510           0 :                                              EmptyString());
     511             :   }
     512             : 
     513           0 :   return NS_OK;
     514             : }
     515             : 
     516             : NS_IMETHODIMP
     517           0 : nsSpeechTask::DispatchError(float aElapsedTime, uint32_t aCharIndex)
     518             : {
     519           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchError"));
     520             : 
     521           0 :   if (!mIndirectAudio) {
     522           0 :     NS_WARNING("Can't call DispatchError() from a direct audio speech service");
     523           0 :     return NS_ERROR_FAILURE;
     524             :   }
     525             : 
     526           0 :   if (!mPreCanceled) {
     527           0 :     nsSynthVoiceRegistry::GetInstance()->SpeakNext();
     528             :   }
     529             : 
     530           0 :   return DispatchErrorImpl(aElapsedTime, aCharIndex);
     531             : }
     532             : 
     533             : nsresult
     534           0 : nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
     535             : {
     536           0 :   MOZ_ASSERT(mUtterance);
     537           0 :   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     538           0 :     return NS_ERROR_NOT_AVAILABLE;
     539             :   }
     540             : 
     541           0 :   if (mSpeechSynthesis) {
     542           0 :     mSpeechSynthesis->OnEnd(this);
     543             :   }
     544             : 
     545           0 :   mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
     546           0 :   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
     547             :                                            aCharIndex, nullptr, aElapsedTime,
     548           0 :                                            EmptyString());
     549           0 :   return NS_OK;
     550             : }
     551             : 
     552             : NS_IMETHODIMP
     553           0 : nsSpeechTask::DispatchBoundary(const nsAString& aName,
     554             :                                float aElapsedTime, uint32_t aCharIndex,
     555             :                                uint32_t aCharLength, uint8_t argc)
     556             : {
     557           0 :   if (!mIndirectAudio) {
     558           0 :     NS_WARNING("Can't call DispatchBoundary() from a direct audio speech service");
     559           0 :     return NS_ERROR_FAILURE;
     560             :   }
     561             : 
     562           0 :   return DispatchBoundaryImpl(aName, aElapsedTime, aCharIndex, aCharLength, argc);
     563             : }
     564             : 
     565             : nsresult
     566           0 : nsSpeechTask::DispatchBoundaryImpl(const nsAString& aName,
     567             :                                    float aElapsedTime, uint32_t aCharIndex,
     568             :                                    uint32_t aCharLength, uint8_t argc)
     569             : {
     570           0 :   MOZ_ASSERT(mUtterance);
     571           0 :   if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
     572           0 :     return NS_ERROR_NOT_AVAILABLE;
     573             :   }
     574           0 :   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("boundary"),
     575             :                                            aCharIndex,
     576           0 :                                            argc ? static_cast<Nullable<uint32_t> >(aCharLength) : nullptr,
     577           0 :                                            aElapsedTime, aName);
     578             : 
     579           0 :   return NS_OK;
     580             : }
     581             : 
     582             : NS_IMETHODIMP
     583           0 : nsSpeechTask::DispatchMark(const nsAString& aName,
     584             :                            float aElapsedTime, uint32_t aCharIndex)
     585             : {
     586           0 :   if (!mIndirectAudio) {
     587           0 :     NS_WARNING("Can't call DispatchMark() from a direct audio speech service");
     588           0 :     return NS_ERROR_FAILURE;
     589             :   }
     590             : 
     591           0 :   return DispatchMarkImpl(aName, aElapsedTime, aCharIndex);
     592             : }
     593             : 
     594             : nsresult
     595           0 : nsSpeechTask::DispatchMarkImpl(const nsAString& aName,
     596             :                                float aElapsedTime, uint32_t aCharIndex)
     597             : {
     598           0 :   MOZ_ASSERT(mUtterance);
     599           0 :   if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
     600           0 :     return NS_ERROR_NOT_AVAILABLE;
     601             :   }
     602             : 
     603           0 :   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("mark"),
     604             :                                            aCharIndex, nullptr, aElapsedTime,
     605           0 :                                            aName);
     606           0 :   return NS_OK;
     607             : }
     608             : 
     609             : void
     610           0 : nsSpeechTask::Pause()
     611             : {
     612           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     613             : 
     614           0 :   if (mCallback) {
     615           0 :     DebugOnly<nsresult> rv = mCallback->OnPause();
     616           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to call onPause() callback");
     617             :   }
     618             : 
     619           0 :   if (mStream) {
     620           0 :     mStream->Suspend();
     621             :   }
     622             : 
     623           0 :   if (!mInited) {
     624           0 :     mPrePaused = true;
     625             :   }
     626             : 
     627           0 :   if (!mIndirectAudio) {
     628           0 :     DispatchPauseImpl(GetCurrentTime(), GetCurrentCharOffset());
     629             :   }
     630           0 : }
     631             : 
     632             : void
     633           0 : nsSpeechTask::Resume()
     634             : {
     635           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     636             : 
     637           0 :   if (mCallback) {
     638           0 :     DebugOnly<nsresult> rv = mCallback->OnResume();
     639           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     640             :                          "Unable to call onResume() callback");
     641             :   }
     642             : 
     643           0 :   if (mStream) {
     644           0 :     mStream->Resume();
     645             :   }
     646             : 
     647           0 :   if (mPrePaused) {
     648           0 :     mPrePaused = false;
     649           0 :     nsSynthVoiceRegistry::GetInstance()->ResumeQueue();
     650             :   }
     651             : 
     652           0 :   if (!mIndirectAudio) {
     653           0 :     DispatchResumeImpl(GetCurrentTime(), GetCurrentCharOffset());
     654             :   }
     655           0 : }
     656             : 
     657             : void
     658           0 : nsSpeechTask::Cancel()
     659             : {
     660           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     661             : 
     662           0 :   LOG(LogLevel::Debug, ("nsSpeechTask::Cancel"));
     663             : 
     664           0 :   if (mCallback) {
     665           0 :     DebugOnly<nsresult> rv = mCallback->OnCancel();
     666           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     667             :                          "Unable to call onCancel() callback");
     668             :   }
     669             : 
     670           0 :   if (mStream) {
     671           0 :     mStream->Suspend();
     672             :   }
     673             : 
     674           0 :   if (!mInited) {
     675           0 :     mPreCanceled = true;
     676             :   }
     677             : 
     678           0 :   if (!mIndirectAudio) {
     679           0 :     DispatchEndInner(GetCurrentTime(), GetCurrentCharOffset());
     680             :   }
     681           0 : }
     682             : 
     683             : void
     684           0 : nsSpeechTask::ForceEnd()
     685             : {
     686           0 :   if (mStream) {
     687           0 :     mStream->Suspend();
     688             :   }
     689             : 
     690           0 :   if (!mInited) {
     691           0 :     mPreCanceled = true;
     692             :   }
     693             : 
     694           0 :   DispatchEndInner(GetCurrentTime(), GetCurrentCharOffset());
     695           0 : }
     696             : 
     697             : float
     698           0 : nsSpeechTask::GetCurrentTime()
     699             : {
     700           0 :   return mStream ? (float)(mStream->GetCurrentTime() / 1000000.0) : 0;
     701             : }
     702             : 
     703             : uint32_t
     704           0 : nsSpeechTask::GetCurrentCharOffset()
     705             : {
     706           0 :   return mStream && mStream->IsFinished() ? mText.Length() : 0;
     707             : }
     708             : 
     709             : void
     710           0 : nsSpeechTask::SetSpeechSynthesis(SpeechSynthesis* aSpeechSynthesis)
     711             : {
     712           0 :   mSpeechSynthesis = aSpeechSynthesis;
     713           0 : }
     714             : 
     715             : void
     716           0 : nsSpeechTask::CreateAudioChannelAgent()
     717             : {
     718           0 :   if (!mUtterance) {
     719           0 :     return;
     720             :   }
     721             : 
     722           0 :   if (mAudioChannelAgent) {
     723           0 :     mAudioChannelAgent->NotifyStoppedPlaying();
     724             :   }
     725             : 
     726           0 :   mAudioChannelAgent = new AudioChannelAgent();
     727           0 :   mAudioChannelAgent->InitWithWeakCallback(mUtterance->GetOwner(),
     728           0 :                                            static_cast<int32_t>(AudioChannelService::GetDefaultAudioChannel()),
     729           0 :                                            this);
     730             : 
     731           0 :   AudioPlaybackConfig config;
     732           0 :   nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
     733           0 :                                                          AudioChannelService::AudibleState::eAudible);
     734           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     735           0 :     return;
     736             :   }
     737             : 
     738           0 :   WindowVolumeChanged(config.mVolume, config.mMuted);
     739           0 :   WindowSuspendChanged(config.mSuspend);
     740             : }
     741             : 
     742             : void
     743           0 : nsSpeechTask::DestroyAudioChannelAgent()
     744             : {
     745           0 :   if (mAudioChannelAgent) {
     746           0 :     mAudioChannelAgent->NotifyStoppedPlaying();
     747           0 :     mAudioChannelAgent = nullptr;
     748             :   }
     749           0 : }
     750             : 
     751             : NS_IMETHODIMP
     752           0 : nsSpeechTask::WindowVolumeChanged(float aVolume, bool aMuted)
     753             : {
     754           0 :   SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
     755           0 :   return NS_OK;
     756             : }
     757             : 
     758             : NS_IMETHODIMP
     759           0 : nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend)
     760             : {
     761           0 :   if (!mUtterance) {
     762           0 :     return NS_OK;
     763             :   }
     764             : 
     765           0 :   if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED &&
     766           0 :       mUtterance->mPaused) {
     767           0 :     Resume();
     768           0 :   } else if (aSuspend != nsISuspendedTypes::NONE_SUSPENDED &&
     769           0 :              !mUtterance->mPaused) {
     770           0 :     Pause();
     771             :   }
     772           0 :   return NS_OK;
     773             : }
     774             : 
     775             : NS_IMETHODIMP
     776           0 : nsSpeechTask::WindowAudioCaptureChanged(bool aCapture)
     777             : {
     778             :   // This is not supported yet.
     779           0 :   return NS_OK;
     780             : }
     781             : 
     782             : void
     783           0 : nsSpeechTask::SetAudioOutputVolume(float aVolume)
     784             : {
     785           0 :   if (mStream && !mStream->IsDestroyed()) {
     786           0 :     mStream->SetAudioOutputVolume(this, aVolume);
     787             :   }
     788           0 :   if (mIndirectAudio && mCallback) {
     789           0 :     mCallback->OnVolumeChanged(aVolume);
     790             :   }
     791           0 : }
     792             : 
     793             : } // namespace dom
     794             : } // namespace mozilla

Generated by: LCOV version 1.13