sc-voice
Version:
SuttaCentral Voice
200 lines (186 loc) • 7.22 kB
JavaScript
(function(exports) {
const fs = require('fs');
const path = require('path');
const { logger, } = require('log-instance');
const { MerkleJson } = require('merkle-json');
const AbstractTTS = require('./abstract-tts');
const SoundStore = require('./sound-store');
const SCAudio = require('./sc-audio');
const SRC = path.join(__dirname, '..');
const ASSETS = path.join(SRC, 'assets');
const NOAUDIOPATH = path.join(ASSETS, 'no_audio.mp3');
class HumanTts extends AbstractTTS {
constructor(opts={}) {
super((opts = HumanTts.options(opts)));
this.voice = opts.voice;
this.api = opts.api;
this.apiVersion = opts.apiVersion;
this.language = opts.language;
this.localeIPA = opts.localeIPA;
this.noAudioPath = opts.noAudioPath;
this.scAudio = opts.scAudio;
this.altTts = opts.altTts;
this.mj = opts.mj || new MerkleJson({
hashTag: 'guid',
});
this.prosody = {
rate: "0%",
};
logger.info([
`HumanTts.ctor()`,
`voice:${this.voice}`,
`language:${this.language}`,
`noAudioPath:${this.noAudioPath}`,
`scAudio:${!!this.scAudio})`,
].join(' '));
}
static options(opts) {
var language = opts.language || 'pli';
var newOpts = Object.assign({
noAudioPath: NOAUDIOPATH,
audioFormat: 'mp3', // iPhone does not support OGG
audioSuffix: '.mp3', // iPhone does not support OGG
voice: 'sujato_pli',
language,
localeIPA: language,
api: 'human-tts',
apiVersion: 'v1',
}, opts);
return newOpts;
}
synthesizeBreak(index) {
var {
altTts,
} = this;
return altTts
? altTts.synthesizeBreak(index)
: super.syntthesizeBreak(index);
}
serviceSynthesize(resolve, reject, request) {
var that = this;
var params = {
Text:`<speak>${request.ssml}</speak>`,
TextType: 'ssml',
OutputFormat: that.audioFormat,
VoiceId: that.voice,
LanguageCode: that.language,
}
logger.info(`HumanTts.serviceSynthesize(`+
`${this.voice}) ${JSON.stringify(request)}`);
that.synthesizeResponse(resolve, reject, request);
}
synthesizeSegment(opts={}) {
var {
segment,
scAudio,
altTts,
language,
soundStore,
translator,
usage,
volume,
downloadAudio,
} = opts;
var that = this;
if (segment == null) {
return Promise.reject(new Error(
`synthesizeSegment() segment is required`));
}
if (language == null) {
return Promise.reject(new Error(
`synthesizeSegment() language is required`));
}
downloadAudio = !(downloadAudio === false);
altTts = altTts || this.altTts;
var {
voice,
mj,
noAudioPath,
} = this;
soundStore = soundStore || this.soundStore;
if (soundStore == null) {
return Promise.reject(new Error(
`synthesizeSegment() soundStore is required`));
}
scAudio = scAudio || this.scAudio;
if (scAudio == null) {
return Promise.reject(new Error(
`synthesizeSegment() scAudio is required`));
}
var scid = segment.scid;
var suttaSegId = scid;
var author = translator;
var signature = {
api: 'human-tts',
suttaSegId,
language,
author,
reader: voice,
volume, // TODO remove
scAudioVersion: SCAudio.VERSION,
};
var result = {
signature,
}
var guid = signature[mj.hashTag] = mj.hash(signature);
var audioPath = soundStore
.signaturePath(signature, scAudio.extSeg); // .webm
var soundPath = soundStore.signaturePath(signature); // .mp3
var stats = fs.existsSync(soundPath) && fs.statSync(soundPath);
if (stats && stats.size > this.ERROR_SIZE) {
result.file = soundPath;
return Promise.resolve(result);
}
usage = usage || this.usage;
var text = segment[language.split('-')[0]] || '(no text)';
var altVolume = altTts && SoundStore.suttaVolumeName(
suttaSegId, language, translator, altTts.voice);
var altArgs = {
scid,
language,
usage,
volume: altVolume || volume,
};
if (altTts == null || downloadAudio) {
return new Promise((resolve, reject) => {
(async function() { try {
var scaResult = await scAudio.downloadSegmentAudio({
suttaSegId,
language,
author,
audioPath,
});
if (fs.existsSync(soundPath)) {
result.file = soundPath;
resolve(result);
} else {
result.file = noAudioPath;
reject(new Error(`no ${language} audio `+
`file:${soundPath} for:${suttaSegId}`
));
}
} catch(e) {
if (altTts == null) {
logger.warn(`synthesizeSegment() failed `+
`with no altTts`);
reject(e);
} else {
var resAlt = await altTts
.synthesizeText(text, altArgs);
resAlt.altTts = altTts.voice;
resolve(resAlt);
}
} })();
});
}
return new Promise((resolve, reject) => {
(async function() { try {
var resAlt = await altTts.synthesizeText(text, altArgs);
resAlt.altTts = altTts.voice;
resolve(resAlt);
} catch(e) {reject(e);} })();
});
}
}
module.exports = exports.HumanTts = HumanTts;
})(typeof exports === "object" ? exports : (exports = {}));