mirror of
https://github.com/rhasspy/espeak-ng.git
synced 2026-10-31 05:35:35 +00:00
147 lines
3.8 KiB
JavaScript
147 lines
3.8 KiB
JavaScript
/*
|
|
* Copyright (C) 2014-2017 Eitan Isaacson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see: <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
eSpeakNGWorker.prototype.list_voices = function() {
|
|
var voices = [];
|
|
var i;
|
|
for (var voice = this.get_voices(i = 0); voice.ptr != 0; voice = this.get_voices(++i)) {
|
|
var v = {
|
|
name: voice.get_name(),
|
|
identifier: voice.get_identifier(),
|
|
languages: [],
|
|
}
|
|
|
|
var ii = 0;
|
|
var byte = voice.get_languages(ii);
|
|
|
|
function nullTerminatedString(offset) {
|
|
var str = '';
|
|
var index = offset;
|
|
var b = voice.get_languages(index++);
|
|
while (b != 0) {
|
|
str += String.fromCharCode(b);
|
|
b = voice.get_languages(index++);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
while (byte != 0) {
|
|
var lang = { priority: byte, name: nullTerminatedString(++ii) }
|
|
v.languages.push(lang);
|
|
ii += lang.name.length + 1;
|
|
byte = voice.get_languages(ii);
|
|
}
|
|
|
|
voices.push(v);
|
|
|
|
}
|
|
return voices;
|
|
};
|
|
|
|
var eventTypes = [
|
|
'list_terminated',
|
|
'word',
|
|
'sentence',
|
|
'mark',
|
|
'play',
|
|
'end',
|
|
'msg_terminated',
|
|
'phoneme',
|
|
'samplerate'
|
|
]
|
|
|
|
eSpeakNGWorker.prototype.synthesize = function (aText, aCallback) {
|
|
var eventStructSize = this.getSizeOfEventStruct_();
|
|
function cb(ptr, length, events_pointer) {
|
|
var data = new Float32Array(length*2);
|
|
for (var i = 0; i < length; i++) {
|
|
data[i*2] = Math.max(-1, Math.min(1, getValue(ptr + i*2, 'i16') / 32768));
|
|
data[i*2+1] = data[i*2];
|
|
}
|
|
var events = [];
|
|
var ptr = events_pointer;
|
|
for (ev = wrapPointer(ptr, espeak_EVENT);
|
|
ev.get_type() != Module.espeakEVENT_LIST_TERMINATED;
|
|
ev = wrapPointer((ptr += eventStructSize), espeak_EVENT)) {
|
|
events.push({
|
|
type: eventTypes[ev.get_type()],
|
|
text_position: ev.get_text_position(),
|
|
word_length: ev.get_length(),
|
|
audio_position: ev.get_audio_position()
|
|
});
|
|
}
|
|
return aCallback(data, events) ? 1 : 0;
|
|
}
|
|
|
|
var fp = Runtime.addFunction(cb);
|
|
this.synth_(aText, fp);
|
|
Runtime.removeFunction(fp);
|
|
};
|
|
|
|
eSpeakNGWorker.prototype.synthesize_ipa = function (aText, aCallback) {
|
|
|
|
// Use a unique temp file for the worker. Avoid collisions, just in case.
|
|
var ipaVirtualFileName = "espeak-ng-ipa-tmp-" + Math.random().toString().substring(2);
|
|
|
|
var res = "";
|
|
var code = this.synth_ipa_(aText, ipaVirtualFileName);
|
|
|
|
if(code == 0)
|
|
res = FS.readFile(ipaVirtualFileName, { encoding: 'utf8' })
|
|
|
|
// Clean up the tmp file
|
|
FS.unlink(ipaVirtualFileName);
|
|
|
|
var ret = {
|
|
code: code,
|
|
ipa: res
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
// Make this a worker
|
|
|
|
if (typeof WorkerGlobalScope !== 'undefined') {
|
|
var worker;
|
|
|
|
Module.postRun.push(function () {
|
|
worker = new eSpeakNGWorker();
|
|
postMessage('ready');
|
|
});
|
|
|
|
onmessage = function(e) {
|
|
|
|
if (!worker) {
|
|
throw 'eSpeakNGWorker worker not initialized';
|
|
}
|
|
var args = e.data.args;
|
|
var message = { callback: e.data.callback, done: true };
|
|
if (e.data.method == 'synthesize') {
|
|
args.push(function(samples, events) {
|
|
postMessage(
|
|
{ callback: e.data.callback,
|
|
result: [samples.buffer, events] }, [samples.buffer]);
|
|
});
|
|
}
|
|
message.result = [worker[e.data.method].apply(worker, args)];
|
|
if (e.data.callback)
|
|
postMessage(message);
|
|
}
|
|
}
|