| @@ -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'; | |||
| } | |||