blob: 7320381547dd25fbf91eaa4e1d9ca9bbc2aec8d3 [file] [log] [blame]
/*
* 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);
for (var i = 0; i < length; i++) {
data[i] = getValue(ptr + i*4, 'float');
}
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 = addFunction(cb);
this.synth_(aText, fp);
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 = Module.postRun || [];
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);
}
}