acces value outside of closure to extend wavesurfer.js lib - javascript

I have 2 functions here: load recieves a path to an mp3 and count is the number of tracks being loaded in. When for loop reaches the last track arraybufferList contains an array of 'arraybuffer' of all the songs. This is then passed on to the loadData function which uses an webaudioapi function 'decodeAudioData' to decode all the information and put everything inside an "currentBuffer". However outside of the closure the "currentBuffer" seems to vanish (in my google chrome debugger).
I'm trying to use the wavesurfer.js library and extend it to multiple tracks.
load: function (src,count) {
var my = this;
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
console.log("loading mp3 & putting inside arraybufferList");
arraybufferList.push(xhr.response);
if (arraybufferList.length==count){
console.log(arraybufferList);
for (var i=0; i<arraybufferList.length; i++){
my.backend.loadData(
arraybufferList[i],
playList[i].draw.bind(playList[i])
);
console.log(playList);
//playList.push(my);
}
console.log(playList);
}
};
xhr.open('GET', src, true);
xhr.send();
},
loadData: function (audioData, cb) {
var my = this;
this.pause();
this.ac.decodeAudioData(
audioData,
function (buffer) {
my.currentBuffer = buffer;
my.lastStart = 0;
my.lastPause = 0;
my.startTime = null;
cb(buffer);
},
Error
);
},
At the beginning for each audio file an object is created where the information from loadData should go into
var WaveSurfer = {
init: function (params) {
var my = this;
var backend = WaveSurfer.Audio;
if (!params.predrawn) {
backend = WaveSurfer.WebAudio;
}
this.backend = Object.create(backend);
this.backend.init(params);
this.drawer = Object.create(WaveSurfer.Drawer);
this.drawer.init(params);
this.backend.bindUpdate(function () {
my.onAudioProcess();
});
this.bindClick(params.canvas, function (percents) {
my.playAt(percents);
});
playList.push(my);
},
some essential code: ac: new (window.AudioContext || window.webkitAudioCont

Related

Increment variable in constructor function (JS)

I am fetching the data from 2 different APIs and I wrote a constructor function that will start XMLHttpRequest based on the new instance of the object creation (if it makes any sense...). Once the data is fetched and parsed I want to save it into different named variables. For instance: trial1data, trial2data. At the minute the new instances of the objects overwrite the data variable. Code below:
var api = "http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}&pretty=true";
var api2 = "http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}&tel={phone|format}&address={streetAddress}&city={city}&state={usState|abbr}&zip={zip}&pretty=true";
function FetchData(apiType) {
var r = new XMLHttpRequest();
this.apiType = apiType;
this.request = function() {
r.open("GET", apiType, true);
r.onload = function() {
var data = JSON.parse(r.responseText);
}
r.send(null);
}
}
trial1 = new FetchData (api);
trial1.request();
trial2 = new FetchData (api2);
trial2.request();
Thanks for the XMLHttpRequest tip, but the issue was to save each data into separate variables such as trial1data and trial2data (or anything else that has got a meaning and I can re-use later on), based on how many new objects I will create.
Your var r = new XMLHttpRequest(); is common.
You need to move it inside the function to create separate request everytime the constructor function is called.
function FetchData(apiType) {
this.apiType = apiType;
this.request = function() {
var r = new XMLHttpRequest();
r.open("GET", apiType, true);
r.onload = function() {
var data = JSON.parse(r.responseText);
console.log(data);
}
r.send(null);
}
}
You should put the request object creation inside the constructor:
function FetchData(apiType) {
var r = new XMLHttpRequest();
this.apiType = apiType;
this.done = new Promise( res => {
r.onload = function() {
res(JSON.parse(r.responseText) );
};
});
this.request = function() {
r.open("GET", apiType, true);
r.send(null);
};
}
So you can do:
const req = new FetchData("whatever");
req.request();
req.done.then(console.log);
Create new instance on var r = new XMLHttpRequest(); inside of constructor, or as a better approach, make it an argument for a constructor and inject new XMLHttpRequets object for each.
To answer the second part of your question, you could store response data in object's property and access it directly or getter interface. So instead of
r.onload = function() {
var data = JSON.parse(r.responseText);
}
Do something like:
function FetchData(apiType) {
var self = this;
this.apiType = apiType;
this.request = function() {
var r = new XMLHttpRequest();
return new Promise(function(resolve, reject) {
r.open("GET", apiType, true);
r.onload = function() {
self.data = JSON.parse(r.responseText);
resolve(self.data);
}
r.send(null);
}
}
Then
trial1 = new FetchData (api);
var trial1resp;
trial1.request().then(function(data) {
trial1resp = data;
}
The last assignment is just to show how is response stored. You must handle async processess to achieve your goal.
You could read little bit more about promisses and how to handle xhr async tasks here https://developers.google.com/web/fundamentals/primers/promises

Loading audio files through URLs from remote locations (Opera)?

Maybe someone knows why codepend does not load audio files from URLs? (I do not have pro codepen, so I can't use direct uploading of files to pen).
I have this Audio "loader" implementation in my program:
// Audio loader implementation.
window.onload = init;
let context;
let bufferLoader;
let greenBuffer = null;
let redBuffer = null;
let blueBuffer = null;
let yellowBuffer = null;
let dohBuffer = null;
let woohooBuffer = null;
let excellentBuffer = null;
let superDohBuffer = null;
// Buffer loader class taken from https://www.html5rocks.com/en/tutorials/webaudio/intro/
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
let request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
let loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
}
BufferLoader.prototype.load = function() {
for (let i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
}
function init() {
try {
// Fix up for prefixing
window.AudioContext = window.AudioContext||window.webkitAudioContext;
context = new AudioContext();
}
catch(e) {
alert('Web Audio API is not supported in this browser');
}
bufferLoader = new BufferLoader(
context,
[
'https://cors-anywhere.herokuapp.com/https://s3.amazonaws.com/freecodecamp/simonSound1.mp3',
'https://cors-anywhere.herokuapp.com/https://s3.amazonaws.com/freecodecamp/simonSound2.mp3',
'https://cors-anywhere.herokuapp.com/https://s3.amazonaws.com/freecodecamp/simonSound3.mp3',
'https://cors-anywhere.herokuapp.com/https://s3.amazonaws.com/freecodecamp/simonSound4.mp3',
'https://cors-anywhere.herokuapp.com/http://www.springfieldfiles.com/sounds/homer/doh.mp3',
'https://cors-anywhere.herokuapp.com/http://www.springfieldfiles.com/sounds/homer/woohoo.mp3',
'https://cors-anywhere.herokuapp.com/http://springfieldfiles.com/sounds/burns/excellnt.mp3',
'https://cors-anywhere.herokuapp.com/http://www.springfieldfiles.com/sounds/homer/doheth.mp3',
],
setBuffers
);
bufferLoader.load();
}
function setBuffers(bufferList){
greenBuffer = bufferList[0];
redBuffer = bufferList[1];
blueBuffer = bufferList[2];
yellowBuffer = bufferList[3];
dohBuffer = bufferList[4];
woohooBuffer = bufferList[5];
excellentBuffer = bufferList[6];
superDohBuffer = bufferList[7];
}
If I use this code locally (not on codepen), it works fine. It loads those files and later I can play those audio files how I want. But if I run it on codepen, it throws this (note I also prepended https://cors-anywhere.herokuapp.com/ to URLs to bypass CORS):
console_runner-079c09a….js:1 decodeAudioData error DOMException: Unable to decode audio data
(anonymous) # console_runner-079c09a….js:1
(anonymous) # pen.js:80
index.html:1 Uncaught (in promise) DOMException: Unable to decode audio data
index.html:1 Uncaught (in promise) DOMException: Unable to decode audio data
Full pen can be checked here: https://codepen.io/andriusl/pen/proxKj
Update.
It seems this is related with browsers. AudioContext does not properly work with Opera browser, so this question is more oriented to browser than codepen itself.

Is it possible to modify XMLHttpRequest.prototype.open()?

I am trying to inject callbacks into XMLHttpRequest.prototype.open() so I can know the method and the url, as well as setting a timestamp for the performance report (alone with the modified XMLHttpRequest.prototype.send()).
Here is what I came up so far:
function addXMLRequestCallback(open, callback) {
var nativeOpen, nativeSend, i;
if (XMLHttpRequest.callbacks) {
XMLHttpRequest.callbacks.push(callback);
} else {
XMLHttpRequest.callbacks = [callback];
nativeOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
open.forEach(function(item) {
// not sure what to do here...
});
}
nativeSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
for (i = 0; i < XMLHttpRequest.callbacks.length; i++) {
XMLHttpRequest.callbacks[i](this);
}
nativeSend.apply(this, arguments);
}
}
}
addXMLRequestCallback(function(method,url){
// Maybe instead I should put code here...
}, function(xhr) {
// then do something here?
});
What you had with the send method was the right approach. In the following snippet, I only inplemented a listener that gets called whenever an XMLHttpRequest's open method is called. I'm sure you can implement the same for send.
(function () {
var nativeOpen = XMLHttpRequest.prototype.open;
var callbacks = XMLHttpRequest.callbacks = [];
XMLHttpRequest.prototype.open = function () {
callbacks.forEach(callback => callback.apply(this, arguments));
nativeOpen.apply(this, arguments);
};
})();
function addXHROpenCallback (cb) {
XMLHttpRequest.callbacks.push(cb);
}
addXHROpenCallback(function (method, url) {
console.log(`XHR opened. Method: ${method}, URL: ${url}`);
});
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com');
However, ask yourself if you really need to modify a built-in prototype. Consider creating a wrapper class that does the same, but without modifying a prototype.

How to I refer to the `postMessage()` function from my XHR callbacks?

The file uploads work perfectly, I can't get the progess and load events to callback
I have the following in a JavaScript file as my WebWorker code:
UploadFileWorker.js
function uploadFile(url, m) {
var f = m.file;
var fd = new FormData();
fd.append('file', f, f.name);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
m.set('status', (e.loaded / e.total) * 100 );
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.upload.addEventListener('load', function (e) {
m.set('status',100);
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.open('POST', url, true);
xhr.send(fd);
}
function getUploadURL(m) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
var url = this.responseText;
uploadFile(url,m);
});
xhr.open('GET', '/upload', false);
xhr.send();
}
var q = [];
onmessage = function(e) {
postMessage(e.data);
q.push(e.data);
while (q.length > 0) {
var m = q.pop();
getUploadURL(m);
}
};
The postMessage(e.data); in the onmessage function work perfectly.
The postMessage(m); in the xhr.upload.addEventListeners() callbacks never happen. They worked fine before I tried and move these functions into WebWorker code.
Is this a scope issue? If so, how do I prefix the calls to get them to work.?
For completeness here is how I am defining my instance of Worker and kicking it off.
onButtonClick: function(button, e, eOpts) {
var ugv = this.getUploadGridView();
var fs = this.getFileUploadStore();
var worker = new Worker('/js/UploadFileWorker.js');
worker.onmessage = function(e) {
console.log(e);
};
var selected = ugv.getSelectionModel().getSelection();
Ext.each(selected, function(m) {
var o = {};
o.name = m.get('name');
o.size = m.get('size');
o.status = m.get('status');
o.file = m.file;
worker.postMessage(o);
});
},
Again the actual uploading of the files works great, I can't figure out how to call postMessage(); from inside the xhr callbacks.
This is apparently a bug that has been fixed just recently.
Issue 71433002: Enable XHR upload progress events for Workers. (Closed)
Workaround until Chrome gets updated
xhr.addEventListener(
instead of
xhr.upload.addEventListener(
This has the drawback that progress only gets called for every 1MB, files smaller than 1MB never get a progress event fired.

Firefox Extension/Addon : Reading a text file from remote server(URL)

After wasting my two days to find out what's going wrong with this script, finally I decide to ask it.
What I am trying to do
I am trying to read a text file from remote server. Then storing all text file updates to an SQLITE database at the time of my Firefox Extension/Addon get loaded.
What I tried
var updatereader = {
start: function () {
//alert('reading update');
var fURL = null;
var ioService = null;
var fURI = null;
var httpChannel = null;
fURL = "http://www.example.com/addon/mlist.txt";
ioService = Components.classes["#mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
fURI = ioService.newURI(fURL, null, null);
httpChannel = ioService.newChannelFromURI(fURI).QueryInterface(Components.interfaces.nsIHttpChannel);
httpChannel.asyncOpen(updatereader.StreamReader, null);
},
onUpdateCompleted: function () {
},
StreamReader:
{
fOutputStream: null,
fPointer: null,
tempFile: "mlist.txt",
onStartRequest: function (aRequest, aContext) {
//alert('onStart');
updatereader.StreamReader.fOutputStream = Components.classes["#mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
updatereader.StreamReader.fPointer = Components.classes["#mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
updatereader.StreamReader.fPointer.append(updatereader.StreamReader.tempFile);
updatereader.StreamReader.fOutputStream.init(updatereader.StreamReader.fPointer, 0x02 | 0x20 | 0x08, 0644, 0);
},
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
//control flow is not entering here - may be here is somehting missing
var sStream = null;
var tempBuffer = null;
sStream = Components.classes["#mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
sStream.init(aInputStream);
tempBuffer = sStream.read(aCount);
updatereader.StreamReader.fOutputStream.write(tempBuffer, aCount);
},
onStopRequest: function (aRequest, aContext, aStatusCode) {
//alert('onStop');
var currentDate = new Date();
if (aStatusCode == 0) {
fileInputStream = Components.classes["#mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
updatereader.StreamReader.fOutputStream.close();
fileInputStream.init(updatereader.StreamReader.fPointer, 0x01, 0, 0);
lineInputStream = fileInputStream.QueryInterface(Components.interfaces.nsILineInputStream);
//pass data to somewhere
var dbH = new dbstore();
dbH.updateData(lineInputStream);
lineInputStream.close();
updatereader.StreamReader.fPointer.remove(false);
updatereader.onUpdateCompleted();
} else {
}
}
}
}
Problem:
Getting nothing in lineInputStream which passes the read data to somewhere else for storing it.
Area of problem:
Program control flow is not entring to this section
onDataAvailable:
Not getting any error.
First of all, there doesn't really seem to be any need to read the file to the disk first (unless it is really, really big).
I'd just use XMLHttpRequest to get the file, which when run from a privileged context (e.g. add-on code, but not a website) can access any and every valid URI.
XMLHttpRequest will simplify almost everything, e.g. no more onDataAvailable, (usually) no more manual text converting, etc.
Also, no need to ever hit the disk during the transfer.
Code would look something like this:
var req = new XMLHttpRequest();
req.open("GET", "http://www.example.com/addon/mlist.txt"); // file:/// would work too, BTW
req.overrideMimeType("text/plain");
req.addEventListener("load", function() {
// Do something with req.responseText
}, false);
req.addEventListener("error", function() {
// Handle error
}, false);
req.send();
If you want to use XMLHttpRequest in a non-window, e.g. js code module or js components, then you need to first initialize a constructor. This is not required for windows, including XUL windows and by that XUL overlays.
// Add XMLHttpRequest constructor, if not already present
if (!('XMLHttpRequest' in this)) {
this.XMLHttpRequest = Components.Constructor("#mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
}
SDK users should use the request module, or net/xhr if a lower-level API is required.
PS: If you're still interested in using raw channels, here is a minimal example I coded up in a Scratchpad (to run, open a Scratchpad for a privileged location, e.g. about:newtab).
You shouldn't alert from your own implementation: alert() will spin the event loop and causes reentrant code, which is not supported in this context.
var {
classes: Cc,
interfaces: Ci,
results: Cr,
utils: Cu
} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
Cu.import("resource://gre/modules/Services.jsm");
var ConverterStream = Components.Constructor(
"#mozilla.org/intl/converter-input-stream;1",
"nsIConverterInputStream",
"init");
var RC = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
function Listener() {
this.content = "";
}
Listener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener]),
onStartRequest: function(req, ctx) {
console.log("start");
},
onDataAvailable: function(req, ctx, stream, offset, count) {
console.log("data", count);
try {
var cs = new ConverterStream(stream, null /* utf-8 */, 4096, RC);
try {
var str = {};
while (cs.readString(4096, str)) {
this.content += str.value;
}
}
finally {
cs.close();
}
}
catch (ex) {
console.error("data", ex.message, ex);
}
},
onStopRequest: function(req, ctx, status) {
console.log("stop", status,
this.content.substr(0, 20), this.content.length);
}
};
var uri = Services.io.newURI("http://example.org", null, null);
Services.io.newChannelFromURI(uri).asyncOpen(new Listener(), null);

Categories

Resources