1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webspeech/synth/SpeechSynthesis.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,259 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsSpeechTask.h" 1.11 +#include "prlog.h" 1.12 + 1.13 +#include "mozilla/dom/ContentChild.h" 1.14 +#include "mozilla/dom/Element.h" 1.15 + 1.16 +#include "mozilla/dom/SpeechSynthesisBinding.h" 1.17 +#include "SpeechSynthesis.h" 1.18 +#include "nsSynthVoiceRegistry.h" 1.19 +#include "nsIDocument.h" 1.20 + 1.21 +#undef LOG 1.22 +#ifdef PR_LOGGING 1.23 +PRLogModuleInfo* 1.24 +GetSpeechSynthLog() 1.25 +{ 1.26 + static PRLogModuleInfo* sLog = nullptr; 1.27 + 1.28 + if (!sLog) { 1.29 + sLog = PR_NewLogModule("SpeechSynthesis"); 1.30 + } 1.31 + 1.32 + return sLog; 1.33 +} 1.34 +#define LOG(type, msg) PR_LOG(GetSpeechSynthLog(), type, msg) 1.35 +#else 1.36 +#define LOG(type, msg) 1.37 +#endif 1.38 + 1.39 +namespace mozilla { 1.40 +namespace dom { 1.41 + 1.42 +static PLDHashOperator 1.43 +TraverseCachedVoices(const nsAString& aKey, SpeechSynthesisVoice* aEntry, void* aData) 1.44 +{ 1.45 + nsCycleCollectionTraversalCallback* cb = static_cast<nsCycleCollectionTraversalCallback*>(aData); 1.46 + cb->NoteXPCOMChild(aEntry); 1.47 + return PL_DHASH_NEXT; 1.48 +} 1.49 + 1.50 +NS_IMPL_CYCLE_COLLECTION_CLASS(SpeechSynthesis) 1.51 + 1.52 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SpeechSynthesis) 1.53 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) 1.54 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentTask) 1.55 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechQueue) 1.56 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.57 + tmp->mVoiceCache.Clear(); 1.58 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.59 + 1.60 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SpeechSynthesis) 1.61 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 1.62 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentTask) 1.63 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechQueue) 1.64 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.65 + tmp->mVoiceCache.EnumerateRead(TraverseCachedVoices, &cb); 1.66 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.67 + 1.68 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SpeechSynthesis) 1.69 + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1.70 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.71 + 1.72 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechSynthesis) 1.73 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.74 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.75 +NS_INTERFACE_MAP_END 1.76 + 1.77 +NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechSynthesis) 1.78 +NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechSynthesis) 1.79 + 1.80 +SpeechSynthesis::SpeechSynthesis(nsPIDOMWindow* aParent) 1.81 + : mParent(aParent) 1.82 +{ 1.83 + SetIsDOMBinding(); 1.84 +} 1.85 + 1.86 +SpeechSynthesis::~SpeechSynthesis() 1.87 +{ 1.88 +} 1.89 + 1.90 +JSObject* 1.91 +SpeechSynthesis::WrapObject(JSContext* aCx) 1.92 +{ 1.93 + return SpeechSynthesisBinding::Wrap(aCx, this); 1.94 +} 1.95 + 1.96 +nsIDOMWindow* 1.97 +SpeechSynthesis::GetParentObject() const 1.98 +{ 1.99 + return mParent; 1.100 +} 1.101 + 1.102 +bool 1.103 +SpeechSynthesis::Pending() const 1.104 +{ 1.105 + switch (mSpeechQueue.Length()) { 1.106 + case 0: 1.107 + return false; 1.108 + 1.109 + case 1: 1.110 + return mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_PENDING; 1.111 + 1.112 + default: 1.113 + return true; 1.114 + } 1.115 +} 1.116 + 1.117 +bool 1.118 +SpeechSynthesis::Speaking() const 1.119 +{ 1.120 + if (mSpeechQueue.IsEmpty()) { 1.121 + return false; 1.122 + } 1.123 + 1.124 + return mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_SPEAKING; 1.125 +} 1.126 + 1.127 +bool 1.128 +SpeechSynthesis::Paused() const 1.129 +{ 1.130 + if (mSpeechQueue.IsEmpty()) { 1.131 + return false; 1.132 + } 1.133 + 1.134 + return mSpeechQueue.ElementAt(0)->IsPaused(); 1.135 +} 1.136 + 1.137 +void 1.138 +SpeechSynthesis::Speak(SpeechSynthesisUtterance& aUtterance) 1.139 +{ 1.140 + if (aUtterance.mState != SpeechSynthesisUtterance::STATE_NONE) { 1.141 + // XXX: Should probably raise an error 1.142 + return; 1.143 + } 1.144 + 1.145 + mSpeechQueue.AppendElement(&aUtterance); 1.146 + aUtterance.mState = SpeechSynthesisUtterance::STATE_PENDING; 1.147 + 1.148 + if (mSpeechQueue.Length() == 1) { 1.149 + AdvanceQueue(); 1.150 + } 1.151 +} 1.152 + 1.153 +void 1.154 +SpeechSynthesis::AdvanceQueue() 1.155 +{ 1.156 + LOG(PR_LOG_DEBUG, 1.157 + ("SpeechSynthesis::AdvanceQueue length=%d", mSpeechQueue.Length())); 1.158 + 1.159 + if (mSpeechQueue.IsEmpty()) { 1.160 + return; 1.161 + } 1.162 + 1.163 + nsRefPtr<SpeechSynthesisUtterance> utterance = mSpeechQueue.ElementAt(0); 1.164 + 1.165 + nsAutoString docLang; 1.166 + nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mParent); 1.167 + nsIDocument* doc = win->GetExtantDoc(); 1.168 + 1.169 + if (doc) { 1.170 + Element* elm = doc->GetHtmlElement(); 1.171 + 1.172 + if (elm) { 1.173 + elm->GetLang(docLang); 1.174 + } 1.175 + } 1.176 + 1.177 + mCurrentTask = 1.178 + nsSynthVoiceRegistry::GetInstance()->SpeakUtterance(*utterance, docLang); 1.179 + 1.180 + if (mCurrentTask) { 1.181 + mCurrentTask->SetSpeechSynthesis(this); 1.182 + } 1.183 + 1.184 + return; 1.185 +} 1.186 + 1.187 +void 1.188 +SpeechSynthesis::Cancel() 1.189 +{ 1.190 + mSpeechQueue.Clear(); 1.191 + 1.192 + if (mCurrentTask) { 1.193 + mCurrentTask->Cancel(); 1.194 + } 1.195 +} 1.196 + 1.197 +void 1.198 +SpeechSynthesis::Pause() 1.199 +{ 1.200 + if (mCurrentTask) { 1.201 + mCurrentTask->Pause(); 1.202 + } 1.203 +} 1.204 + 1.205 +void 1.206 +SpeechSynthesis::Resume() 1.207 +{ 1.208 + if (mCurrentTask) { 1.209 + mCurrentTask->Resume(); 1.210 + } 1.211 +} 1.212 + 1.213 +void 1.214 +SpeechSynthesis::OnEnd(const nsSpeechTask* aTask) 1.215 +{ 1.216 + MOZ_ASSERT(mCurrentTask == aTask); 1.217 + 1.218 + if (!mSpeechQueue.IsEmpty()) { 1.219 + mSpeechQueue.RemoveElementAt(0); 1.220 + } 1.221 + 1.222 + mCurrentTask = nullptr; 1.223 + AdvanceQueue(); 1.224 +} 1.225 + 1.226 +void 1.227 +SpeechSynthesis::GetVoices(nsTArray< nsRefPtr<SpeechSynthesisVoice> >& aResult) 1.228 +{ 1.229 + aResult.Clear(); 1.230 + uint32_t voiceCount = 0; 1.231 + 1.232 + nsresult rv = nsSynthVoiceRegistry::GetInstance()->GetVoiceCount(&voiceCount); 1.233 + NS_ENSURE_SUCCESS_VOID(rv); 1.234 + 1.235 + for (uint32_t i = 0; i < voiceCount; i++) { 1.236 + nsAutoString uri; 1.237 + rv = nsSynthVoiceRegistry::GetInstance()->GetVoice(i, uri); 1.238 + 1.239 + if (NS_FAILED(rv)) { 1.240 + NS_WARNING("Failed to retrieve voice from registry"); 1.241 + continue; 1.242 + } 1.243 + 1.244 + SpeechSynthesisVoice* voice = mVoiceCache.GetWeak(uri); 1.245 + 1.246 + if (!voice) { 1.247 + voice = new SpeechSynthesisVoice(this, uri); 1.248 + } 1.249 + 1.250 + aResult.AppendElement(voice); 1.251 + } 1.252 + 1.253 + mVoiceCache.Clear(); 1.254 + 1.255 + for (uint32_t i = 0; i < aResult.Length(); i++) { 1.256 + SpeechSynthesisVoice* voice = aResult[i]; 1.257 + mVoiceCache.Put(voice->mUri, voice); 1.258 + } 1.259 +} 1.260 + 1.261 +} // namespace dom 1.262 +} // namespace mozilla