1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webspeech/synth/nsSynthVoiceRegistry.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,577 @@ 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 "nsILocaleService.h" 1.11 +#include "nsISpeechService.h" 1.12 +#include "nsServiceManagerUtils.h" 1.13 + 1.14 +#include "SpeechSynthesisUtterance.h" 1.15 +#include "SpeechSynthesisVoice.h" 1.16 +#include "nsSynthVoiceRegistry.h" 1.17 +#include "nsSpeechTask.h" 1.18 + 1.19 +#include "nsString.h" 1.20 +#include "mozilla/StaticPtr.h" 1.21 +#include "mozilla/dom/ContentChild.h" 1.22 +#include "mozilla/dom/ContentParent.h" 1.23 +#include "mozilla/unused.h" 1.24 + 1.25 +#include "SpeechSynthesisChild.h" 1.26 +#include "SpeechSynthesisParent.h" 1.27 + 1.28 +#undef LOG 1.29 +#ifdef PR_LOGGING 1.30 +extern PRLogModuleInfo* GetSpeechSynthLog(); 1.31 +#define LOG(type, msg) PR_LOG(GetSpeechSynthLog(), type, msg) 1.32 +#else 1.33 +#define LOG(type, msg) 1.34 +#endif 1.35 + 1.36 +namespace { 1.37 + 1.38 +void 1.39 +GetAllSpeechSynthActors(InfallibleTArray<mozilla::dom::SpeechSynthesisParent*>& aActors) 1.40 +{ 1.41 + MOZ_ASSERT(NS_IsMainThread()); 1.42 + MOZ_ASSERT(aActors.IsEmpty()); 1.43 + 1.44 + nsAutoTArray<mozilla::dom::ContentParent*, 20> contentActors; 1.45 + mozilla::dom::ContentParent::GetAll(contentActors); 1.46 + 1.47 + for (uint32_t contentIndex = 0; 1.48 + contentIndex < contentActors.Length(); 1.49 + ++contentIndex) { 1.50 + MOZ_ASSERT(contentActors[contentIndex]); 1.51 + 1.52 + AutoInfallibleTArray<mozilla::dom::PSpeechSynthesisParent*, 5> speechsynthActors; 1.53 + contentActors[contentIndex]->ManagedPSpeechSynthesisParent(speechsynthActors); 1.54 + 1.55 + for (uint32_t speechsynthIndex = 0; 1.56 + speechsynthIndex < speechsynthActors.Length(); 1.57 + ++speechsynthIndex) { 1.58 + MOZ_ASSERT(speechsynthActors[speechsynthIndex]); 1.59 + 1.60 + mozilla::dom::SpeechSynthesisParent* actor = 1.61 + static_cast<mozilla::dom::SpeechSynthesisParent*>(speechsynthActors[speechsynthIndex]); 1.62 + aActors.AppendElement(actor); 1.63 + } 1.64 + } 1.65 +} 1.66 +} 1.67 + 1.68 +namespace mozilla { 1.69 +namespace dom { 1.70 + 1.71 +// VoiceData 1.72 + 1.73 +class VoiceData MOZ_FINAL 1.74 +{ 1.75 +private: 1.76 + // Private destructor, to discourage deletion outside of Release(): 1.77 + ~VoiceData() {} 1.78 + 1.79 +public: 1.80 + VoiceData(nsISpeechService* aService, const nsAString& aUri, 1.81 + const nsAString& aName, const nsAString& aLang, bool aIsLocal) 1.82 + : mService(aService) 1.83 + , mUri(aUri) 1.84 + , mName(aName) 1.85 + , mLang(aLang) 1.86 + , mIsLocal(aIsLocal) {} 1.87 + 1.88 + NS_INLINE_DECL_REFCOUNTING(VoiceData) 1.89 + 1.90 + nsCOMPtr<nsISpeechService> mService; 1.91 + 1.92 + nsString mUri; 1.93 + 1.94 + nsString mName; 1.95 + 1.96 + nsString mLang; 1.97 + 1.98 + bool mIsLocal; 1.99 +}; 1.100 + 1.101 +// nsSynthVoiceRegistry 1.102 + 1.103 +static StaticRefPtr<nsSynthVoiceRegistry> gSynthVoiceRegistry; 1.104 + 1.105 +NS_IMPL_ISUPPORTS(nsSynthVoiceRegistry, nsISynthVoiceRegistry) 1.106 + 1.107 +nsSynthVoiceRegistry::nsSynthVoiceRegistry() 1.108 + : mSpeechSynthChild(nullptr) 1.109 +{ 1.110 + if (XRE_GetProcessType() == GeckoProcessType_Content) { 1.111 + 1.112 + mSpeechSynthChild = new SpeechSynthesisChild(); 1.113 + ContentChild::GetSingleton()->SendPSpeechSynthesisConstructor(mSpeechSynthChild); 1.114 + 1.115 + InfallibleTArray<RemoteVoice> voices; 1.116 + InfallibleTArray<nsString> defaults; 1.117 + 1.118 + mSpeechSynthChild->SendReadVoiceList(&voices, &defaults); 1.119 + 1.120 + for (uint32_t i = 0; i < voices.Length(); ++i) { 1.121 + RemoteVoice voice = voices[i]; 1.122 + AddVoiceImpl(nullptr, voice.voiceURI(), 1.123 + voice.name(), voice.lang(), 1.124 + voice.localService()); 1.125 + } 1.126 + 1.127 + for (uint32_t i = 0; i < defaults.Length(); ++i) { 1.128 + SetDefaultVoice(defaults[i], true); 1.129 + } 1.130 + } 1.131 +} 1.132 + 1.133 +nsSynthVoiceRegistry::~nsSynthVoiceRegistry() 1.134 +{ 1.135 + LOG(PR_LOG_DEBUG, ("~nsSynthVoiceRegistry")); 1.136 + 1.137 + // mSpeechSynthChild's lifecycle is managed by the Content protocol. 1.138 + mSpeechSynthChild = nullptr; 1.139 + 1.140 + mUriVoiceMap.Clear(); 1.141 +} 1.142 + 1.143 +nsSynthVoiceRegistry* 1.144 +nsSynthVoiceRegistry::GetInstance() 1.145 +{ 1.146 + MOZ_ASSERT(NS_IsMainThread()); 1.147 + 1.148 + if (!gSynthVoiceRegistry) { 1.149 + gSynthVoiceRegistry = new nsSynthVoiceRegistry(); 1.150 + } 1.151 + 1.152 + return gSynthVoiceRegistry; 1.153 +} 1.154 + 1.155 +already_AddRefed<nsSynthVoiceRegistry> 1.156 +nsSynthVoiceRegistry::GetInstanceForService() 1.157 +{ 1.158 + nsRefPtr<nsSynthVoiceRegistry> registry = GetInstance(); 1.159 + 1.160 + return registry.forget(); 1.161 +} 1.162 + 1.163 +void 1.164 +nsSynthVoiceRegistry::Shutdown() 1.165 +{ 1.166 + LOG(PR_LOG_DEBUG, ("[%s] nsSynthVoiceRegistry::Shutdown()", 1.167 + (XRE_GetProcessType() == GeckoProcessType_Content) ? "Content" : "Default")); 1.168 + gSynthVoiceRegistry = nullptr; 1.169 +} 1.170 + 1.171 +void 1.172 +nsSynthVoiceRegistry::SendVoices(InfallibleTArray<RemoteVoice>* aVoices, 1.173 + InfallibleTArray<nsString>* aDefaults) 1.174 +{ 1.175 + for (uint32_t i=0; i < mVoices.Length(); ++i) { 1.176 + nsRefPtr<VoiceData> voice = mVoices[i]; 1.177 + 1.178 + aVoices->AppendElement(RemoteVoice(voice->mUri, voice->mName, voice->mLang, 1.179 + voice->mIsLocal)); 1.180 + } 1.181 + 1.182 + for (uint32_t i=0; i < mDefaultVoices.Length(); ++i) { 1.183 + aDefaults->AppendElement(mDefaultVoices[i]->mUri); 1.184 + } 1.185 +} 1.186 + 1.187 +void 1.188 +nsSynthVoiceRegistry::RecvRemoveVoice(const nsAString& aUri) 1.189 +{ 1.190 + // If we dont have a local instance of the registry yet, we will recieve current 1.191 + // voices at contruction time. 1.192 + if(!gSynthVoiceRegistry) { 1.193 + return; 1.194 + } 1.195 + 1.196 + gSynthVoiceRegistry->RemoveVoice(nullptr, aUri); 1.197 +} 1.198 + 1.199 +void 1.200 +nsSynthVoiceRegistry::RecvAddVoice(const RemoteVoice& aVoice) 1.201 +{ 1.202 + // If we dont have a local instance of the registry yet, we will recieve current 1.203 + // voices at contruction time. 1.204 + if(!gSynthVoiceRegistry) { 1.205 + return; 1.206 + } 1.207 + 1.208 + gSynthVoiceRegistry->AddVoiceImpl(nullptr, aVoice.voiceURI(), 1.209 + aVoice.name(), aVoice.lang(), 1.210 + aVoice.localService()); 1.211 +} 1.212 + 1.213 +void 1.214 +nsSynthVoiceRegistry::RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault) 1.215 +{ 1.216 + // If we dont have a local instance of the registry yet, we will recieve current 1.217 + // voices at contruction time. 1.218 + if(!gSynthVoiceRegistry) { 1.219 + return; 1.220 + } 1.221 + 1.222 + gSynthVoiceRegistry->SetDefaultVoice(aUri, aIsDefault); 1.223 +} 1.224 + 1.225 +NS_IMETHODIMP 1.226 +nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService, 1.227 + const nsAString& aUri, 1.228 + const nsAString& aName, 1.229 + const nsAString& aLang, 1.230 + bool aLocalService) 1.231 +{ 1.232 + LOG(PR_LOG_DEBUG, 1.233 + ("nsSynthVoiceRegistry::AddVoice uri='%s' name='%s' lang='%s' local=%s", 1.234 + NS_ConvertUTF16toUTF8(aUri).get(), NS_ConvertUTF16toUTF8(aName).get(), 1.235 + NS_ConvertUTF16toUTF8(aLang).get(), 1.236 + aLocalService ? "true" : "false")); 1.237 + 1.238 + NS_ENSURE_FALSE(XRE_GetProcessType() == GeckoProcessType_Content, 1.239 + NS_ERROR_NOT_AVAILABLE); 1.240 + 1.241 + return AddVoiceImpl(aService, aUri, aName, aLang, 1.242 + aLocalService); 1.243 +} 1.244 + 1.245 +NS_IMETHODIMP 1.246 +nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService, 1.247 + const nsAString& aUri) 1.248 +{ 1.249 + LOG(PR_LOG_DEBUG, 1.250 + ("nsSynthVoiceRegistry::RemoveVoice uri='%s' (%s)", 1.251 + NS_ConvertUTF16toUTF8(aUri).get(), 1.252 + (XRE_GetProcessType() == GeckoProcessType_Content) ? "child" : "parent")); 1.253 + 1.254 + bool found = false; 1.255 + VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found); 1.256 + 1.257 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.258 + NS_ENSURE_TRUE(aService == retval->mService, NS_ERROR_INVALID_ARG); 1.259 + 1.260 + mVoices.RemoveElement(retval); 1.261 + mDefaultVoices.RemoveElement(retval); 1.262 + mUriVoiceMap.Remove(aUri); 1.263 + 1.264 + nsTArray<SpeechSynthesisParent*> ssplist; 1.265 + GetAllSpeechSynthActors(ssplist); 1.266 + 1.267 + for (uint32_t i = 0; i < ssplist.Length(); ++i) 1.268 + unused << ssplist[i]->SendVoiceRemoved(nsString(aUri)); 1.269 + 1.270 + return NS_OK; 1.271 +} 1.272 + 1.273 +NS_IMETHODIMP 1.274 +nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri, 1.275 + bool aIsDefault) 1.276 +{ 1.277 + bool found = false; 1.278 + VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found); 1.279 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.280 + 1.281 + mDefaultVoices.RemoveElement(retval); 1.282 + 1.283 + LOG(PR_LOG_DEBUG, ("nsSynthVoiceRegistry::SetDefaultVoice %s %s", 1.284 + NS_ConvertUTF16toUTF8(aUri).get(), 1.285 + aIsDefault ? "true" : "false")); 1.286 + 1.287 + if (aIsDefault) { 1.288 + mDefaultVoices.AppendElement(retval); 1.289 + } 1.290 + 1.291 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.292 + nsTArray<SpeechSynthesisParent*> ssplist; 1.293 + GetAllSpeechSynthActors(ssplist); 1.294 + 1.295 + for (uint32_t i = 0; i < ssplist.Length(); ++i) { 1.296 + unused << ssplist[i]->SendSetDefaultVoice(nsString(aUri), aIsDefault); 1.297 + } 1.298 + } 1.299 + 1.300 + return NS_OK; 1.301 +} 1.302 + 1.303 +NS_IMETHODIMP 1.304 +nsSynthVoiceRegistry::GetVoiceCount(uint32_t* aRetval) 1.305 +{ 1.306 + *aRetval = mVoices.Length(); 1.307 + 1.308 + return NS_OK; 1.309 +} 1.310 + 1.311 +NS_IMETHODIMP 1.312 +nsSynthVoiceRegistry::GetVoice(uint32_t aIndex, nsAString& aRetval) 1.313 +{ 1.314 + NS_ENSURE_TRUE(aIndex < mVoices.Length(), NS_ERROR_INVALID_ARG); 1.315 + 1.316 + aRetval = mVoices[aIndex]->mUri; 1.317 + 1.318 + return NS_OK; 1.319 +} 1.320 + 1.321 +NS_IMETHODIMP 1.322 +nsSynthVoiceRegistry::IsDefaultVoice(const nsAString& aUri, bool* aRetval) 1.323 +{ 1.324 + bool found; 1.325 + VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found); 1.326 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.327 + 1.328 + for (int32_t i = mDefaultVoices.Length(); i > 0; ) { 1.329 + VoiceData* defaultVoice = mDefaultVoices[--i]; 1.330 + 1.331 + if (voice->mLang.Equals(defaultVoice->mLang)) { 1.332 + *aRetval = voice == defaultVoice; 1.333 + return NS_OK; 1.334 + } 1.335 + } 1.336 + 1.337 + *aRetval = false; 1.338 + return NS_OK; 1.339 +} 1.340 + 1.341 +NS_IMETHODIMP 1.342 +nsSynthVoiceRegistry::IsLocalVoice(const nsAString& aUri, bool* aRetval) 1.343 +{ 1.344 + bool found; 1.345 + VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found); 1.346 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.347 + 1.348 + *aRetval = voice->mIsLocal; 1.349 + return NS_OK; 1.350 +} 1.351 + 1.352 +NS_IMETHODIMP 1.353 +nsSynthVoiceRegistry::GetVoiceLang(const nsAString& aUri, nsAString& aRetval) 1.354 +{ 1.355 + bool found; 1.356 + VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found); 1.357 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.358 + 1.359 + aRetval = voice->mLang; 1.360 + return NS_OK; 1.361 +} 1.362 + 1.363 +NS_IMETHODIMP 1.364 +nsSynthVoiceRegistry::GetVoiceName(const nsAString& aUri, nsAString& aRetval) 1.365 +{ 1.366 + bool found; 1.367 + VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found); 1.368 + NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE); 1.369 + 1.370 + aRetval = voice->mName; 1.371 + return NS_OK; 1.372 +} 1.373 + 1.374 +nsresult 1.375 +nsSynthVoiceRegistry::AddVoiceImpl(nsISpeechService* aService, 1.376 + const nsAString& aUri, 1.377 + const nsAString& aName, 1.378 + const nsAString& aLang, 1.379 + bool aLocalService) 1.380 +{ 1.381 + bool found = false; 1.382 + mUriVoiceMap.GetWeak(aUri, &found); 1.383 + NS_ENSURE_FALSE(found, NS_ERROR_INVALID_ARG); 1.384 + 1.385 + nsRefPtr<VoiceData> voice = new VoiceData(aService, aUri, aName, aLang, 1.386 + aLocalService); 1.387 + 1.388 + mVoices.AppendElement(voice); 1.389 + mUriVoiceMap.Put(aUri, voice); 1.390 + 1.391 + nsTArray<SpeechSynthesisParent*> ssplist; 1.392 + GetAllSpeechSynthActors(ssplist); 1.393 + 1.394 + if (!ssplist.IsEmpty()) { 1.395 + mozilla::dom::RemoteVoice ssvoice(nsString(aUri), 1.396 + nsString(aName), 1.397 + nsString(aLang), 1.398 + aLocalService); 1.399 + 1.400 + for (uint32_t i = 0; i < ssplist.Length(); ++i) { 1.401 + unused << ssplist[i]->SendVoiceAdded(ssvoice); 1.402 + } 1.403 + } 1.404 + 1.405 + return NS_OK; 1.406 +} 1.407 + 1.408 +bool 1.409 +nsSynthVoiceRegistry::FindVoiceByLang(const nsAString& aLang, 1.410 + VoiceData** aRetval) 1.411 +{ 1.412 + nsAString::const_iterator dashPos, start, end; 1.413 + aLang.BeginReading(start); 1.414 + aLang.EndReading(end); 1.415 + 1.416 + while (true) { 1.417 + nsAutoString langPrefix(Substring(start, end)); 1.418 + 1.419 + for (int32_t i = mDefaultVoices.Length(); i > 0; ) { 1.420 + VoiceData* voice = mDefaultVoices[--i]; 1.421 + 1.422 + if (StringBeginsWith(voice->mLang, langPrefix)) { 1.423 + *aRetval = voice; 1.424 + return true; 1.425 + } 1.426 + } 1.427 + 1.428 + for (int32_t i = mVoices.Length(); i > 0; ) { 1.429 + VoiceData* voice = mVoices[--i]; 1.430 + 1.431 + if (StringBeginsWith(voice->mLang, langPrefix)) { 1.432 + *aRetval = voice; 1.433 + return true; 1.434 + } 1.435 + } 1.436 + 1.437 + dashPos = end; 1.438 + end = start; 1.439 + 1.440 + if (!RFindInReadable(NS_LITERAL_STRING("-"), end, dashPos)) { 1.441 + break; 1.442 + } 1.443 + } 1.444 + 1.445 + return false; 1.446 +} 1.447 + 1.448 +VoiceData* 1.449 +nsSynthVoiceRegistry::FindBestMatch(const nsAString& aUri, 1.450 + const nsAString& aLang) 1.451 +{ 1.452 + if (mVoices.IsEmpty()) { 1.453 + return nullptr; 1.454 + } 1.455 + 1.456 + bool found = false; 1.457 + VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found); 1.458 + 1.459 + if (found) { 1.460 + LOG(PR_LOG_DEBUG, ("nsSynthVoiceRegistry::FindBestMatch - Matched URI")); 1.461 + return retval; 1.462 + } 1.463 + 1.464 + // Try finding a match for given voice. 1.465 + if (!aLang.IsVoid() && !aLang.IsEmpty()) { 1.466 + if (FindVoiceByLang(aLang, &retval)) { 1.467 + LOG(PR_LOG_DEBUG, 1.468 + ("nsSynthVoiceRegistry::FindBestMatch - Matched language (%s ~= %s)", 1.469 + NS_ConvertUTF16toUTF8(aLang).get(), 1.470 + NS_ConvertUTF16toUTF8(retval->mLang).get())); 1.471 + 1.472 + return retval; 1.473 + } 1.474 + } 1.475 + 1.476 + // Try UI language. 1.477 + nsresult rv; 1.478 + nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); 1.479 + NS_ENSURE_SUCCESS(rv, nullptr); 1.480 + 1.481 + nsAutoString uiLang; 1.482 + rv = localeService->GetLocaleComponentForUserAgent(uiLang); 1.483 + NS_ENSURE_SUCCESS(rv, nullptr); 1.484 + 1.485 + if (FindVoiceByLang(uiLang, &retval)) { 1.486 + LOG(PR_LOG_DEBUG, 1.487 + ("nsSynthVoiceRegistry::FindBestMatch - Matched UI language (%s ~= %s)", 1.488 + NS_ConvertUTF16toUTF8(uiLang).get(), 1.489 + NS_ConvertUTF16toUTF8(retval->mLang).get())); 1.490 + 1.491 + return retval; 1.492 + } 1.493 + 1.494 + // Try en-US, the language of locale "C" 1.495 + if (FindVoiceByLang(NS_LITERAL_STRING("en-US"), &retval)) { 1.496 + LOG(PR_LOG_DEBUG, 1.497 + ("nsSynthVoiceRegistry::FindBestMatch - Matched C locale language (en-US ~= %s)", 1.498 + NS_ConvertUTF16toUTF8(retval->mLang).get())); 1.499 + 1.500 + return retval; 1.501 + } 1.502 + 1.503 + // The top default voice is better than nothing... 1.504 + if (!mDefaultVoices.IsEmpty()) { 1.505 + return mDefaultVoices.LastElement(); 1.506 + } 1.507 + 1.508 + return nullptr; 1.509 +} 1.510 + 1.511 +already_AddRefed<nsSpeechTask> 1.512 +nsSynthVoiceRegistry::SpeakUtterance(SpeechSynthesisUtterance& aUtterance, 1.513 + const nsAString& aDocLang) 1.514 +{ 1.515 + nsString lang = nsString(aUtterance.mLang.IsEmpty() ? aDocLang : aUtterance.mLang); 1.516 + nsAutoString uri; 1.517 + 1.518 + if (aUtterance.mVoice) { 1.519 + aUtterance.mVoice->GetVoiceURI(uri); 1.520 + } 1.521 + 1.522 + nsRefPtr<nsSpeechTask> task; 1.523 + if (XRE_GetProcessType() == GeckoProcessType_Content) { 1.524 + task = new SpeechTaskChild(&aUtterance); 1.525 + SpeechSynthesisRequestChild* actor = 1.526 + new SpeechSynthesisRequestChild(static_cast<SpeechTaskChild*>(task.get())); 1.527 + mSpeechSynthChild->SendPSpeechSynthesisRequestConstructor(actor, 1.528 + aUtterance.mText, 1.529 + lang, 1.530 + uri, 1.531 + aUtterance.Volume(), 1.532 + aUtterance.Rate(), 1.533 + aUtterance.Pitch()); 1.534 + } else { 1.535 + task = new nsSpeechTask(&aUtterance); 1.536 + Speak(aUtterance.mText, lang, uri, 1.537 + aUtterance.Rate(), aUtterance.Pitch(), task); 1.538 + } 1.539 + 1.540 + return task.forget(); 1.541 +} 1.542 + 1.543 +void 1.544 +nsSynthVoiceRegistry::Speak(const nsAString& aText, 1.545 + const nsAString& aLang, 1.546 + const nsAString& aUri, 1.547 + const float& aRate, 1.548 + const float& aPitch, 1.549 + nsSpeechTask* aTask) 1.550 +{ 1.551 + LOG(PR_LOG_DEBUG, 1.552 + ("nsSynthVoiceRegistry::Speak text='%s' lang='%s' uri='%s' rate=%f pitch=%f", 1.553 + NS_ConvertUTF16toUTF8(aText).get(), NS_ConvertUTF16toUTF8(aLang).get(), 1.554 + NS_ConvertUTF16toUTF8(aUri).get(), aRate, aPitch)); 1.555 + 1.556 + VoiceData* voice = FindBestMatch(aUri, aLang); 1.557 + 1.558 + if (!voice) { 1.559 + NS_WARNING("No voices found."); 1.560 + aTask->DispatchError(0, 0); 1.561 + return; 1.562 + } 1.563 + 1.564 + LOG(PR_LOG_DEBUG, ("nsSynthVoiceRegistry::Speak - Using voice URI: %s", 1.565 + NS_ConvertUTF16toUTF8(voice->mUri).get())); 1.566 + 1.567 + SpeechServiceType serviceType; 1.568 + 1.569 + DebugOnly<nsresult> rv = voice->mService->GetServiceType(&serviceType); 1.570 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to get speech service type"); 1.571 + 1.572 + if (serviceType == nsISpeechService::SERVICETYPE_INDIRECT_AUDIO) { 1.573 + aTask->SetIndirectAudio(true); 1.574 + } 1.575 + 1.576 + voice->mService->Speak(aText, voice->mUri, aRate, aPitch, aTask); 1.577 +} 1.578 + 1.579 +} // namespace dom 1.580 +} // namespace mozilla