1

I'm developping a simple web application which needs to load 16 audio files to process an hearing test. But my code is loading the files twice!

The application needs to be very light and fast, so it's a big problem.

For the same reason, i don't want to use jQuery libraries.

function loadSound(array) {
    var i = 0;
    array.forEach(function(soundUrl) {
        var request = new XMLHttpRequest();
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                sources[i] = contextAudio.createBufferSource();
                sources[i].buffer = buffer;
                sources[i].connect(contextAudio.destination);
                i++
            });
        };
        request.send(null);
    });
}

The soundList array and loadSound calling:

var soundList = new Array(
'http://localhost/testauditif/sons/440L.wav',
'http://localhost/testauditif/sons/440R.wav',
'http://localhost/testauditif/sons/125L.wav',
'http://localhost/testauditif/sons/125R.wav',
'http://localhost/testauditif/sons/250L.wav',
'http://localhost/testauditif/sons/250R.wav',
'http://localhost/testauditif/sons/500L.wav',
'http://localhost/testauditif/sons/500R.wav',
'http://localhost/testauditif/sons/1000L.wav',
'http://localhost/testauditif/sons/1000R.wav',
'http://localhost/testauditif/sons/2000L.wav',
'http://localhost/testauditif/sons/2000R.wav',
'http://localhost/testauditif/sons/4000L.wav',
'http://localhost/testauditif/sons/4000R.wav',
'http://localhost/testauditif/sons/8000L.wav',
'http://localhost/testauditif/sons/8000R.wav'
);

loadSound(soundList);

The javascript console: XHRGEThttp://localhost/testauditif/sons/440L.wav [HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440R.wav
[HTTP/1.1 200 OK 0ms]

0 script.js:88:12
XHRGEThttp://localhost/testauditif/sons/125L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125R.wav
[HTTP/1.1 200 OK 0ms]

0 script.js:88:12
XHRGEThttp://localhost/testauditif/sons/250L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000R.wav
[HTTP/1.1 200 OK 0ms]
10
  • from where you are calling loadSound method? Commented Jun 11, 2019 at 5:19
  • How can you tell that the files are loading twice? Commented Jun 11, 2019 at 8:48
  • try to write your input array to the console before going into the forEach loop like for example console.log(array) to see if you maybe have the sound url twice in your array? Commented Jun 11, 2019 at 8:57
  • You mean to say you are observing a total of 32 requests to urls instead of 16? Or there are a few files missing while a few files are coming twice in sources array? Commented Jun 11, 2019 at 9:42
  • @2x2p Are you sure that it wouldn't cause the closure problem? As it is, I see the variable i messing up due to closure causing a race around. Commented Jun 11, 2019 at 9:43

2 Answers 2

1

Closures!!!

Lets take a clos(ure)er look at what's happening with your code.

You start iterating over the array with each URL defined as soundUrl in array.forEach(function(soundUrl) {}). Please note that variable i is retained as it is inside this anonymous function because it is defined in the loadSound function.

You send your request using request.send(null);. In the request.onloadend function, the i value is 0. All good so far.

Now comes the trouble. Without waiting for onloadend to be called, you move to the next soundUrl in your forEach loop. When the request.onloadend is initialized, the i value is still 0 unless onloadend for previous request is already called. (This is highly unlikely assuming the audio files take a while to download to browser)

Somewhere in middle of your forEach loop, request.onloadend get called for your first request incrementing the i.

End result? You end up with a pile garbage for in sources array where few files are downloaded, few are overwritten by next audio files with gaping holes in between.

PS: As the code stands, it doesn't work. Is that the real issue? May be. Without further information about what the array contains and how many requests the code is sending to backend.


The solution I would suggest.

function loadSound(array) {
    array.forEach(function(soundUrl, i) {
        // No need to declare var i for iteration. Foreach provides the index.
        var request = new XMLHttpRequest();
        // Creating an instance of XMLHttpRequest inside loop to ensure 
        // request.onloadend does not get overriden in the next iteration.
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            // Retain i inside the function using a local variable inside the callback function.
            var idx = i;
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                // Not sure whether decodeAudioData in asyc. If so, you again need to retain idx inside the callback.
                var src_idx = idx;
                sources[src_idx] = contextAudio.createBufferSource();
                sources[src_idx].buffer = buffer;
                sources[src_idx].connect(contextAudio.destination);
            });
        };
        request.send(null);
    });
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the code describtion. I fully agree with you. The problem is the asynchronous request. I also tried to use synchronous request but it's completly obsolete and may slow the navigator. But is there a solution to create an asynchronous "queue"?
My mistake. I will edit the answer to provide a solution that solves the problem.
0

Please try if this solves your problem

This version is not using var i for setting things in sources. If you need i later to know how many sources have been buffered. just make i the length of sources.

const request = new XMLHttpRequest();
var sources = null;
var i = null; // only if you want to keep i 

function loadSound(array) {
    console.log(array); // only here for testing
    var sources = []; // define or re-define sources as empty array

    array.forEach(function(soundUrl) {
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            console.log(request); // only here for testing
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                let newsource = contextAudio.createBufferSource(); 
                // let is ES6 style it can also work without let
                newsource.buffer = buffer;
                newsource.connect(contextAudio.destination);
                sources.push(newsource); 
                // added your new source to your sources array
                var i = sources.length; 
                // if you need var i elsewhere it now contains total number of sources
                // if you don't need var i hereafter remove it from the code
                console.log(sources); // only here for testing
            });
        };
        request.send(null);
    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.