Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsISupports.h"
8 #include "nsPicoService.h"
9 #include "nsPrintfCString.h"
10 #include "nsIWeakReferenceUtils.h"
11 #include "SharedBuffer.h"
12 #include "nsISimpleEnumerator.h"
14 #include "mozilla/dom/nsSynthVoiceRegistry.h"
15 #include "mozilla/dom/nsSpeechTask.h"
17 #include "nsIFile.h"
18 #include "nsThreadUtils.h"
19 #include "prenv.h"
21 #include "mozilla/DebugOnly.h"
22 #include <dlfcn.h>
24 // Pico API constants
26 // Size of memory allocated for pico engine and voice resources.
27 // We only have one voice and its resources loaded at once, so this
28 // should always be enough.
29 #define PICO_MEM_SIZE 2500000
31 // Max length of returned strings. Pico will never return longer strings,
32 // so this amount should be good enough for preallocating.
33 #define PICO_RETSTRINGSIZE 200
35 // Max amount we want from a single call of pico_getData
36 #define PICO_MAX_CHUNK_SIZE 128
38 // Arbitrary name for loaded voice, it doesn't mean anything outside of Pico
39 #define PICO_VOICE_NAME "pico"
41 // Return status from pico_getData meaning there is more data in the pipeline
42 // to get from more calls to pico_getData
43 #define PICO_STEP_BUSY 201
45 // For performing a "soft" reset between utterances. This is used when one
46 // utterance is interrupted by a new one.
47 #define PICO_RESET_SOFT 0x10
49 // Currently, Pico only provides mono output.
50 #define PICO_CHANNELS_NUM 1
52 // Pico's sample rate is always 16000
53 #define PICO_SAMPLE_RATE 16000
55 // The path to the language files in Gonk
56 #define GONK_PICO_LANG_PATH "/system/tts/lang_pico"
58 namespace mozilla {
59 namespace dom {
61 StaticRefPtr<nsPicoService> nsPicoService::sSingleton;
63 class PicoApi
64 {
65 public:
67 PicoApi() : mInitialized(false) {}
69 bool Init()
70 {
71 if (mInitialized) {
72 return true;
73 }
75 void* handle = dlopen("libttspico.so", RTLD_LAZY);
77 if (!handle) {
78 NS_WARNING("Failed to open libttspico.so, pico cannot run");
79 return false;
80 }
82 pico_initialize =
83 (pico_Status (*)(void*, uint32_t, pico_System*))dlsym(
84 handle, "pico_initialize");
86 pico_terminate =
87 (pico_Status (*)(pico_System*))dlsym(handle, "pico_terminate");
89 pico_getSystemStatusMessage =
90 (pico_Status (*)(pico_System, pico_Status, pico_Retstring))dlsym(
91 handle, "pico_getSystemStatusMessage");;
93 pico_loadResource =
94 (pico_Status (*)(pico_System, const char*, pico_Resource*))dlsym(
95 handle, "pico_loadResource");
97 pico_unloadResource =
98 (pico_Status (*)(pico_System, pico_Resource*))dlsym(
99 handle, "pico_unloadResource");
101 pico_getResourceName =
102 (pico_Status (*)(pico_System, pico_Resource, pico_Retstring))dlsym(
103 handle, "pico_getResourceName");
105 pico_createVoiceDefinition =
106 (pico_Status (*)(pico_System, const char*))dlsym(
107 handle, "pico_createVoiceDefinition");
109 pico_addResourceToVoiceDefinition =
110 (pico_Status (*)(pico_System, const char*, const char*))dlsym(
111 handle, "pico_addResourceToVoiceDefinition");
113 pico_releaseVoiceDefinition =
114 (pico_Status (*)(pico_System, const char*))dlsym(
115 handle, "pico_releaseVoiceDefinition");
117 pico_newEngine =
118 (pico_Status (*)(pico_System, const char*, pico_Engine*))dlsym(
119 handle, "pico_newEngine");
121 pico_disposeEngine =
122 (pico_Status (*)(pico_System, pico_Engine*))dlsym(
123 handle, "pico_disposeEngine");
125 pico_resetEngine =
126 (pico_Status (*)(pico_Engine, int32_t))dlsym(handle, "pico_resetEngine");
128 pico_putTextUtf8 =
129 (pico_Status (*)(pico_Engine, const char*, const int16_t, int16_t*))dlsym(
130 handle, "pico_putTextUtf8");
132 pico_getData =
133 (pico_Status (*)(pico_Engine, void*, int16_t, int16_t*, int16_t*))dlsym(
134 handle, "pico_getData");
136 mInitialized = true;
137 return true;
138 }
140 typedef signed int pico_Status;
141 typedef char pico_Retstring[PICO_RETSTRINGSIZE];
143 pico_Status (* pico_initialize)(void*, uint32_t, pico_System*);
144 pico_Status (* pico_terminate)(pico_System*);
145 pico_Status (* pico_getSystemStatusMessage)(
146 pico_System, pico_Status, pico_Retstring);
148 pico_Status (* pico_loadResource)(pico_System, const char*, pico_Resource*);
149 pico_Status (* pico_unloadResource)(pico_System, pico_Resource*);
150 pico_Status (* pico_getResourceName)(
151 pico_System, pico_Resource, pico_Retstring);
152 pico_Status (* pico_createVoiceDefinition)(pico_System, const char*);
153 pico_Status (* pico_addResourceToVoiceDefinition)(
154 pico_System, const char*, const char*);
155 pico_Status (* pico_releaseVoiceDefinition)(pico_System, const char*);
156 pico_Status (* pico_newEngine)(pico_System, const char*, pico_Engine*);
157 pico_Status (* pico_disposeEngine)(pico_System, pico_Engine*);
159 pico_Status (* pico_resetEngine)(pico_Engine, int32_t);
160 pico_Status (* pico_putTextUtf8)(
161 pico_Engine, const char*, const int16_t, int16_t*);
162 pico_Status (* pico_getData)(
163 pico_Engine, void*, const int16_t, int16_t*, int16_t*);
165 private:
167 bool mInitialized;
169 } sPicoApi;
171 #define PICO_ENSURE_SUCCESS_VOID(_funcName, _status) \
172 if (_status < 0) { \
173 PicoApi::pico_Retstring message; \
174 sPicoApi.pico_getSystemStatusMessage( \
175 nsPicoService::sSingleton->mPicoSystem, _status, message); \
176 NS_WARNING( \
177 nsPrintfCString("Error running %s: %s", _funcName, message).get()); \
178 return; \
179 }
181 #define PICO_ENSURE_SUCCESS(_funcName, _status, _rv) \
182 if (_status < 0) { \
183 PicoApi::pico_Retstring message; \
184 sPicoApi.pico_getSystemStatusMessage( \
185 nsPicoService::sSingleton->mPicoSystem, _status, message); \
186 NS_WARNING( \
187 nsPrintfCString("Error running %s: %s", _funcName, message).get()); \
188 return _rv; \
189 }
191 class PicoVoice
192 {
193 public:
195 PicoVoice(const nsAString& aLanguage)
196 : mLanguage(aLanguage) {}
198 ~PicoVoice() {}
200 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PicoVoice)
202 // Voice language, in BCB-47 syntax
203 nsString mLanguage;
205 // Language resource file
206 nsCString mTaFile;
208 // Speaker resource file
209 nsCString mSgFile;
210 };
212 class PicoCallbackRunnable : public nsRunnable,
213 public nsISpeechTaskCallback
214 {
215 friend class PicoSynthDataRunnable;
217 public:
218 PicoCallbackRunnable(const nsAString& aText, PicoVoice* aVoice,
219 float aRate, float aPitch, nsISpeechTask* aTask,
220 nsPicoService* aService)
221 : mText(NS_ConvertUTF16toUTF8(aText))
222 , mRate(aRate)
223 , mPitch(aPitch)
224 , mFirstData(true)
225 , mTask(aTask)
226 , mVoice(aVoice)
227 , mService(aService) { }
229 ~PicoCallbackRunnable() { }
231 NS_DECL_ISUPPORTS_INHERITED
232 NS_DECL_NSISPEECHTASKCALLBACK
234 NS_IMETHOD Run() MOZ_OVERRIDE;
236 bool IsCurrentTask() { return mService->mCurrentTask == mTask; }
238 private:
239 void DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>&& aBuffer,
240 size_t aBufferSize);
242 nsCString mText;
244 float mRate;
246 float mPitch;
248 bool mFirstData;
250 // We use this pointer to compare it with the current service task.
251 // If they differ, this runnable should stop.
252 nsISpeechTask* mTask;
254 // We hold a strong reference to the service, which in turn holds
255 // a strong reference to this voice.
256 PicoVoice* mVoice;
258 // By holding a strong reference to the service we guarantee that it won't be
259 // destroyed before this runnable.
260 nsRefPtr<nsPicoService> mService;
261 };
263 NS_IMPL_ISUPPORTS_INHERITED(PicoCallbackRunnable, nsRunnable, nsISpeechTaskCallback)
265 // nsRunnable
267 NS_IMETHODIMP
268 PicoCallbackRunnable::Run()
269 {
270 MOZ_ASSERT(!NS_IsMainThread());
271 PicoApi::pico_Status status = 0;
273 if (mService->CurrentVoice() != mVoice) {
274 mService->LoadEngine(mVoice);
275 } else {
276 status = sPicoApi.pico_resetEngine(mService->mPicoEngine, PICO_RESET_SOFT);
277 PICO_ENSURE_SUCCESS("pico_unloadResource", status, NS_ERROR_FAILURE);
278 }
280 // Add SSML markup for pitch and rate. Pico uses a minimal parser,
281 // so no namespace is needed.
282 nsPrintfCString markedUpText(
283 "<pitch level=\"%0.0f\"><speed level=\"%0.0f\">%s</speed></pitch>",
284 std::min(std::max(50.0f, mPitch * 100), 200.0f),
285 std::min(std::max(20.0f, mRate * 100), 500.0f),
286 mText.get());
288 const char* text = markedUpText.get();
289 size_t buffer_size = 512, buffer_offset = 0;
290 nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(buffer_size);
291 int16_t text_offset = 0, bytes_recv = 0, bytes_sent = 0, out_data_type = 0;
292 int16_t text_remaining = markedUpText.Length() + 1;
294 // Run this loop while this is the current task
295 while (IsCurrentTask()) {
296 if (text_remaining) {
297 status = sPicoApi.pico_putTextUtf8(mService->mPicoEngine,
298 text + text_offset, text_remaining,
299 &bytes_sent);
300 PICO_ENSURE_SUCCESS("pico_putTextUtf8", status, NS_ERROR_FAILURE);
301 // XXX: End speech task on error
302 text_remaining -= bytes_sent;
303 text_offset += bytes_sent;
304 } else {
305 // If we already fed all the text to the engine, send a zero length buffer
306 // and quit.
307 DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(nullptr), 0);
308 break;
309 }
311 do {
312 // Run this loop while the result of getData is STEP_BUSY, when it finishes
313 // synthesizing audio for the given text, it returns STEP_IDLE. We then
314 // break to the outer loop and feed more text, if there is any left.
315 if (!IsCurrentTask()) {
316 // If the task has changed, quit.
317 break;
318 }
320 if (buffer_size - buffer_offset < PICO_MAX_CHUNK_SIZE) {
321 // The next audio chunk retrieved may be bigger than our buffer,
322 // so send the data and flush the buffer.
323 DispatchSynthDataRunnable(buffer.forget(), buffer_offset);
324 buffer_offset = 0;
325 buffer = SharedBuffer::Create(buffer_size);
326 }
328 status = sPicoApi.pico_getData(mService->mPicoEngine,
329 (uint8_t*)buffer->Data() + buffer_offset,
330 PICO_MAX_CHUNK_SIZE,
331 &bytes_recv, &out_data_type);
332 PICO_ENSURE_SUCCESS("pico_getData", status, NS_ERROR_FAILURE);
333 buffer_offset += bytes_recv;
334 } while (status == PICO_STEP_BUSY);
335 }
337 return NS_OK;
338 }
340 void
341 PicoCallbackRunnable::DispatchSynthDataRunnable(
342 already_AddRefed<SharedBuffer>&& aBuffer, size_t aBufferSize)
343 {
344 class PicoSynthDataRunnable MOZ_FINAL : public nsRunnable
345 {
346 public:
347 PicoSynthDataRunnable(already_AddRefed<SharedBuffer>& aBuffer,
348 size_t aBufferSize, bool aFirstData,
349 PicoCallbackRunnable* aCallback)
350 : mBuffer(aBuffer)
351 , mBufferSize(aBufferSize)
352 , mFirstData(aFirstData)
353 , mCallback(aCallback) {
354 }
356 NS_IMETHOD Run()
357 {
358 MOZ_ASSERT(NS_IsMainThread());
360 if (!mCallback->IsCurrentTask()) {
361 return NS_ERROR_NOT_AVAILABLE;
362 }
364 nsISpeechTask* task = mCallback->mTask;
366 if (mFirstData) {
367 task->Setup(mCallback, PICO_CHANNELS_NUM, PICO_SAMPLE_RATE, 2);
368 }
370 return task->SendAudioNative(
371 mBufferSize ? static_cast<short*>(mBuffer->Data()) : nullptr, mBufferSize / 2);
372 }
374 private:
375 nsRefPtr<SharedBuffer> mBuffer;
377 size_t mBufferSize;
379 bool mFirstData;
381 nsRefPtr<PicoCallbackRunnable> mCallback;
382 };
384 nsCOMPtr<nsIRunnable> sendEvent =
385 new PicoSynthDataRunnable(aBuffer, aBufferSize, mFirstData, this);
386 NS_DispatchToMainThread(sendEvent);
387 mFirstData = false;
388 }
390 // nsISpeechTaskCallback
392 NS_IMETHODIMP
393 PicoCallbackRunnable::OnPause()
394 {
395 return NS_OK;
396 }
398 NS_IMETHODIMP
399 PicoCallbackRunnable::OnResume()
400 {
401 return NS_OK;
402 }
404 NS_IMETHODIMP
405 PicoCallbackRunnable::OnCancel()
406 {
407 mService->mCurrentTask = nullptr;
408 return NS_OK;
409 }
411 NS_INTERFACE_MAP_BEGIN(nsPicoService)
412 NS_INTERFACE_MAP_ENTRY(nsISpeechService)
413 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechService)
414 NS_INTERFACE_MAP_END
416 NS_IMPL_ADDREF(nsPicoService)
417 NS_IMPL_RELEASE(nsPicoService)
419 nsPicoService::nsPicoService()
420 : mInitialized(false)
421 , mVoicesMonitor("nsPicoService::mVoices")
422 , mCurrentTask(nullptr)
423 , mPicoSystem(nullptr)
424 , mPicoEngine(nullptr)
425 , mSgResource(nullptr)
426 , mTaResource(nullptr)
427 , mPicoMemArea(nullptr)
428 {
429 DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
430 MOZ_ASSERT(NS_SUCCEEDED(rv));
431 rv = mThread->Dispatch(NS_NewRunnableMethod(this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
432 MOZ_ASSERT(NS_SUCCEEDED(rv));
433 }
435 nsPicoService::~nsPicoService()
436 {
437 // We don't worry about removing the voices because this gets
438 // destructed at shutdown along with the voice registry.
439 MonitorAutoLock autoLock(mVoicesMonitor);
440 mVoices.Clear();
442 if (mThread) {
443 mThread->Shutdown();
444 }
446 UnloadEngine();
447 }
449 // nsISpeechService
451 NS_IMETHODIMP
452 nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
453 float aRate, float aPitch, nsISpeechTask* aTask)
454 {
455 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_AVAILABLE);
457 MonitorAutoLock autoLock(mVoicesMonitor);
458 bool found = false;
459 PicoVoice* voice = mVoices.GetWeak(aUri, &found);
460 NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
462 mCurrentTask = aTask;
463 nsRefPtr<PicoCallbackRunnable> cb = new PicoCallbackRunnable(aText, voice, aRate, aPitch, aTask, this);
464 return mThread->Dispatch(cb, NS_DISPATCH_NORMAL);
465 }
467 NS_IMETHODIMP
468 nsPicoService::GetServiceType(SpeechServiceType* aServiceType)
469 {
470 *aServiceType = nsISpeechService::SERVICETYPE_DIRECT_AUDIO;
471 return NS_OK;
472 }
474 struct VoiceTraverserData
475 {
476 nsPicoService* mService;
477 nsSynthVoiceRegistry* mRegistry;
478 };
480 // private methods
482 static PLDHashOperator
483 PicoAddVoiceTraverser(const nsAString& aUri,
484 nsRefPtr<PicoVoice>& aVoice,
485 void* aUserArg)
486 {
487 // If we are missing either a language or a voice resource, it is invalid.
488 if (aVoice->mTaFile.IsEmpty() || aVoice->mSgFile.IsEmpty()) {
489 return PL_DHASH_REMOVE;
490 }
492 VoiceTraverserData* data = static_cast<VoiceTraverserData*>(aUserArg);
494 nsAutoString name;
495 name.AssignLiteral("Pico ");
496 name.Append(aVoice->mLanguage);
498 DebugOnly<nsresult> rv =
499 data->mRegistry->AddVoice(
500 data->mService, aUri, name, aVoice->mLanguage, true);
501 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice");
503 return PL_DHASH_NEXT;
504 }
506 void
507 nsPicoService::Init()
508 {
509 MOZ_ASSERT(!NS_IsMainThread());
510 MOZ_ASSERT(!mInitialized);
512 sPicoApi.Init();
514 // Use environment variable, or default android/b2g path
515 nsAutoCString langPath(PR_GetEnv("PICO_LANG_PATH"));
517 if (langPath.IsEmpty()) {
518 langPath.AssignLiteral(GONK_PICO_LANG_PATH);
519 }
521 nsCOMPtr<nsIFile> voicesDir;
522 NS_NewNativeLocalFile(langPath, true, getter_AddRefs(voicesDir));
524 nsCOMPtr<nsISimpleEnumerator> dirIterator;
525 nsresult rv = voicesDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
527 if (NS_FAILED(rv)) {
528 NS_WARNING(nsPrintfCString("Failed to get contents of directory: %s", langPath.get()).get());
529 return;
530 }
532 bool hasMoreElements = false;
533 rv = dirIterator->HasMoreElements(&hasMoreElements);
534 MOZ_ASSERT(NS_SUCCEEDED(rv));
536 MonitorAutoLock autoLock(mVoicesMonitor);
538 while (hasMoreElements && NS_SUCCEEDED(rv)) {
539 nsCOMPtr<nsISupports> supports;
540 rv = dirIterator->GetNext(getter_AddRefs(supports));
541 MOZ_ASSERT(NS_SUCCEEDED(rv));
543 nsCOMPtr<nsIFile> voiceFile = do_QueryInterface(supports);
544 MOZ_ASSERT(voiceFile);
546 nsAutoCString leafName;
547 voiceFile->GetNativeLeafName(leafName);
549 nsAutoString lang;
551 if (GetVoiceFileLanguage(leafName, lang)) {
552 nsAutoString uri;
553 uri.AssignLiteral("urn:moz-tts:pico:");
554 uri.Append(lang);
556 bool found = false;
557 PicoVoice* voice = mVoices.GetWeak(uri, &found);
559 if (!found) {
560 voice = new PicoVoice(lang);
561 mVoices.Put(uri, voice);
562 }
564 // Each voice consists of two lingware files: A language resource file,
565 // suffixed by _ta.bin, and a speaker resource file, suffixed by _sb.bin.
566 // We currently assume that there is a pair of files for each language.
567 if (StringEndsWith(leafName, NS_LITERAL_CSTRING("_ta.bin"))) {
568 rv = voiceFile->GetPersistentDescriptor(voice->mTaFile);
569 MOZ_ASSERT(NS_SUCCEEDED(rv));
570 } else if (StringEndsWith(leafName, NS_LITERAL_CSTRING("_sg.bin"))) {
571 rv = voiceFile->GetPersistentDescriptor(voice->mSgFile);
572 MOZ_ASSERT(NS_SUCCEEDED(rv));
573 }
574 }
576 rv = dirIterator->HasMoreElements(&hasMoreElements);
577 }
579 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsPicoService::RegisterVoices));
580 }
582 void
583 nsPicoService::RegisterVoices()
584 {
585 VoiceTraverserData data = { this, nsSynthVoiceRegistry::GetInstance() };
586 mVoices.Enumerate(PicoAddVoiceTraverser, &data);
588 mInitialized = true;
589 }
591 bool
592 nsPicoService::GetVoiceFileLanguage(const nsACString& aFileName, nsAString& aLang)
593 {
594 nsACString::const_iterator start, end;
595 aFileName.BeginReading(start);
596 aFileName.EndReading(end);
598 // The lingware filename syntax is language_(ta/sg).bin,
599 // we extract the language prefix here.
600 if (FindInReadable(NS_LITERAL_CSTRING("_"), start, end)) {
601 end = start;
602 aFileName.BeginReading(start);
603 aLang.Assign(NS_ConvertUTF8toUTF16(Substring(start, end)));
604 return true;
605 }
607 return false;
608 }
610 void
611 nsPicoService::LoadEngine(PicoVoice* aVoice)
612 {
613 PicoApi::pico_Status status = 0;
615 if (mPicoSystem) {
616 UnloadEngine();
617 }
619 if (!mPicoMemArea) {
620 mPicoMemArea = new uint8_t[PICO_MEM_SIZE];
621 }
623 status = sPicoApi.pico_initialize(mPicoMemArea, PICO_MEM_SIZE, &mPicoSystem);
624 PICO_ENSURE_SUCCESS_VOID("pico_initialize", status);
626 status = sPicoApi.pico_loadResource(mPicoSystem, aVoice->mTaFile.get(), &mTaResource);
627 PICO_ENSURE_SUCCESS_VOID("pico_loadResource", status);
629 status = sPicoApi.pico_loadResource(mPicoSystem, aVoice->mSgFile.get(), &mSgResource);
630 PICO_ENSURE_SUCCESS_VOID("pico_loadResource", status);
632 status = sPicoApi.pico_createVoiceDefinition(mPicoSystem, PICO_VOICE_NAME);
633 PICO_ENSURE_SUCCESS_VOID("pico_createVoiceDefinition", status);
635 char taName[PICO_RETSTRINGSIZE];
636 status = sPicoApi.pico_getResourceName(mPicoSystem, mTaResource, taName);
637 PICO_ENSURE_SUCCESS_VOID("pico_getResourceName", status);
639 status = sPicoApi.pico_addResourceToVoiceDefinition(
640 mPicoSystem, PICO_VOICE_NAME, taName);
641 PICO_ENSURE_SUCCESS_VOID("pico_addResourceToVoiceDefinition", status);
643 char sgName[PICO_RETSTRINGSIZE];
644 status = sPicoApi.pico_getResourceName(mPicoSystem, mSgResource, sgName);
645 PICO_ENSURE_SUCCESS_VOID("pico_getResourceName", status);
647 status = sPicoApi.pico_addResourceToVoiceDefinition(
648 mPicoSystem, PICO_VOICE_NAME, sgName);
649 PICO_ENSURE_SUCCESS_VOID("pico_addResourceToVoiceDefinition", status);
651 status = sPicoApi.pico_newEngine(mPicoSystem, PICO_VOICE_NAME, &mPicoEngine);
652 PICO_ENSURE_SUCCESS_VOID("pico_newEngine", status);
654 if (sSingleton) {
655 sSingleton->mCurrentVoice = aVoice;
656 }
657 }
659 void
660 nsPicoService::UnloadEngine()
661 {
662 PicoApi::pico_Status status = 0;
664 if (mPicoEngine) {
665 status = sPicoApi.pico_disposeEngine(mPicoSystem, &mPicoEngine);
666 PICO_ENSURE_SUCCESS_VOID("pico_disposeEngine", status);
667 status = sPicoApi.pico_releaseVoiceDefinition(mPicoSystem, PICO_VOICE_NAME);
668 PICO_ENSURE_SUCCESS_VOID("pico_releaseVoiceDefinition", status);
669 mPicoEngine = nullptr;
670 }
672 if (mSgResource) {
673 status = sPicoApi.pico_unloadResource(mPicoSystem, &mSgResource);
674 PICO_ENSURE_SUCCESS_VOID("pico_unloadResource", status);
675 mSgResource = nullptr;
676 }
678 if (mTaResource) {
679 status = sPicoApi.pico_unloadResource(mPicoSystem, &mTaResource);
680 PICO_ENSURE_SUCCESS_VOID("pico_unloadResource", status);
681 mTaResource = nullptr;
682 }
684 if (mPicoSystem) {
685 status = sPicoApi.pico_terminate(&mPicoSystem);
686 PICO_ENSURE_SUCCESS_VOID("pico_terminate", status);
687 mPicoSystem = nullptr;
688 }
689 }
691 PicoVoice*
692 nsPicoService::CurrentVoice()
693 {
694 MOZ_ASSERT(!NS_IsMainThread());
696 return mCurrentVoice;
697 }
699 // static methods
701 nsPicoService*
702 nsPicoService::GetInstance()
703 {
704 MOZ_ASSERT(NS_IsMainThread());
705 if (XRE_GetProcessType() != GeckoProcessType_Default) {
706 MOZ_ASSERT(false, "nsPicoService can only be started on main gecko process");
707 return nullptr;
708 }
710 if (!sSingleton) {
711 sSingleton = new nsPicoService();
712 }
714 return sSingleton;
715 }
717 already_AddRefed<nsPicoService>
718 nsPicoService::GetInstanceForService()
719 {
720 nsRefPtr<nsPicoService> picoService = GetInstance();
721 return picoService.forget();
722 }
724 void
725 nsPicoService::Shutdown()
726 {
727 if (!sSingleton) {
728 return;
729 }
731 sSingleton->mCurrentTask = nullptr;
733 sSingleton = nullptr;
734 }
736 } // namespace dom
737 } // namespace mozilla