HTML <form> input blocked after WASM loads - html

Please could somebody help me to resolve this problem?
I have canvas, inserted in html and drawn usng WebAssembly in C, however it seems to block the HTML form input fields - I cannot type anything once the wasm module is loaded and runs...
I use emscripten_set_main_loop_arg() in C instead, of requestAnimationFrame() in JS:
const int simulate_infinite_loop = 1; // call the function repeatedly
const int fps = -1; // call the function as fast as the browser wants to render (typically 60fps)
emscripten_set_main_loop_arg(render, &cbp, fps, simulate_infinite_loop);
Later, I insert it in HTML:
<script type='text/javascript'>
var Module = {};
fetch('app/aghdr.wasm')
.then(response =>
response.arrayBuffer()
).then(buffer => {
Module.canvas = document.getElementById("canvas");
Module.wasmBinary = buffer;
var script = document.createElement('script');
script.src = "app/aghdr.js";
script.onload = function() {
console.log("Emscripten boilerplate loaded.")
}
document.body.appendChild(script);
});
</script>
Does anybody know, hot to ensure that normal HTML form processes messages while WASM module is running?
See: http://inters.cloud/test3/

Perhaps it's caused by emscripten_set_keypress_callback().
When a argument callback returns non-zero, event.preventDefault() blocks a keypress event.

Related

Is this possible to send and get back the value from google app script to html page without rendering html output?

After much discussion and R&D, image cropping is not possible with Google APP scripts. So I decided to try one using the Canvas API.
I am trying to pass the value from server script(.gs) to the HTML file and get back the value in the server side script without opening HTML output as in sidebar or model/modelLess dialog box. You can say silently call HTML, complete the process and return the value to server script method.
I tried but getFromFileArg() is not running when i am running the callToHtml().
Is this possible with below script? what you will suggest?
Server side (.gs)
function callToHtml() {
var ui = SlidesApp.getUi();
var htmlTemp = HtmlService.createTemplateFromFile('crop_img');
htmlTemp["data"] = pageElements.asImage().getBlob();
var htmlOutput = htmlTemp.evaluate();
}
function getFromFileArg(data) {
Logger.log(data);
}
crop_img.html template :
<script>
var data = <?= data ?>;
//call the server script method
google.script.run
.withSuccessHandler(
function(result, element) {
element.disabled = false;
})
.withFailureHandler(
function(msg, element) {
console.log(msg);
element.disabled = false;
})
.withUserObject(this)
.getFromFileArg(data);
</script>
You cannot "silently" call the HTML this way, no.
The HTML needs to go to the user and the user is not inside of your web app, but Google's web app (Slides), so you have to play by their rules.
You need to use one of the available UI methods such as showSidebar. You could have the displayed HTML be a spinner or message like "processing..." while the JavaScript runs.
function callToHtml() {
var ui = SlidesApp.getUi();
var htmlTemp = HtmlService.createTemplateFromFile('crop_img');
htmlTemp["data"] = pageElements.asImage().getBlob();
ui.showSidebar(htmlTemp.evaluate());
}

Multiple JavaScript functions not running

I'm trying to make an app that allows you a text editor. You can type anything in to a text block. You can do two things with said text block:
a) run the text as an html page in a new about:blank tab or
b) save the text as a .html
For some reason, though, when I tried to implement the save ability, neither function would load. If I go into the JS console, it shows me this error upon clicking on the save button:
"Uncaught ReferenceError: saveAsFile is not defined (temp.html,1)"
When I seperated them into two <script> blocks, I could get the save function to work. However, when I did that, the Run in New Tab function no longer worked. It was utterly confusing.
I have used multiple functions before, and I don't know why it's suddenly not working. Can someone help? This is my code:
<script>
function run() {
var codeTab = window.open("" _blank);
var codeRun = document.getElementById("code").value;
codeTab.document.write(codeRun);
}
</script>
<script>
/*i stole-i mean used-this code from someone else*/
function saveAsFile() {
var textToSave = document.getElementById("code").value;
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(textToSave);
hiddenElement.target = '_blank';
hiddenElement.download = 'save.html';
hiddenElement.click();
}
</script>
On line 3, you have window.open("" _blank);. It should be window.open("_blank");.
I also cannot see anywhere saveAs() is being called (or defined). Is it possibly being called in codeRun? See below code which worked for me.
<textarea id="code">
</textarea>
<button onclick="run();">Run</button>
<button onclick="saveAsFile();">Save</button>
<script>
function run() {
var codeTab = window.open("_blank");
var codeRun = document.getElementById("code").value;
codeTab.document.write(codeRun);
}
/*i stole-i mean used-this code from someone else*/
function saveAsFile() {
var textToSave = document.getElementById("code").value;
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(textToSave);
hiddenElement.target = '_blank';
hiddenElement.download = 'save.html';
hiddenElement.click();
}
</script>

Getting the list of voices in speechSynthesis (Web Speech API)

Following HTML shows empty array in console on first click:
<!DOCTYPE html>
<html>
<head>
<script>
function test(){
console.log(window.speechSynthesis.getVoices())
}
</script>
</head>
<body>
Test
</body>
</html>
In second click you will get the expected list.
If you add onload event to call this function (<body onload="test()">), then you can get correct result on first click. Note that the first call on onload still doesn't work properly. It returns empty on page load but works afterward.
Questions:
Since it might be a bug in beta version, I gave up on "Why" questions.
Now, the question is if you want to access window.speechSynthesis on page load:
What is the best hack for this issue?
How can you make sure it will load speechSynthesis, on page load?
Background and tests:
I was testing the new features in Web Speech API, then I got to this problem in my code:
<script type="text/javascript">
$(document).ready(function(){
// Browser support messages. (You might need Chrome 33.0 Beta)
if (!('speechSynthesis' in window)) {
alert("You don't have speechSynthesis");
}
var voices = window.speechSynthesis.getVoices();
console.log(voices) // []
$("#test").on('click', function(){
var voices = window.speechSynthesis.getVoices();
console.log(voices); // [SpeechSynthesisVoice, ...]
});
});
</script>
<a id="test" href="#">click here if 'ready()' didn't work</a>
My question was: why does window.speechSynthesis.getVoices() return empty array, after page is loaded and onready function is triggered? As you can see if you click on the link, same function returns an array of available voices of Chrome by onclick triger?
It seems Chrome loads window.speechSynthesis after the page load!
The problem is not in ready event. If I remove the line var voice=... from ready function, for first click it shows empty list in console. But the second click works fine.
It seems window.speechSynthesis needs more time to load after first call. You need to call it twice! But also, you need to wait and let it load before second call on window.speechSynthesis. For example, following code shows two empty arrays in console if you run it for first time:
// First speechSynthesis call
var voices = window.speechSynthesis.getVoices();
console.log(voices);
// Second speechSynthesis call
voices = window.speechSynthesis.getVoices();
console.log(voices);
According to Web Speech API Errata (E11 2013-10-17), the voice list is loaded async to the page. An onvoiceschanged event is fired when they are loaded.
voiceschanged: Fired when the contents of the SpeechSynthesisVoiceList, that the getVoices method will return, have changed. Examples include: server-side synthesis where the list is determined asynchronously, or when client-side voices are installed/uninstalled.
So, the trick is to set your voice from the callback for that event listener:
// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
...
};
You can use a setInterval to wait until the voices are loaded before using them however you need and then clearing the setInterval:
var timer = setInterval(function() {
var voices = speechSynthesis.getVoices();
console.log(voices);
if (voices.length !== 0) {
var msg = new SpeechSynthesisUtterance(/*some string here*/);
msg.voice = voices[/*some number here to choose from array*/];
speechSynthesis.speak(msg);
clearInterval(timer);
}
}, 200);
$("#test").on('click', timer);
After studying the behavior on Google Chrome and Firefox, this is what can get all voices:
Since it involves something asynchronous, it might be best done with a promise:
const allVoicesObtained = new Promise(function(resolve, reject) {
let voices = window.speechSynthesis.getVoices();
if (voices.length !== 0) {
resolve(voices);
} else {
window.speechSynthesis.addEventListener("voiceschanged", function() {
voices = window.speechSynthesis.getVoices();
resolve(voices);
});
}
});
allVoicesObtained.then(voices => console.log("All voices:", voices));
Note:
When the event voiceschanged fires, we need to call .getVoices() again. The original array won't be populated with content.
On Google Chrome, we don't have to call getVoices() initially. We only need to listen on the event, and it will then happen. On Firefox, listening is not enough, you have to call getVoices() and then listen on the event voiceschanged, and set the array using getVoices() once you get notified.
Using a promise makes the code more clean. Everything related to getting voices are in this promise code. If you don't use a promise but instead put this code in your speech routine, it is quite messy.
You can write a voiceObtained promise to resolve to a voice you want, and then your function to say something can just do: voiceObtained.then(voice => { }) and inside that handler, call the window.speechSynthesis.speak() to speak something. Or you can even write a promise speechReady("hello world").then(speech => { window.speechSynthesis.speak(speech) }) to say something.
heres the answer
function synthVoice(text) {
const awaitVoices = new Promise(resolve=>
window.speechSynthesis.onvoiceschanged = resolve)
.then(()=> {
const synth = window.speechSynthesis;
var voices = synth.getVoices();
console.log(voices)
const utterance = new SpeechSynthesisUtterance();
utterance.voice = voices[3];
utterance.text = text;
synth.speak(utterance);
});
}
At first i used onvoiceschanged , but it kept firing even after the voices was loaded, so my goal was to avoid onvoiceschanged at all cost.
This is what i came up with. It seems to work so far, will update if it breaks.
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
setInterval solution by Salman Oskooi was perfect
Please see https://jsfiddle.net/exrx8e1y/
function myFunction() {
dtlarea=document.getElementById("details");
//dtlarea.style.display="none";
dtltxt="";
var mytimer = setInterval(function() {
var voices = speechSynthesis.getVoices();
//console.log(voices);
if (voices.length !== 0) {
var msg = new SpeechSynthesisUtterance();
msg.rate = document.getElementById("rate").value; // 0.1 to 10
msg.pitch = document.getElementById("pitch").value; //0 to 2
msg.volume = document.getElementById("volume").value; // 0 to 1
msg.text = document.getElementById("sampletext").value;
msg.lang = document.getElementById("lang").value; //'hi-IN';
for(var i=0;i<voices.length;i++){
dtltxt+=voices[i].lang+' '+voices[i].name+'\n';
if(voices[i].lang==msg.lang) {
msg.voice = voices[i]; // Note: some voices don't support altering params
msg.voiceURI = voices[i].voiceURI;
// break;
}
}
msg.onend = function(e) {
console.log('Finished in ' + event.elapsedTime + ' seconds.');
dtlarea.value=dtltxt;
};
speechSynthesis.speak(msg);
clearInterval(mytimer);
}
}, 1000);
}
This works fine on Chrome for MAC, Linux(Ubuntu), Windows and Android
Android has non-standard en_GB wile others have en-GB as language code
Also you will see that same language(lang) has multiple names
On Mac Chrome you get en-GB Daniel besides en-GB Google UK English Female and n-GB Google UK English Male
en-GB Daniel (Mac and iOS)
en-GB Google UK English Female
en-GB Google UK English Male
en_GB English United Kingdom
hi-IN Google हिन्दी
hi-IN Lekha (Mac and iOS)
hi_IN Hindi India
Another way to ensure voices are loaded before you need them is to bind their loading state to a promise, and then dispatch your speech commands from a then:
const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);
function listVoices() {
awaitVoices.then(()=> {
let voices = speechSynthesis.getVoices();
console.log(voices);
});
}
When you call listVoices, it will either wait for the voices to load first, or dispatch your operation on the next tick.
I used this code to load voices successfully:
<select id="voices"></select>
...
function loadVoices() {
populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
}
function populateVoiceList() {
var allVoices = speechSynthesis.getVoices();
allVoices.forEach(function(voice, index) {
var option = $('<option>').val(index).html(voice.name).prop("selected", voice.default);
$('#voices').append(option);
});
if (allVoices.length > 0 && speechSynthesis.onvoiceschanged !== undefined) {
// unregister event listener (it is fired multiple times)
speechSynthesis.onvoiceschanged = null;
}
}
I found the 'onvoiceschanged' code from this article: https://hacks.mozilla.org/2016/01/firefox-and-the-web-speech-api/
Note: requires JQuery.
Works in Firefox/Safari and Chrome (and in Google Apps Script too - but only in the HTML).
async function speak(txt) {
await initVoices();
const u = new SpeechSynthesisUtterance(txt);
u.voice = speechSynthesis.getVoices()[3];
speechSynthesis.speak(u);
}
function initVoices() {
return new Promise(function (res, rej){
speechSynthesis.getVoices();
if (window.speechSynthesis.onvoiceschanged) {
res();
} else {
window.speechSynthesis.onvoiceschanged = () => res();
}
});
}
While the accepted answer works great but if you're using SPA and not loading full-page, on navigating between links, the voices will not be available.
This will run on a full-page load
window.speechSynthesis.onvoiceschanged
For SPA, it wouldn't run.
You can check if it's undefined, run it, or else, get it from the window object.
An example that works:
let voices = [];
if(window.speechSynthesis.onvoiceschanged == undefined){
window.speechSynthesis.onvoiceschanged = () => {
voices = window.speechSynthesis.getVoices();
}
}else{
voices = window.speechSynthesis.getVoices();
}
// console.log("voices", voices);
I had to do my own research for this to make sure I understood it properly, so just sharing (feel free to edit).
My goal is to:
Get a list of voices available on my device
Populate a select element with those voices (after a particular page loads)
Use easy to understand code
The basic functionality is demonstrated in MDN's official live demo of:
https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis
but I wanted to understand it better.
To break the topic down...
SpeechSynthesis
The SpeechSynthesis interface of the Web Speech API is the controller
interface for the speech service; this can be used to retrieve
information about the synthesis voices available on the device, start
and pause speech, and other commands besides.
Source
onvoiceschanged
The onvoiceschanged property of the SpeechSynthesis interface
represents an event handler that will run when the list of
SpeechSynthesisVoice objects that would be returned by the
SpeechSynthesis.getVoices() method has changed (when the voiceschanged
event fires.)
Source
Example A
If my application merely has:
var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);
Chrome developer tools console will show:
Example B
If I change the code to:
var synth = window.speechSynthesis;
console.log("BEFORE");
console.log(synth);
console.log(synth.onvoiceschanged);
console.log("AFTER");
var voices = synth.getVoices();
console.log(voices);
console.log(synth);
console.log(synth.onvoiceschanged);
The before and after states are the same, and voices is an empty array.
Solution
Although i'm not confident implementing Promises, the following worked for me:
Defining the function
var synth = window.speechSynthesis;
// declare so that values are accessible globally
var voices = [];
function set_up_speech() {
return new Promise(function(resolve, reject) {
// get the voices
var voices = synth.getVoices();
// get reference to select element
var $select_topic_speaking_voice = $("#select_topic_speaking_voice");
// for each voice, generate select option html and append to select
for (var i = 0; i < voices.length; i++) {
var option = $("<option></option>");
var suffix = "";
// if it is the default voice, add suffix text
if (voices[i].default) {
suffix = " -- DEFAULT";
}
// create the option text
var option_text = voices[i].name + " (" + voices[i].lang + suffix + ")";
// add the option text
option.text(option_text);
// add option attributes
option.attr("data-lang", voices[i].lang);
option.attr("data-name", voices[i].name);
// append option to select element
$select_topic_speaking_voice.append(option);
}
// resolve the voices value
resolve(voices)
});
}
Calling the function
// in your handler, populate the select element
if (page_title === "something") {
set_up_speech()
}
Android Chrome - turn off data saver. It was helpfull for me.(Chrome 71.0.3578.99)
// wait until the voices load
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
};
let voices = speechSynthesis.getVoices();
let gotVoices = false;
if (voices.length) {
resolve(voices, message);
} else {
speechSynthesis.onvoiceschanged = () => {
if (!gotVoices) {
voices = speechSynthesis.getVoices();
gotVoices = true;
if (voices.length) resolve(voices, message);
}
};
}
function resolve(voices, message) {
var synth = window.speechSynthesis;
let utter = new SpeechSynthesisUtterance();
utter.lang = 'en-US';
utter.voice = voices[65];
utter.text = message;
utter.volume = 100.0;
synth.speak(utter);
}
Works for Edge, Chrome and Safari - doesn't repeat the sentences.

JSONP callback issue

I'm trying to get JSONP working with a server running on an Arduino.
This is my JS code:
window.onload = init;
function init()
{
//alert("Test");
SendRequest();
}
function SendRequest()
{
alert("Sending request");
var url = "http://192.168.1.177";
var request = new XMLHttpRequest();
request.open("GET", url);
request.error = function(e) {
alert("ERROR");
};
request.send(null);
}
function ArduinoJSONP()
{
alert("Callback received!!!");
}
The callback function is never reached.
But if I just direct my browser directly to the Arduino IP I see the following displayed in the browser:
ArduinoJSONP({"data": 12345})
So it seems the server is sending the response with correct JSONP format but somehow the function is not invoked. Is there anything else I need to for JS to call the function? I even tried moving the function to the HTML body but it didn't help either.
Thank you.
You are not handling server response at all. If you doing it without any libraries you need to eval result that was returned by the server.
And actually JSONP implementation is not XHR, you have to inject it as a script tag into html with correct src attribute.
Just use a library that already have all this logic abstracted for you.
Simply inject script tag into HTML tree:
function SendRequest()
{
var element = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
element.type = 'text/javascript';
element.async = true;
element.src = 'http://192.168.1.177';
s.parentNode.insertBefore(element, s);
}
You can mark it with unique id. Hookup to onload event and once executed remove that script.

How to get FFT from audio tag in HTML5

I want to get the FFT data from an <audio> tag, but it doesn't work without any syntax error. Looking at the Web Audio API document, I write a sample code, here is my code:
<audio id="aud" controls="controls" src="test.mp3"></audio>
<script type="text/javascript">
var audioElement = document.getElementById("aud");
var audioContext = new webkitAudioContext();
var streamingAudioSource = audioContext.createMediaElementSource(audioElement);
var jsProcessor = audioContext.createJavaScriptNode(4096,1,1);
jsProcessor.onaudioprocess = process;
var analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
//streaming:AudioSource->jsProcessor->analyser->destination
streamingAudioSource.connect(jsProcessor);
jsProcessor.connect(analyser);
analyser.connect(audioContext.destination);
//autoplay
audioElement.play();
function process(event){
var freqByteData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqByteData);
document.getElementById("info").innerHTML=freqByteData[1];//show data in div
}
</script>
My Chrome version is 20.0.1096.1 dev-m and I think it works well. Through process(), I'm trying to write down freqByteData, but it shows 0, and all of them are always 0.
It must have something wrong of my code, and I want to know how to get frequency data from an audio tag.
It seems that createMediaSourceElement breaks if it's called before window.onload. There is a bug report about this issue: http://code.google.com/p/chromium/issues/detail?id=112368
There are currently two workarounds:
Wait the window load event before executing the whole javascript
window.addEventListener('load', function(){
// your code
}, false);
or
Create the MediaElementSource in a setTimeout with 0 delay
setTimeout(function (){
var streamingAudioSource = audioContext.createMediaElementSource(audioElement);
var jsProcessor = audioContext.createJavaScriptNode(4096,1,1);
// The rest of the code
}, 0);