AudioContext issue with safari - javascript

I use AudioContext to play some audios in my site.
It works fine on Chrome and Firefox, but not on Safari. On Safari stop function does not work and I get the following:
[Error] InvalidStateError: DOM Exception 11: An attempt was made to use an object that is not, or is no longer, usable.
noteOff (preload.js, line 85)
Does anyone know , how to fix this, and why this error occurrs?
function _initWebAudio(AudioContext, format, audios, callback) {
var context = new AudioContext();
preloader(audios, _preload, callback);
function _preload(asset, doneCallback) {
var request = new XMLHttpRequest();
request.open('GET', 'audio/' + asset.id + '.' + format, true);
request.responseType = 'arraybuffer';
request.onload = function () {
context.decodeAudioData(request.response, function (buffer) {
var source;
// default volume
//// support both webkitAudioContext or standard AudioContext
asset.gain = context.createGain ? context.createGain() : context.createGainNode();
asset.play = function () {
source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(asset.gain); // connect the source to the context's destination (the speakers)
asset.gain.connect(context.destination);
// play the source now
// support both webkitAudioContext or standard AudioContext
source.noteOn ? source.noteOn(0) : source.start(0);
};
asset.stop = function () {
console.log(source);
source = context.createBufferSource(); // creates a sound source
source.noteOff ? source.noteOff(0) : source.stop(0);
}
asset.toggleVolume = function (muteSound) {
if (muteSound) {
asset.gain.gain.value = 0;
} else {
asset.gain.gain.value = 1;
}
}
doneCallback();
}, function (err) {
asset.play = function () {
};
doneCallback(err, asset.id);
});
};
request.onerror = function (err) {
console.log(err);
asset.play = function () {
};
doneCallback(err, asset.id);
};
// kick off load
request.send();
}
}

I'm not sure why this happened, but I solved this by using disconnect() method.
asset.stop = function () {
asset.gain.disconnect();
//source.noteOff ? source.noteOff(0) : source.stop(0);
}

Related

javascript when page load ask user audio permission

I have a site need auto play sound notification.
I need to trigger browser sound permission, anyone know how to use javascript to trigger this?
new Audio('notification.mp3').play();
You can use the AudioContext() interface to play it.
window.onload = function(){
var url = '<AUDIO_URL>.mp3';
window.AudioContext = window.AudioContext||window.webkitAudioContext; //fix up prefixing
var context = new AudioContext(); //context
var source = context.createBufferSource(); //source node
source.connect(context.destination); //connect source to speakers so we can hear it
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer'; //the response is an array of bits
request.onload = function() {
context.decodeAudioData(request.response, function(response) {
source.buffer = response;
source.start(0); //play audio immediately
source.loop = true;
}, function () { console.error('The request failed.'); } );
}
request.send();
}
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia
if (navigator.getUserMedia) {
navigator.getUserMedia(
{ audio: true },
function onSuccess(stream) {
// go play
},
function onError(error) {
}
)
} else {
}

JavaScript - file to bytes array

I'm trying to get a script to work, which is called sfo.js.
The repo mentions only this usage:
keys = parse_sfo(Some_ArrayBuffer);
console.log(keys['TITLE']);
Looking into the sfo.js, parse_sfo has the sfoBytes argument.
From this I've concluded the sfoBytes argument needs to be an arraybuffer of file bytes.
I've tried to make a script that parses the SFO file into a array of bytes:
<script src="sfo.js"></script>
<script>
function stringToArrayBuffer(str) {
var buf = [];
for (var i=0, strLen=str.length; i<strLen; i++) {
buf[i] = str.charCodeAt(i);
}
console.log(buf);
return buf;
}
function testing(url) {
var request = new XMLHttpRequest();
request.open('GET', url, false);
request.send(null);
if (request.status === 200) {
console.log(request.response);
var response = request.response;
var array = stringToArrayBuffer(response);
return array;
} else {
alert('Error!');
}
}
var data = testing('param.sfo');
var sfo = parse_sfo(data);
</script>
That throws an error in the console:
Uncaught RangeError: byte length of Uint32Array should be a multiple of 4 at new Uint32Array (<anonymous>)
at readUint32At (sfo.js:20)
at parse_sfo (sfo.js:113)
at (index):29
I'm pretty sure I'm doing something wrong. Does anybody understand how I can make the script work properly?
I have a sample file for param.sfo: https://filebin.net/gghosrp6u93jn7y8 (if linking to a download is not allowed please let me know)
Ok, finally the working example.
I've found a small param.sfo file in the internet.
Notes:
there are 2 versions of file reader: local and remote
in the snippet below you can test both (I've added my param.sfo as an external link to test 'remote'). To test 'local' you just need to select any sfo file from you PC.
as a result I show all the keys, not only 'TITLE' (as it is in your question). You can then select desired key
function getSfoLocal(callback) {
// for now I use local file for testing
document.querySelector('input').addEventListener('change', function() {
var reader = new FileReader();
reader.onload = function() {
var arrayBuffer = this.result;
var array = new Uint8Array(arrayBuffer);
// var binaryString = String.fromCharCode.apply(null, array);
if (typeof callback === 'function') callback(array);
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
}
function getSfoRemote(url, callback) {
var concatArrayBuffers = function(buffer1, buffer2) {
if (!buffer1) {
return buffer2;
} else if (!buffer2) {
return buffer1;
}
var tmp = new Uint8Array(buffer1.length + buffer2.length);
tmp.set(buffer1, 0);
tmp.set(buffer2, buffer1.byteLength);
return tmp.buffer;
};
fetch(url).then(res => {
const reader = res.body.getReader();
let charsReceived = 0;
let result = new Uint8Array;
reader.read().then(function processText({
done,
value
}) {
// Result objects contain two properties:
// done - true if the stream has already given you all its data.
// value - some data. Always undefined when done is true.
if (done) {
if (typeof callback === 'function') callback(result);
return result;
}
// value for fetch streams is a Uint8Array
charsReceived += value.length;
const chunk = value;
result = concatArrayBuffers(result, chunk);
// Read some more, and call this function again
return reader.read().then(processText);
});
});
}
function getSfo(type, url, callback) {
if (type === 'local') getSfoLocal(callback);
if (type === 'remote') getSfoRemote(url, callback);
}
getSfo('local', null, (data) => {
keys = parse_sfo(data);
console.log('LOCAL =', keys);
});
function goremote() {
getSfo('remote', 'https://srv-file9.gofile.io/download/Y0gVfw/PARAM.SFO', (data) => {
keys = parse_sfo(data);
console.log('REMOTE =', keys);
});
}
div { padding: 4px; }
<!--script src="https://rawcdn.githack.com/KuromeSan/sfo.js/c7aa8209785cc5a39c4231e683f6a2d1b1e91153/sfo.js" for="release"></script-->
<script src="https://raw.githack.com/KuromeSan/sfo.js/master/sfo.js" for="develop"></script>
<div>Local version: <input type="file" /></div>
<div>Remote version: <button onclick="goremote()">Go remote</button></div>
P.S. It seems that gofile.io service that I've used for remote example is not visible sometimes. If you have permanent link to param.sfo just let me know. But now I have to visit my file, and only after that it becomes visible.
You are using some 20 years old JavaScript.
Please use fetch add TextEncoder for your own sanity.
fetch(url).then(res => res.json().then(data => {
const encoder = new TextEncoder()
const bytes = encoder.encode(data)
parse_sfo(data)
})
sfoBytes should be the actual array buffer of the sfo file. It's simpler than what you are trying, you shouldn't need to convert the request response with stringToArrayBuffer since you can get an array buffer from XMLHttpRequest. Also, the SFO file isn't a text file, it's binary, so conversion from string wouldn't work anyway.
Changing your request to get a arraybuffer response type should do it like this:
function testing(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = "arraybuffer";
request.send(null);
if (request.status === 200) {
console.log(request.response);
var response = request.response;
var sfo = parse_sfo(response);
} else {
alert('Error!');
}
}

Audio loading with XMLHttpRequest in JavaScript

I am learing Web Audio API. Im having a problem in loading my sound file thourgh XMLHttpRequest in JavaScript. Kindly please help.Here is my code
var source;
function start() {
console.log("WELCOME!!");
try{
var actx = new AudioContext();
}catch(e){
console.log('WebAudio api is not supported!!');
}
source = actx.createBufferSource()
var req = new XMLHttpRequest();
req.open('GET','src3.ogg',true);
req.responseType='ArrayBuffer';
req.onload = function(){
var audioData = req.response;
actx.decodeAudioData(audioData,function(buffer){
source.buffer = buffer;
source.connect(actx.destination);
source.loop();
},
function(e){
console.log('Error in decoding audio'+e.err);
}
);
}
req.send();
}
function play() {
source.start(0);
}
function stop(){
source.stop(0);
}
Browser is showing this error this:
XML Parsing Error: not well-formed
Location: file:///home/uzumaki/Web_Audio_Projects/src3.ogg
Line Number 1, Column 5:
And start() method is called on the onload event of body tag
Substitute
req.responseType = "arraybuffer";
for
req.responseType = "ArrayBuffer";
.responseType is not converted to lowercase by XMLHttpResponse instance

Web Audio load both .mp3 and .ogg

I'm building a canvas game using javascript and web audio. To get it to work in Firefox I have a copy of all the audio files in .ogg format. The code to load these files is below. To get the desired audio file I use ' playSound(samplebb[3], channel1); ', I have done it like this as in my app it is useful to choose the sample based on number, for example sounds can be chosen using probability and randomness.
I read on a forum "the loader will accept both [mp3 and ogg] for the same sound, just pass both paths in an array rather than a single string."
The 4th line of code is me trying this but it does not work.
Is it possible to load an alternate ogg file for each mp3 like this? (In one bufferlist) Or will I have to detect the browser and build a bufferlist of oggs if the browser is Firefox?
Thanks
function loadSounds() {
bufferLoader = new BufferLoader(audioContext,
[
['sounds/1-KICK.mp3', 'sounds/1-KICK.ogg'], //0 // Not found
'sounds/2-BASS.mp3', //1
'sounds/3-BASS2.mp3', //2
'sounds/4-BASS4.mp3' //3
// ... ... ...
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
for (var i = 0, l = bufferList.length; i < l; i += 1) {
var source = audioContext.createBufferSource();
source.buffer = bufferList[i];
source.connect(audioContext.destination);
var note = {
note: source,
ready: true
};
samplebb.push(note);
}
setTimeout(play, 1000);
}
Are you using BufferLoader from html5rocks? If so, the JS file clearly shows that it only expects strings (not arrays) as url arguments. However you can modify the class so that it works like you want. Use the following BufferLoader.loadBuffer() function instead:
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest(),
mult = typeof url != 'string',
srcInd = 0;
request.open("GET", mult ? url[srcInd++] : url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
if(!mult || srcInd == url.length) {
console.error('error decoding file data:', url);
return;
} else {
console.info('error decoding file data, trying next source');
request.open("GET", url[srcInd++], true);
return request.send();
}
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
if(!mult || srcInd == url.length) {
console.error('decodeAudioData error:', url);
return;
} else {
console.info('decodeAudioData error, trying next source');
request.open("GET", url[srcInd++], true);
return request.send();
}
}
);
}
request.onerror = function() {
if(!mult || srcInd == url.length) {
console.error('BufferLoader XHR error:', url);
return;
} else {
console.info('BufferLoader XHR error, trying next source');
request.open("GET", url[srcInd++], true);
return request.send();
}
}
request.send();
}

How to create a Web Worker from a string

How can I use create a Web worker from a string (which is supplied via a POST request)?
One way I can think of, but I'm not sure how to implement it, is by creating a data-URI from the server response, and passing that to the Worker constructor, but I've heard that some browsers don't allow this, because of the same origin policy.
MDN states the uncertainty about the origin policy around data URI's:
Note: The URI passed as parameter of the Worker constructor must obey the same-origin policy. There is currently disagreement among browsers vendors on whether data URIs are of the same-origin or not; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) and later do allow data URIs as a valid script for workers. Other browsers may disagree.
Here's also a post discussing it on the whatwg.
Summary
blob: for Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+
data:application/javascript for Opera 10.60 - 12
eval otherwise (IE 10+)
URL.createObjectURL(<Blob blob>) can be used to create a Web worker from a string. The blob can be created using the BlobBuilder API deprecated or the Blob constructor.
Demo: http://jsfiddle.net/uqcFM/49/
// URL.createObjectURL
window.URL = window.URL || window.webkitURL;
// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";
var blob;
try {
blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(response);
blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));
// Test, used in all examples:
worker.onmessage = function(e) {
alert('Response: ' + e.data);
};
worker.postMessage('Test');
Compatibility
Web workers are supported in the following browsers source:
Chrome 3
Firefox 3.5
IE 10
Opera 10.60
Safari 4
This method's support is based on the support of the Blob API and the URL.createObjectUrl method. Blob compatibility:
Chrome 8+ (WebKitBlobBuilder), 20+ (Blob constructor)
Firefox 6+ (MozBlobBuilder), 13+ (Blob constructor)
Safari 6+ (Blob constructor)
IE10 supports MSBlobBuilder and URL.createObjectURL. However, trying to create a Web Worker from a blob:-URL throws a SecurityError.
Opera 12 does not support URL API. Some users may have a fake version of the URL object, thanks to this hack in browser.js.
Fallback 1: data-URI
Opera supports data-URIs as an argument to the Worker constructor. Note: Do not forget to escape special characters (Such as # and %).
// response as defined in the first example
var worker = new Worker('data:application/javascript,' +
encodeURIComponent(response) );
// ... Test as defined in the first example
Demo: http://jsfiddle.net/uqcFM/37/
Fallback 2: Eval
eval can be used as a fallback for Safari (<6) and IE 10.
// Worker-helper.js
self.onmessage = function(e) {
self.onmessage = null; // Clean-up
eval(e.data);
};
// Usage:
var worker = new Worker('Worker-helper.js');
// `response` as defined in the first example
worker.postMessage(response);
// .. Test as defined in the first example
I agree with the current accepted answer but often editing and managing the worker code will be hectic as its in the form of a string.
So optionally we can use the below approach where we can keep the worker as a function, and then covert to string->blob:
// function to be your worker
function workerFunction() {
var self = this;
self.onmessage = function(e) {
console.log('Received input: ', e.data); // message received from main thread
self.postMessage("Response back to main thread");
}
}
///////////////////////////////
var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds "use strict"; to any function which might block worker execution so knock it off
var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
type: 'application/javascript; charset=utf-8'
});
var worker = new Worker(blobURL); // spawn new worker
worker.onmessage = function(e) {
console.log('Worker said: ', e.data); // message received from worker
};
worker.postMessage("some input to worker"); // Send data to our worker.
This is tested in IE11+ and FF and Chrome
The accepted answer is a bit complex, due to supporting backwards compatibility, so I wanted to post the same thing but simplified. Try this in your (modern) browser console:
const code = "console.log('Hello from web worker!')"
const blob = new Blob([code], {type: 'application/javascript'})
const worker = new Worker(URL.createObjectURL(blob))
// See the output in your console.
I've made an approach with most of your ideas and adding some of mine. The only thing my code needs on worker is to use 'this' to refer 'self' scope. I'm pretty sure that this is very improvable:
// Sample code
var code = function() {
this.onmessage = function(e) {
this.postMessage('Worker: '+e.data);
this.postMessage('Worker2: '+e.data);
};
};
// New thread worker code
FakeWorkerCode = function(code, worker) {
code.call(this);
this.worker = worker;
}
FakeWorkerCode.prototype.postMessage = function(e) {
this.worker.onmessage({data: e});
}
// Main thread worker side
FakeWorker = function(code) {
this.code = new FakeWorkerCode(code, this);
}
FakeWorker.prototype.postMessage = function(e) {
this.code.onmessage({data: e});
}
// Utilities for generating workers
Utils = {
stringifyFunction: function(func) {
// Stringify the code
return '(' + func + ').call(self);';
},
generateWorker: function(code) {
// URL.createObjectURL
windowURL = window.URL || window.webkitURL;
var blob, worker;
var stringified = Utils.stringifyFunction(code);
try {
blob = new Blob([stringified], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(stringified);
blob = blob.getBlob();
}
if ("Worker" in window) {
worker = new Worker(windowURL.createObjectURL(blob));
} else {
worker = new FakeWorker(code);
}
return worker;
}
};
// Generate worker
var worker = Utils.generateWorker(code);
// Test, used in all examples:
worker.onmessage = function(e) {
alert('Response: ' + e.data);
};
function runWorker() {
worker.postMessage('working fine');
}
Demo: http://jsfiddle.net/8N6aR/
Nice answer - I've been working on a similar problem today when trying to create Web Workers with fallback capabilities when they're not available (i.e. run worker script in main thread). As this thread is pertains to the topic, I thought I'd provide my solution here:
<script type="javascript/worker">
//WORKER FUNCTIONS
self.onmessage = function(event) {
postMessage('Hello, ' + event.data.name + '!');
}
</script>
<script type="text/javascript">
function inlineWorker(parts, params, callback) {
var URL = (window.URL || window.webkitURL);
if (!URL && window.Worker) {
var worker = new window.Worker(URL.createObjectURL(new Blob([parts], { "type" : "text/javascript" })));
worker.onmessage = function(event) {
callback(event.data);
};
worker.postMessage(params);
} else {
var postMessage = function(result) {
callback(result);
};
var self = {}; //'self' in scope of inlineWorker.
eval(parts); //Converts self.onmessage function string to function on self via nearest scope (previous line) - please email chrisgwgreen.site#gmail.com if this could be tidier.
self.onmessage({
data: params
});
}
}
inlineWorker(
document.querySelector('[type="javascript/worker"]').textContent,
{
name: 'Chaps!!'
},
function(result) {
document.body.innerHTML = result;
}
);
</script>
</body>
Depending on your use case you can use something like
task.js Simplified interface for getting CPU intensive code to run on all cores (node.js, and web)
A example would be
// turn blocking pure function into a worker task
const functionFromPostRequest = task.wrap('function (exampleArgument) {}');
// run task on a autoscaling worker pool
functionFromPostRequest('exampleArgumentValue').then(result => {
// do something with result
});
Expanding on #Chanu_Sukarno's code, you can simply pass in a worker function (or string) to this function and it will execute it inside a web worker:
async function doWorkerTask(workerFunction, input, buffers) {
// Create worker
let fnString = '(' + workerFunction.toString().replace('"use strict";', '') + ')();';
let workerBlob = new Blob([fnString]);
let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
let worker = new Worker(workerBlobURL);
// Run worker
return await new Promise(function(resolve, reject) {
worker.onmessage = function(e) { resolve(e.data); };
worker.postMessage(input, buffers);
});
}
Here's an example of how to use it:
function myTask() {
self.onmessage = function(e) {
// do stuff with `e.data`, then:
self.postMessage("my response");
self.close();
}
}
let output = await doWorkerTask(myTask, input, inputBuffers);
// now you can do something with `output` (which will be equal to "my response")
In nodejs, doWorkerTask looks like this:
async function doWorkerTask(workerFunction, input, buffers) {
let Worker = require('webworker-threads').Worker;
let worker = new Worker(workerFunction);
// Run worker
return await new Promise(function(resolve, reject) {
worker.onmessage = function(e) { resolve(e.data); };
worker.postMessage(input, buffers);
});
}
Here's an alternative approach that lets you define a worker using a function with optional setup arguments.
note - you can only use setup arguments that can be stringified - use worker.postMessage to send anything else
// define our worker function
function workerFunc(arg1,arg2) {
console.log("worker is running:"+arg1+" "+arg2);
postMessage(arg1+" "+arg2);
setInterval(sendTime,1000);
function sendTime(){
postMessage(new Date().toUTCString() );
}
}
// (optionally) define an element to inspect the script that worker will use
workerFunc.debug = document.getElementById("worker_script");
// start the worker and get replies from it
startWorker(workerFunc,"hello","world").addEventListener("message",function(e){
document.getElementById("worker_reply").innerHTML = e.data;
});
function startWorker (fn) {
const src = fn.toString();
const args = src.substring(src.indexOf("(")+1,src.indexOf(")"));
const code = ( args ? "let ["+args+"]="+JSON.stringify([].slice.call(arguments,1))+";\n" : "" )+ src.substring(src.indexOf("{")+1,src.length-1);
const blob = new Blob([code], {type: 'application/javascript'})
if (fn.debug) fn.debug.innerHTML=code;
return new Worker(URL.createObjectURL(blob))
}
<textarea id="worker_reply"></textarea>
<h1>Worker Script</h1>
<pre id="worker_script">
</pre>
Use my tiny plugin https://github.com/zevero/worker-create
var worker_url = Worker.create("self.postMessage('Example post from Worker');");
var worker = new Worker(worker_url);
But you may also give it a function.
My solution (can be "promise"d easily...)
function makeWorker(workerFunction, cb) {
// Create worker
var tplFun = "onmessage = function(e){console.log(e); var args = Array.prototype.slice.call(e.data); var res=___.apply(this,args);postMessage(res);}"
var fnTxt = workerFunction.toString().replace('"use strict";', '');
var final = tplFun.replace("___", fnTxt);
let workerBlob = new Blob([final]);
let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
let worker = new Worker(workerBlobURL);
return function () {
var args = Array.prototype.slice.call(arguments);
console.log(args)
worker.postMessage(args);
worker.onmessage = function (e) {
console.log(e.data);
cb(e.data);
}
}
}
function myTask(a, b, c) {
return a * b * c;
}
function onresult(e) {
alert(e);
}
var fn = makeWorker(myTask, onresult)
fn(1, 2, 3);
Can be nice with "pure and slow" function !
Based on accepted answer, class to worker, interesting topic
// worker class
class SimpleWorker {
constructor() {
console.log("simple worker init");
}
onMessage(event) {
console.log("main to worker", event.data);
postMessage({
type: "answer",
data: "data from worker"
});
}
}
// class to worker
const workerFromClass = workerClassRef => {
console.log(workerClassRef.name, "to worker");
// factory method, converted to string, used to instanciate worker
let workerFactory = (self, workerClass) => {
let worker = new workerClass();
self["onmessage"] = worker.onMessage.bind(worker);
};
// compute worker code string
// worker class & factory function
let str = workerClassRef.toString() + "\n"
+ "(" + workerFactory.toString() + ")"
+ "(this," + workerClassRef.name + ");"
// worker code to blob
let blob = new Blob(
[str],
{type: "application/javascript"}
);
// return worker instance
return new Worker(
URL.createObjectURL(blob)
);
};
// main
// create worker
let worker = workerFromClass(SimpleWorker);
// handle messages from worker
worker.addEventListener(
"message",
event => console.log("worker to main", event.data)
);
// send message to worker
let message = {
type: "question",
data: "data from main"
};
console.log("main to worker", message);
worker.postMessage(message);
You can get real-data from the objectURL and not just blob by changing the responseType to either "text" or "arraybuffer".
Here is a back-and-forth conversion of text/javascript to blob to objectURL back to blob or text/javascript.
if you are wondering, I'm using it to generate a web-worker with no external files
you may use it to return binary content, for example a YouTube video ;) (from the <video> tag resource attribute)
var blob = new Blob(['self.onmessage=function(e){postMessage(e)}'],{type: 'text/javascript'}); //->console: (object) Blob {size: 42, type: "text/javascript", slice: function}
var obju = URL.createObjectURL(js_blob); //->console: "blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7"
var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7', true);
xhr.responseType = 'text'; /* or "blob" */
xhr.onreadystatechange = function(){
if(xhr.DONE !== xhr.readyState) return;
console.log(xhr.response);
}
xhr.send();
/*
responseType "blob" ->console: (object) Blob {size: 42, type: "text/javascript", slice: function}
responseType "text" ->console: (text) 'self.onmessage=function(e){postMessage(e)}'
*/

Categories