display: block; | display: block; | ||||
margin-bottom: 1rem; | margin-bottom: 1rem; | ||||
} | } | ||||
#ipaarea { | |||||
width: 100%; | |||||
height: 8rem; | |||||
display: block; | |||||
margin-bottom: 1rem; | |||||
} | |||||
p.gh { | p.gh { | ||||
color: #333; | color: #333; | ||||
text-align: right; | text-align: right; | ||||
<h1>espeakng.js 1.49.1 Demo</h1> | <h1>espeakng.js 1.49.1 Demo</h1> | ||||
<form> | <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 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"> | <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> | <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> | </div> | ||||
<label for="voice">Voice</label><select id="voice"></select><button type="button" aria-label="Reset voice" title="Reset voice" onclick="resetVoice();">↶</button> | <label for="voice">Voice</label><select id="voice"></select><button type="button" aria-label="Reset voice" title="Reset voice" onclick="resetVoice();">↶</button> | ||||
</div> | </div> | ||||
<div class="bottom"> | <div class="bottom"> | ||||
<button type="button" onmousedown="speak();">Speak</button> | |||||
<button type="button" onmousedown="speakAndIpa();">Speak</button> | |||||
<button type="button" onmousedown="stop();">Stop</button> | <button type="button" onmousedown="stop();">Stop</button> | ||||
</div> | </div> | ||||
<p class="gh"><a href="https://github.com/eeejay/espeak/tree/emscripten">Original Code for eSpeak: Eitan Isaacson</a></p> | <p class="gh"><a href="https://github.com/eeejay/espeak/tree/emscripten">Original Code for eSpeak: Eitan Isaacson</a></p> |
espeak_SetSynthCallback(NULL); | 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( | long set_voice( | ||||
const char* aName, | const char* aName, | ||||
const char* aLang=NULL, | const char* aLang=NULL, |
interface eSpeakNGWorker { | interface eSpeakNGWorker { | ||||
void eSpeakNGWorker(); | void eSpeakNGWorker(); | ||||
void synth_(DOMString aText, VoidPtr aCallback); | void synth_(DOMString aText, VoidPtr aCallback); | ||||
long synth_ipa_(DOMString aText, DOMString virtualFileName); | |||||
long getSizeOfEventStruct_(); | long getSizeOfEventStruct_(); | ||||
long set_voice(DOMString aName, DOMString aLang, optional octet gender=0, optional octet age=0, optional octet aVariant=0); | long set_voice(DOMString aName, DOMString aLang, optional octet gender=0, optional octet age=0, optional octet aVariant=0); | ||||
[Const] attribute espeak_VOICE[] voices; | [Const] attribute espeak_VOICE[] voices; |
pusher.connect(ctx.destination); | pusher.connect(ctx.destination); | ||||
console.log(' Creating pusher... done'); | console.log(' Creating pusher... done'); | ||||
var user_text = document.getElementById('texttospeak').value; | |||||
// actual synthesis | // actual synthesis | ||||
console.log(' Calling synthesize...'); | console.log(' Calling synthesize...'); | ||||
tts.synthesize( | tts.synthesize( | ||||
document.getElementById('texttospeak').value, | |||||
user_text, | |||||
function cb(samples, events) { | function cb(samples, events) { | ||||
//console.log(' Inside synt cb'); | //console.log(' Inside synt cb'); | ||||
if (!samples) { | if (!samples) { | ||||
//console.log(' Leaving synt cb'); | //console.log(' Leaving synt cb'); | ||||
} // end of function cb | } // end of function cb | ||||
); // end of tts.synthesize() | ); // end of tts.synthesize() | ||||
console.log(' Calling synthesize... done'); | |||||
console.log(' Calling synthesize... done'); | |||||
console.log('Leaving speak()'); | console.log('Leaving speak()'); | ||||
} // end of 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() { | function resetPitch() { | ||||
document.getElementById('pitch').value = 50; | document.getElementById('pitch').value = 50; | ||||
} | } | ||||
console.log('Leaving cb1'); | console.log('Leaving cb1'); | ||||
} // end of function cb1 | } // end of function cb1 | ||||
); | ); | ||||
console.log('Creating eSpeakNG instance... done'); | console.log('Creating eSpeakNG instance... done'); | ||||
} | } |
'set_rate', | 'set_rate', | ||||
'set_pitch', | 'set_pitch', | ||||
'set_voice', | 'set_voice', | ||||
'synthesize' | |||||
'synthesize', | |||||
'synthesize_ipa' | |||||
]) { | ]) { | ||||
eSpeakNG.prototype[method] = _createAsyncMethod(method); | eSpeakNG.prototype[method] = _createAsyncMethod(method); | ||||
} | } |
Runtime.removeFunction(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 | // Make this a worker | ||||
if (typeof WorkerGlobalScope !== 'undefined') { | if (typeof WorkerGlobalScope !== 'undefined') { | ||||
}); | }); | ||||
onmessage = function(e) { | onmessage = function(e) { | ||||
if (!worker) { | if (!worker) { | ||||
throw 'eSpeakNGWorker worker not initialized'; | throw 'eSpeakNGWorker worker not initialized'; | ||||
} | } |