@@ -52,6 +52,12 @@ | |||
display: block; | |||
margin-bottom: 1rem; | |||
} | |||
#ipaarea { | |||
width: 100%; | |||
height: 8rem; | |||
display: block; | |||
margin-bottom: 1rem; | |||
} | |||
p.gh { | |||
color: #333; | |||
text-align: right; | |||
@@ -100,6 +106,7 @@ | |||
<h1>espeakng.js 1.49.1 Demo</h1> | |||
<form> | |||
<textarea id="texttospeak">Call me Ishmael. Some years ago --- never mind how long precisely --- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation.</textarea> | |||
<textarea readonly="true" placeholder="IPA" id="ipaarea"></textarea> | |||
<div class="speecharg"> | |||
<label for="pitch">Pitch</label><input id="pitch" type="range" value="50" min="0" max="100" /><button type="button" aria-label="Reset pitch" title="Reset pitch" onclick="resetPitch();">↶</button> | |||
</div> | |||
@@ -110,7 +117,7 @@ | |||
<label for="voice">Voice</label><select id="voice"></select><button type="button" aria-label="Reset voice" title="Reset voice" onclick="resetVoice();">↶</button> | |||
</div> | |||
<div class="bottom"> | |||
<button type="button" onmousedown="speak();">Speak</button> | |||
<button type="button" onmousedown="speakAndIpa();">Speak</button> | |||
<button type="button" onmousedown="stop();">Stop</button> | |||
</div> | |||
<p class="gh"><a href="https://github.com/eeejay/espeak/tree/emscripten">Original Code for eSpeak: Eitan Isaacson</a></p> |
@@ -50,6 +50,35 @@ public: | |||
espeak_SetSynthCallback(NULL); | |||
} | |||
int synth_ipa_(const char* aText, const char* virtualFileName) { | |||
/* phoneme_mode | |||
bit 1: 0=eSpeak's ascii phoneme names, 1= International Phonetic Alphabet (as UTF-8 characters). | |||
bit 7: use (bits 8-23) as a tie within multi-letter phonemes names | |||
bits 8-23: separator character, between phoneme names | |||
*/ | |||
espeak_SetSynthCallback(NULL); | |||
int phoneme_options = (1 << 1); // Use IPA | |||
int use_custom_phoneme_separator = (0 << 7); | |||
int phonemes_separator = ' '; // Use a default value | |||
int phoneme_conf = phoneme_options | (phonemes_separator << 8); | |||
FILE* f_phonemes_out = fopen(virtualFileName,"wb"); | |||
if(!f_phonemes_out) | |||
return -1; | |||
//espeak_ng_InitializeOutput(ENOUTPUT_MODE_SYNCHRONOUS, 0, NULL); | |||
espeak_SetPhonemeTrace(phoneme_conf, f_phonemes_out); | |||
espeak_Synth(aText, 0, 0, POS_CHARACTER, 0, 0, NULL, NULL); | |||
espeak_SetPhonemeTrace(0, NULL); | |||
fclose(f_phonemes_out); | |||
return 0; | |||
} | |||
long set_voice( | |||
const char* aName, | |||
const char* aLang=NULL, |
@@ -52,6 +52,7 @@ interface espeak_EVENT { | |||
interface eSpeakNGWorker { | |||
void eSpeakNGWorker(); | |||
void synth_(DOMString aText, VoidPtr aCallback); | |||
long synth_ipa_(DOMString aText, DOMString virtualFileName); | |||
long getSizeOfEventStruct_(); | |||
long set_voice(DOMString aName, DOMString aLang, optional octet gender=0, optional octet age=0, optional octet aVariant=0); | |||
[Const] attribute espeak_VOICE[] voices; |
@@ -179,10 +179,12 @@ function speak() { | |||
pusher.connect(ctx.destination); | |||
console.log(' Creating pusher... done'); | |||
var user_text = document.getElementById('texttospeak').value; | |||
// actual synthesis | |||
console.log(' Calling synthesize...'); | |||
tts.synthesize( | |||
document.getElementById('texttospeak').value, | |||
user_text, | |||
function cb(samples, events) { | |||
//console.log(' Inside synt cb'); | |||
if (!samples) { | |||
@@ -203,11 +205,32 @@ function speak() { | |||
//console.log(' Leaving synt cb'); | |||
} // end of function cb | |||
); // end of tts.synthesize() | |||
console.log(' Calling synthesize... done'); | |||
console.log(' Calling synthesize... done'); | |||
console.log('Leaving speak()'); | |||
} // end of speak() | |||
function ipa() { | |||
console.log("Synthesizing ipa ... "); | |||
var ts = new Date(); | |||
var user_text = document.getElementById('texttospeak').value; | |||
//user_text = user_text.repeat(50); | |||
tts.set_voice(document.getElementById('voice').value); | |||
tts.synthesize_ipa(user_text, function(result) { | |||
var te = new Date(); | |||
document.getElementById('ipaarea').value = result.ipa; | |||
console.log("Ipa synthesis done in " + (te-ts) + " ms.") | |||
}); | |||
} | |||
function speakAndIpa() { | |||
speak(); | |||
ipa(); | |||
} | |||
function resetPitch() { | |||
document.getElementById('pitch').value = 50; | |||
} | |||
@@ -254,5 +277,6 @@ function initializeDemo() { | |||
console.log('Leaving cb1'); | |||
} // end of function cb1 | |||
); | |||
console.log('Creating eSpeakNG instance... done'); | |||
} |
@@ -63,7 +63,8 @@ for (var method of [ | |||
'set_rate', | |||
'set_pitch', | |||
'set_voice', | |||
'synthesize' | |||
'synthesize', | |||
'synthesize_ipa' | |||
]) { | |||
eSpeakNG.prototype[method] = _createAsyncMethod(method); | |||
} |
@@ -93,6 +93,28 @@ eSpeakNGWorker.prototype.synthesize = function (aText, aCallback) { | |||
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') { | |||
@@ -104,6 +126,7 @@ if (typeof WorkerGlobalScope !== 'undefined') { | |||
}); | |||
onmessage = function(e) { | |||
if (!worker) { | |||
throw 'eSpeakNGWorker worker not initialized'; | |||
} |