Bug 1331696 - P1. Remove WebSpeech Pico service. r=chmanchester,eeejay

It's no longer used, and won't be able to work with the removal of speech synth direct audio support.

MozReview-Commit-ID: BMdeRJHes0R

--HG--
extra : rebase_source : 9f45b360d27f8013ef229eba79e9d9921d0fdb26
This commit is contained in:
Jean-Yves Avenard 2017-12-02 09:17:39 +01:00
parent bf59da05d8
commit cfa7a324aa
6 changed files with 0 additions and 943 deletions

View File

@ -51,9 +51,6 @@ if CONFIG['MOZ_WEBSPEECH']:
if CONFIG['MOZ_SYNTH_SPEECHD']: if CONFIG['MOZ_SYNTH_SPEECHD']:
DIRS += ['speechd'] DIRS += ['speechd']
if CONFIG['MOZ_SYNTH_PICO']:
DIRS += ['pico']
IPDL_SOURCES += [ IPDL_SOURCES += [
'ipc/PSpeechSynthesis.ipdl', 'ipc/PSpeechSynthesis.ipdl',
'ipc/PSpeechSynthesisRequest.ipdl', 'ipc/PSpeechSynthesisRequest.ipdl',

View File

@ -1,58 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"
#ifdef MOZ_WEBRTC
#include "nsPicoService.h"
using namespace mozilla::dom;
#define PICOSERVICE_CID \
{0x346c4fc8, 0x12fe, 0x459c, {0x81, 0x19, 0x9a, 0xa7, 0x73, 0x37, 0x7f, 0xf4}}
#define PICOSERVICE_CONTRACTID "@mozilla.org/synthpico;1"
// Defines nsPicoServiceConstructor
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPicoService,
nsPicoService::GetInstanceForService)
// Defines kPICOSERVICE_CID
NS_DEFINE_NAMED_CID(PICOSERVICE_CID);
static const mozilla::Module::CIDEntry kCIDs[] = {
{ &kPICOSERVICE_CID, true, nullptr, nsPicoServiceConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kContracts[] = {
{ PICOSERVICE_CONTRACTID, &kPICOSERVICE_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kCategories[] = {
{ "profile-after-change", "Pico Speech Synth", PICOSERVICE_CONTRACTID },
{ nullptr }
};
static void
UnloadPicoModule()
{
nsPicoService::Shutdown();
}
static const mozilla::Module kModule = {
mozilla::Module::kVersion,
kCIDs,
kContracts,
kCategories,
nullptr,
nullptr,
UnloadPicoModule
};
NSMODULE_DEFN(synthpico) = &kModule;
#endif

View File

@ -1,13 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'nsPicoService.cpp',
'PicoModule.cpp'
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -1,762 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.h"
#include "nsPicoService.h"
#include "nsPrintfCString.h"
#include "nsIWeakReferenceUtils.h"
#include "SharedBuffer.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/dom/nsSynthVoiceRegistry.h"
#include "mozilla/dom/nsSpeechTask.h"
#include "nsIFile.h"
#include "nsThreadUtils.h"
#include "prenv.h"
#include "mozilla/Preferences.h"
#include "mozilla/DebugOnly.h"
#include <dlfcn.h>
// Pico API constants
// Size of memory allocated for pico engine and voice resources.
// We only have one voice and its resources loaded at once, so this
// should always be enough.
#define PICO_MEM_SIZE 2500000
// Max length of returned strings. Pico will never return longer strings,
// so this amount should be good enough for preallocating.
#define PICO_RETSTRINGSIZE 200
// Max amount we want from a single call of pico_getData
#define PICO_MAX_CHUNK_SIZE 128
// Arbitrary name for loaded voice, it doesn't mean anything outside of Pico
#define PICO_VOICE_NAME "pico"
// Return status from pico_getData meaning there is more data in the pipeline
// to get from more calls to pico_getData
#define PICO_STEP_BUSY 201
// For performing a "soft" reset between utterances. This is used when one
// utterance is interrupted by a new one.
#define PICO_RESET_SOFT 0x10
// Currently, Pico only provides mono output.
#define PICO_CHANNELS_NUM 1
// Pico's sample rate is always 16000
#define PICO_SAMPLE_RATE 16000
// The path to the language files in Android
#define PICO_LANG_PATH "/system/tts/lang_pico"
namespace mozilla {
namespace dom {
StaticRefPtr<nsPicoService> nsPicoService::sSingleton;
class PicoApi
{
public:
PicoApi() : mInitialized(false) {}
bool Init()
{
if (mInitialized) {
return true;
}
void* handle = dlopen("libttspico.so", RTLD_LAZY);
if (!handle) {
NS_WARNING("Failed to open libttspico.so, pico cannot run");
return false;
}
pico_initialize =
(pico_Status (*)(void*, uint32_t, pico_System*))dlsym(
handle, "pico_initialize");
pico_terminate =
(pico_Status (*)(pico_System*))dlsym(handle, "pico_terminate");
pico_getSystemStatusMessage =
(pico_Status (*)(pico_System, pico_Status, pico_Retstring))dlsym(
handle, "pico_getSystemStatusMessage");;
pico_loadResource =
(pico_Status (*)(pico_System, const char*, pico_Resource*))dlsym(
handle, "pico_loadResource");
pico_unloadResource =
(pico_Status (*)(pico_System, pico_Resource*))dlsym(
handle, "pico_unloadResource");
pico_getResourceName =
(pico_Status (*)(pico_System, pico_Resource, pico_Retstring))dlsym(
handle, "pico_getResourceName");
pico_createVoiceDefinition =
(pico_Status (*)(pico_System, const char*))dlsym(
handle, "pico_createVoiceDefinition");
pico_addResourceToVoiceDefinition =
(pico_Status (*)(pico_System, const char*, const char*))dlsym(
handle, "pico_addResourceToVoiceDefinition");
pico_releaseVoiceDefinition =
(pico_Status (*)(pico_System, const char*))dlsym(
handle, "pico_releaseVoiceDefinition");
pico_newEngine =
(pico_Status (*)(pico_System, const char*, pico_Engine*))dlsym(
handle, "pico_newEngine");
pico_disposeEngine =
(pico_Status (*)(pico_System, pico_Engine*))dlsym(
handle, "pico_disposeEngine");
pico_resetEngine =
(pico_Status (*)(pico_Engine, int32_t))dlsym(handle, "pico_resetEngine");
pico_putTextUtf8 =
(pico_Status (*)(pico_Engine, const char*, const int16_t, int16_t*))dlsym(
handle, "pico_putTextUtf8");
pico_getData =
(pico_Status (*)(pico_Engine, void*, int16_t, int16_t*, int16_t*))dlsym(
handle, "pico_getData");
mInitialized = true;
return true;
}
typedef signed int pico_Status;
typedef char pico_Retstring[PICO_RETSTRINGSIZE];
pico_Status (* pico_initialize)(void*, uint32_t, pico_System*);
pico_Status (* pico_terminate)(pico_System*);
pico_Status (* pico_getSystemStatusMessage)(
pico_System, pico_Status, pico_Retstring);
pico_Status (* pico_loadResource)(pico_System, const char*, pico_Resource*);
pico_Status (* pico_unloadResource)(pico_System, pico_Resource*);
pico_Status (* pico_getResourceName)(
pico_System, pico_Resource, pico_Retstring);
pico_Status (* pico_createVoiceDefinition)(pico_System, const char*);
pico_Status (* pico_addResourceToVoiceDefinition)(
pico_System, const char*, const char*);
pico_Status (* pico_releaseVoiceDefinition)(pico_System, const char*);
pico_Status (* pico_newEngine)(pico_System, const char*, pico_Engine*);
pico_Status (* pico_disposeEngine)(pico_System, pico_Engine*);
pico_Status (* pico_resetEngine)(pico_Engine, int32_t);
pico_Status (* pico_putTextUtf8)(
pico_Engine, const char*, const int16_t, int16_t*);
pico_Status (* pico_getData)(
pico_Engine, void*, const int16_t, int16_t*, int16_t*);
private:
bool mInitialized;
} sPicoApi;
#define PICO_ENSURE_SUCCESS_VOID(_funcName, _status) \
if (_status < 0) { \
PicoApi::pico_Retstring message; \
sPicoApi.pico_getSystemStatusMessage( \
nsPicoService::sSingleton->mPicoSystem, _status, message); \
NS_WARNING( \
nsPrintfCString("Error running %s: %s", _funcName, message).get()); \
return; \
}
#define PICO_ENSURE_SUCCESS(_funcName, _status, _rv) \
if (_status < 0) { \
PicoApi::pico_Retstring message; \
sPicoApi.pico_getSystemStatusMessage( \
nsPicoService::sSingleton->mPicoSystem, _status, message); \
NS_WARNING( \
nsPrintfCString("Error running %s: %s", _funcName, message).get()); \
return _rv; \
}
class PicoVoice
{
public:
PicoVoice(const nsAString& aLanguage)
: mLanguage(aLanguage) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PicoVoice)
// Voice language, in BCB-47 syntax
nsString mLanguage;
// Language resource file
nsCString mTaFile;
// Speaker resource file
nsCString mSgFile;
private:
~PicoVoice() {}
};
class PicoCallbackRunnable : public Runnable,
public nsISpeechTaskCallback
{
friend class PicoSynthDataRunnable;
public:
PicoCallbackRunnable(const nsAString& aText, PicoVoice* aVoice,
float aRate, float aPitch, nsISpeechTask* aTask,
nsPicoService* aService)
: mText(NS_ConvertUTF16toUTF8(aText))
, mRate(aRate)
, mPitch(aPitch)
, mFirstData(true)
, mTask(aTask)
, mVoice(aVoice)
, mService(aService) { }
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSISPEECHTASKCALLBACK
NS_IMETHOD Run() override;
bool IsCurrentTask() { return mService->mCurrentTask == mTask; }
private:
~PicoCallbackRunnable() { }
void DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>&& aBuffer,
size_t aBufferSize);
nsCString mText;
float mRate;
float mPitch;
bool mFirstData;
// We use this pointer to compare it with the current service task.
// If they differ, this runnable should stop.
nsISpeechTask* mTask;
// We hold a strong reference to the service, which in turn holds
// a strong reference to this voice.
PicoVoice* mVoice;
// By holding a strong reference to the service we guarantee that it won't be
// destroyed before this runnable.
RefPtr<nsPicoService> mService;
};
NS_IMPL_ISUPPORTS_INHERITED(PicoCallbackRunnable, Runnable, nsISpeechTaskCallback)
// Runnable
NS_IMETHODIMP
PicoCallbackRunnable::Run()
{
MOZ_ASSERT(!NS_IsMainThread());
PicoApi::pico_Status status = 0;
if (mService->CurrentVoice() != mVoice) {
mService->LoadEngine(mVoice);
} else {
status = sPicoApi.pico_resetEngine(mService->mPicoEngine, PICO_RESET_SOFT);
PICO_ENSURE_SUCCESS("pico_unloadResource", status, NS_ERROR_FAILURE);
}
// Add SSML markup for pitch and rate. Pico uses a minimal parser,
// so no namespace is needed.
nsPrintfCString markedUpText(
"<pitch level=\"%0.0f\"><speed level=\"%0.0f\">%s</speed></pitch>",
std::min(std::max(50.0f, mPitch * 100), 200.0f),
std::min(std::max(20.0f, mRate * 100), 500.0f),
mText.get());
const char* text = markedUpText.get();
size_t buffer_size = 512, buffer_offset = 0;
RefPtr<SharedBuffer> buffer = SharedBuffer::Create(buffer_size);
int16_t text_offset = 0, bytes_recv = 0, bytes_sent = 0, out_data_type = 0;
int16_t text_remaining = markedUpText.Length() + 1;
// Run this loop while this is the current task
while (IsCurrentTask()) {
if (text_remaining) {
status = sPicoApi.pico_putTextUtf8(mService->mPicoEngine,
text + text_offset, text_remaining,
&bytes_sent);
PICO_ENSURE_SUCCESS("pico_putTextUtf8", status, NS_ERROR_FAILURE);
// XXX: End speech task on error
text_remaining -= bytes_sent;
text_offset += bytes_sent;
} else {
// If we already fed all the text to the engine, send a zero length buffer
// and quit.
DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(), 0);
break;
}
do {
// Run this loop while the result of getData is STEP_BUSY, when it finishes
// synthesizing audio for the given text, it returns STEP_IDLE. We then
// break to the outer loop and feed more text, if there is any left.
if (!IsCurrentTask()) {
// If the task has changed, quit.
break;
}
if (buffer_size - buffer_offset < PICO_MAX_CHUNK_SIZE) {
// The next audio chunk retrieved may be bigger than our buffer,
// so send the data and flush the buffer.
DispatchSynthDataRunnable(buffer.forget(), buffer_offset);
buffer_offset = 0;
buffer = SharedBuffer::Create(buffer_size);
}
status = sPicoApi.pico_getData(mService->mPicoEngine,
(uint8_t*)buffer->Data() + buffer_offset,
PICO_MAX_CHUNK_SIZE,
&bytes_recv, &out_data_type);
PICO_ENSURE_SUCCESS("pico_getData", status, NS_ERROR_FAILURE);
buffer_offset += bytes_recv;
} while (status == PICO_STEP_BUSY);
}
return NS_OK;
}
void
PicoCallbackRunnable::DispatchSynthDataRunnable(
already_AddRefed<SharedBuffer>&& aBuffer, size_t aBufferSize)
{
class PicoSynthDataRunnable final : public Runnable
{
public:
PicoSynthDataRunnable(already_AddRefed<SharedBuffer>& aBuffer,
size_t aBufferSize, bool aFirstData,
PicoCallbackRunnable* aCallback)
: mBuffer(aBuffer)
, mBufferSize(aBufferSize)
, mFirstData(aFirstData)
, mCallback(aCallback) {
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (!mCallback->IsCurrentTask()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsISpeechTask* task = mCallback->mTask;
if (mFirstData) {
task->Setup(mCallback, PICO_CHANNELS_NUM, PICO_SAMPLE_RATE, 2);
}
return task->SendAudioNative(
mBufferSize ? static_cast<short*>(mBuffer->Data()) : nullptr, mBufferSize / 2);
}
private:
RefPtr<SharedBuffer> mBuffer;
size_t mBufferSize;
bool mFirstData;
RefPtr<PicoCallbackRunnable> mCallback;
};
nsCOMPtr<nsIRunnable> sendEvent =
new PicoSynthDataRunnable(aBuffer, aBufferSize, mFirstData, this);
NS_DispatchToMainThread(sendEvent);
mFirstData = false;
}
// nsISpeechTaskCallback
NS_IMETHODIMP
PicoCallbackRunnable::OnPause()
{
return NS_OK;
}
NS_IMETHODIMP
PicoCallbackRunnable::OnResume()
{
return NS_OK;
}
NS_IMETHODIMP
PicoCallbackRunnable::OnCancel()
{
mService->mCurrentTask = nullptr;
return NS_OK;
}
NS_IMETHODIMP
PicoCallbackRunnable::OnVolumeChanged(float aVolume)
{
return NS_OK;
}
NS_INTERFACE_MAP_BEGIN(nsPicoService)
NS_INTERFACE_MAP_ENTRY(nsISpeechService)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsPicoService)
NS_IMPL_RELEASE(nsPicoService)
nsPicoService::nsPicoService()
: mInitialized(false)
, mVoicesMonitor("nsPicoService::mVoices")
, mCurrentTask(nullptr)
, mPicoSystem(nullptr)
, mPicoEngine(nullptr)
, mSgResource(nullptr)
, mTaResource(nullptr)
, mPicoMemArea(nullptr)
{
}
nsPicoService::~nsPicoService()
{
// We don't worry about removing the voices because this gets
// destructed at shutdown along with the voice registry.
MonitorAutoLock autoLock(mVoicesMonitor);
mVoices.Clear();
if (mThread) {
mThread->Shutdown();
}
UnloadEngine();
}
// nsIObserver
NS_IMETHODIMP
nsPicoService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
return NS_ERROR_UNEXPECTED;
}
if (!Preferences::GetBool("media.webspeech.synth.enabled") ||
Preferences::GetBool("media.webspeech.synth.test")) {
return NS_OK;
}
DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
MOZ_ASSERT(NS_SUCCEEDED(rv));
return mThread->Dispatch(
NewRunnableMethod("nsPicoService::Init", this, &nsPicoService::Init), NS_DISPATCH_NORMAL);
}
// nsISpeechService
NS_IMETHODIMP
nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
float aVolume, float aRate, float aPitch,
nsISpeechTask* aTask)
{
if(NS_WARN_IF(!(mInitialized))) {
return NS_ERROR_NOT_AVAILABLE;
}
MonitorAutoLock autoLock(mVoicesMonitor);
bool found = false;
PicoVoice* voice = mVoices.GetWeak(aUri, &found);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
mCurrentTask = aTask;
RefPtr<PicoCallbackRunnable> cb = new PicoCallbackRunnable(aText, voice, aRate, aPitch, aTask, this);
return mThread->Dispatch(cb, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
nsPicoService::GetServiceType(SpeechServiceType* aServiceType)
{
*aServiceType = nsISpeechService::SERVICETYPE_DIRECT_AUDIO;
return NS_OK;
}
// private methods
void
nsPicoService::Init()
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mInitialized);
if (!sPicoApi.Init()) {
NS_WARNING("Failed to initialize pico library");
return;
}
// Use environment variable, or default android path
nsAutoCString langPath(PR_GetEnv("PICO_LANG_PATH"));
if (langPath.IsEmpty()) {
langPath.AssignLiteral(PICO_LANG_PATH);
}
nsCOMPtr<nsIFile> voicesDir;
NS_NewNativeLocalFile(langPath, true, getter_AddRefs(voicesDir));
nsCOMPtr<nsISimpleEnumerator> dirIterator;
nsresult rv = voicesDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) {
NS_WARNING(nsPrintfCString("Failed to get contents of directory: %s", langPath.get()).get());
return;
}
bool hasMoreElements = false;
rv = dirIterator->HasMoreElements(&hasMoreElements);
MOZ_ASSERT(NS_SUCCEEDED(rv));
MonitorAutoLock autoLock(mVoicesMonitor);
while (hasMoreElements && NS_SUCCEEDED(rv)) {
nsCOMPtr<nsISupports> supports;
rv = dirIterator->GetNext(getter_AddRefs(supports));
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIFile> voiceFile = do_QueryInterface(supports);
MOZ_ASSERT(voiceFile);
nsAutoCString leafName;
voiceFile->GetNativeLeafName(leafName);
nsAutoString lang;
if (GetVoiceFileLanguage(leafName, lang)) {
nsAutoString uri;
uri.AssignLiteral("urn:moz-tts:pico:");
uri.Append(lang);
bool found = false;
PicoVoice* voice = mVoices.GetWeak(uri, &found);
if (!found) {
voice = new PicoVoice(lang);
mVoices.Put(uri, voice);
}
// Each voice consists of two lingware files: A language resource file,
// suffixed by _ta.bin, and a speaker resource file, suffixed by _sb.bin.
// We currently assume that there is a pair of files for each language.
if (StringEndsWith(leafName, NS_LITERAL_CSTRING("_ta.bin"))) {
rv = voiceFile->GetPersistentDescriptor(voice->mTaFile);
MOZ_ASSERT(NS_SUCCEEDED(rv));
} else if (StringEndsWith(leafName, NS_LITERAL_CSTRING("_sg.bin"))) {
rv = voiceFile->GetPersistentDescriptor(voice->mSgFile);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
rv = dirIterator->HasMoreElements(&hasMoreElements);
}
NS_DispatchToMainThread(NewRunnableMethod("nsPicoService::RegisterVoices",
this, &nsPicoService::RegisterVoices));
}
void
nsPicoService::RegisterVoices()
{
nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
for (auto iter = mVoices.Iter(); !iter.Done(); iter.Next()) {
const nsAString& uri = iter.Key();
RefPtr<PicoVoice>& voice = iter.Data();
// If we are missing either a language or a voice resource, it is invalid.
if (voice->mTaFile.IsEmpty() || voice->mSgFile.IsEmpty()) {
iter.Remove();
continue;
}
nsAutoString name;
name.AssignLiteral("Pico ");
name.Append(voice->mLanguage);
// This service is multi-threaded and can handle more than one utterance at a
// time before previous utterances end. So, aQueuesUtterances == false
DebugOnly<nsresult> rv =
registry->AddVoice(this, uri, name, voice->mLanguage, true, false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to add voice");
}
mInitialized = true;
}
bool
nsPicoService::GetVoiceFileLanguage(const nsACString& aFileName, nsAString& aLang)
{
nsACString::const_iterator start, end;
aFileName.BeginReading(start);
aFileName.EndReading(end);
// The lingware filename syntax is language_(ta/sg).bin,
// we extract the language prefix here.
if (FindInReadable(NS_LITERAL_CSTRING("_"), start, end)) {
end = start;
aFileName.BeginReading(start);
aLang.Assign(NS_ConvertUTF8toUTF16(Substring(start, end)));
return true;
}
return false;
}
void
nsPicoService::LoadEngine(PicoVoice* aVoice)
{
PicoApi::pico_Status status = 0;
if (mPicoSystem) {
UnloadEngine();
}
if (!mPicoMemArea) {
mPicoMemArea = MakeUnique<uint8_t[]>(PICO_MEM_SIZE);
}
status = sPicoApi.pico_initialize(mPicoMemArea.get(),
PICO_MEM_SIZE, &mPicoSystem);
PICO_ENSURE_SUCCESS_VOID("pico_initialize", status);
status = sPicoApi.pico_loadResource(mPicoSystem, aVoice->mTaFile.get(), &mTaResource);
PICO_ENSURE_SUCCESS_VOID("pico_loadResource", status);
status = sPicoApi.pico_loadResource(mPicoSystem, aVoice->mSgFile.get(), &mSgResource);
PICO_ENSURE_SUCCESS_VOID("pico_loadResource", status);
status = sPicoApi.pico_createVoiceDefinition(mPicoSystem, PICO_VOICE_NAME);
PICO_ENSURE_SUCCESS_VOID("pico_createVoiceDefinition", status);
char taName[PICO_RETSTRINGSIZE];
status = sPicoApi.pico_getResourceName(mPicoSystem, mTaResource, taName);
PICO_ENSURE_SUCCESS_VOID("pico_getResourceName", status);
status = sPicoApi.pico_addResourceToVoiceDefinition(
mPicoSystem, PICO_VOICE_NAME, taName);
PICO_ENSURE_SUCCESS_VOID("pico_addResourceToVoiceDefinition", status);
char sgName[PICO_RETSTRINGSIZE];
status = sPicoApi.pico_getResourceName(mPicoSystem, mSgResource, sgName);
PICO_ENSURE_SUCCESS_VOID("pico_getResourceName", status);
status = sPicoApi.pico_addResourceToVoiceDefinition(
mPicoSystem, PICO_VOICE_NAME, sgName);
PICO_ENSURE_SUCCESS_VOID("pico_addResourceToVoiceDefinition", status);
status = sPicoApi.pico_newEngine(mPicoSystem, PICO_VOICE_NAME, &mPicoEngine);
PICO_ENSURE_SUCCESS_VOID("pico_newEngine", status);
if (sSingleton) {
sSingleton->mCurrentVoice = aVoice;
}
}
void
nsPicoService::UnloadEngine()
{
PicoApi::pico_Status status = 0;
if (mPicoEngine) {
status = sPicoApi.pico_disposeEngine(mPicoSystem, &mPicoEngine);
PICO_ENSURE_SUCCESS_VOID("pico_disposeEngine", status);
status = sPicoApi.pico_releaseVoiceDefinition(mPicoSystem, PICO_VOICE_NAME);
PICO_ENSURE_SUCCESS_VOID("pico_releaseVoiceDefinition", status);
mPicoEngine = nullptr;
}
if (mSgResource) {
status = sPicoApi.pico_unloadResource(mPicoSystem, &mSgResource);
PICO_ENSURE_SUCCESS_VOID("pico_unloadResource", status);
mSgResource = nullptr;
}
if (mTaResource) {
status = sPicoApi.pico_unloadResource(mPicoSystem, &mTaResource);
PICO_ENSURE_SUCCESS_VOID("pico_unloadResource", status);
mTaResource = nullptr;
}
if (mPicoSystem) {
status = sPicoApi.pico_terminate(&mPicoSystem);
PICO_ENSURE_SUCCESS_VOID("pico_terminate", status);
mPicoSystem = nullptr;
}
}
PicoVoice*
nsPicoService::CurrentVoice()
{
MOZ_ASSERT(!NS_IsMainThread());
return mCurrentVoice;
}
// static methods
nsPicoService*
nsPicoService::GetInstance()
{
MOZ_ASSERT(NS_IsMainThread());
if (!XRE_IsParentProcess()) {
MOZ_ASSERT(false, "nsPicoService can only be started on main gecko process");
return nullptr;
}
if (!sSingleton) {
sSingleton = new nsPicoService();
}
return sSingleton;
}
already_AddRefed<nsPicoService>
nsPicoService::GetInstanceForService()
{
RefPtr<nsPicoService> picoService = GetInstance();
return picoService.forget();
}
void
nsPicoService::Shutdown()
{
if (!sSingleton) {
return;
}
sSingleton->mCurrentTask = nullptr;
sSingleton = nullptr;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,93 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsPicoService_h
#define nsPicoService_h
#include "mozilla/Mutex.h"
#include "nsTArray.h"
#include "nsIObserver.h"
#include "nsIThread.h"
#include "nsISpeechService.h"
#include "nsRefPtrHashtable.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Monitor.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace dom {
class PicoVoice;
class PicoCallbackRunnable;
typedef void* pico_System;
typedef void* pico_Resource;
typedef void* pico_Engine;
class nsPicoService : public nsIObserver,
public nsISpeechService
{
friend class PicoCallbackRunnable;
friend class PicoInitRunnable;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISPEECHSERVICE
NS_DECL_NSIOBSERVER
nsPicoService();
static nsPicoService* GetInstance();
static already_AddRefed<nsPicoService> GetInstanceForService();
static void Shutdown();
private:
virtual ~nsPicoService();
void Init();
void RegisterVoices();
bool GetVoiceFileLanguage(const nsACString& aFileName, nsAString& aLang);
void LoadEngine(PicoVoice* aVoice);
void UnloadEngine();
PicoVoice* CurrentVoice();
bool mInitialized;
nsCOMPtr<nsIThread> mThread;
nsRefPtrHashtable<nsStringHashKey, PicoVoice> mVoices;
Monitor mVoicesMonitor;
PicoVoice* mCurrentVoice;
Atomic<nsISpeechTask*> mCurrentTask;
pico_System mPicoSystem;
pico_Engine mPicoEngine;
pico_Resource mSgResource;
pico_Resource mTaResource;
mozilla::UniquePtr<uint8_t[]> mPicoMemArea;
static StaticRefPtr<nsPicoService> sSingleton;
};
} // namespace dom
} // namespace mozilla
#endif

View File

@ -2572,8 +2572,6 @@ if test -n "$MOZ_WEBRTC"; then
if test -n "$MOZ_X11"; then if test -n "$MOZ_X11"; then
MOZ_WEBRTC_X11_LIBS="-lXext -lXdamage -lXfixes -lXcomposite" MOZ_WEBRTC_X11_LIBS="-lXext -lXdamage -lXfixes -lXcomposite"
fi fi
else
MOZ_SYNTH_PICO=
fi fi
dnl ======================================================== dnl ========================================================
@ -3965,18 +3963,6 @@ if test -n "$MOZ_DISABLE_STARTUPCACHE"; then
fi fi
AC_SUBST(MOZ_DISABLE_STARTUPCACHE) AC_SUBST(MOZ_DISABLE_STARTUPCACHE)
dnl ========================================================
dnl = Enable Pico Speech Synthesis
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(synth-pico,
[ --enable-synth-pico Set compile flags necessary for compiling Pico Web Speech API ],
MOZ_SYNTH_PICO=1,
MOZ_SYNTH_PICO= )
if test -n "$MOZ_SYNTH_PICO"; then
AC_DEFINE(MOZ_SYNTH_PICO)
fi
AC_SUBST(MOZ_SYNTH_PICO)
dnl ======================================================== dnl ========================================================
dnl = Enable Support for Time Manager API dnl = Enable Support for Time Manager API
dnl ======================================================== dnl ========================================================