content/media/webspeech/synth/pico/nsPicoService.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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

mercurial