Catching 'file not found' errors with loadSound()

I'm loading several audio files with loadSound() and storing them in an array, and some of the files can't be found (404 error). However, this is a normal behavior or my sketch (I'm parsing external data and using it to load the files, so there might be inconsistencies), and I wanted to know if I could catch these errors and simply ignore them.

var list = ["horn", "bell", "guitar"];
var allSounds = [];
function preload() {
    for (i=0; i<list.length; i++) {
        var path = "audio/" + list[i] + ".mp3";
        var thisSound = loadSound(path, storeSound);
    }
}
function storeSound(soundFile) {
    // could I catch the errors here?
    allSounds.push(soundFile);
}

Answers

  • insert this

            var path = "audio/" + list[i] + ".mp3";
            if (fileExists(path)) {
                 var thisSound = loadSound(path, storeSound);
            }
    

    and this

    public boolean fileExists(String fileName) {
            File file=new File(fileName);
            boolean exists = file.exists();
            if (!exists) {
                return false;
            }else{
                return true;
            }
        } 
    
  • instead of all sounds as an array you could make it an ArrayList (to avoid empty slots in the array)

    or with an array you can probably check whether the entry is != null and only then play it

  • edited November 2015

    @Chrisir: did you test that in p5js? It looks like Java code and not JS :/

    When I try and load a file from an invalid URL loadSound simply chokes. Looking at the source there doesn't appear to be any obvious error handling for a failed load; which is a shame as it should be fairly simple to check the request status and fail gracefully...

    As such the best thing @ncc1989 can do is file a bug report.

    Also this is p5js: JavaScript arrays are not fixed like Java arrays: they're as flexible as ArrayLists...

    Ideally what should happen is that loadSound fires a callback on success or failure - including a reference to the sound file if loaded. You could then append to your array based on that status:

    var sound1,
           sound2,
           loadedSounds = [],
           loadCallback = function(sound) {
              // for illustrative purposes - this doesn't work at present...
              if(sound) {
                  loadedSounds.push(sound)
              }
          };
    
    
    function preload() {
      sound1 = loadSound('pop.mp3', loadCallback);
      sound2 = loadSound('brokenURL.mp3', loadCallback);
    }
    

    [edit: refactored my suggested code; but I think it needs more thought...]

  • edited November 2015

    Looking at the source there doesn't appear to be any obvious error handling for a failed load;
    ... which is a shame as it should be fairly simple to check the request status and fail gracefully...

    Right on the spot: None whatsoever! 3:-O
    Take a look below... just onload() and no onerror() to be seen: 8-}

    p5.SoundFile.prototype.load = function (callback) {
        if (this.url != undefined && this.url != '') {
          var sf = this;
          var request = new XMLHttpRequest();
          request.addEventListener('progress', function (evt) {
            sf._updateProgress(evt);
          }, false);
          request.open('GET', this.url, true);
          request.responseType = 'arraybuffer';
          // decode asyncrohonously
          var self = this;
          request.onload = function () {
            ac.decodeAudioData(request.response, function (buff) {
              self.buffer = buff;
              self.panner.inputChannels(buff.numberOfChannels);
              if (callback) {
                callback(self);
              }
            });
          };
          request.send();
        } else if (this.file != undefined) {
          var reader = new FileReader();
          var self = this;
          reader.onload = function () {
            ac.decodeAudioData(reader.result, function (buff) {
              self.buffer = buff;
              self.panner.inputChannels(buff.numberOfChannels);
              if (callback) {
                callback(self);
              }
            });
          };
          reader.readAsArrayBuffer(this.file);
        }
      };
    

    IMO, that's a serious gross error! Why would a developer assume that's OK not to provide an error handler callback for resource loading??? =;
    And instead, provide some superfluous whileLoading() callback??? :-@

    I can't see another way but to hack and override p5.SoundFile.prototype.load() till they come on top of their senses! /:)

  • edited November 2015

    JavaScript arrays are not fixed like Java arrays: they're as flexible as ArrayLists...

    Actually the only Java container class that comes as close as JS' is the slowest of all LinkedList:
    http://docs.Oracle.com/javase/8/docs/api/java/util/LinkedList.html

    That's why JS' arrays are much slower than Java's. In order to provide such flexibility, performance sacrifices needs to be made... :-q

  • Thanks to all for the replies! Agreed with @blindfish and @GoToLoop, there really should be an error handler for loadSound().

    @Chrisir 's idea of checking if the file exists can be applied, but the way I did it is pretty much a hack. I used jQuery's $.get to check if the file exists. This is a bad approach because it adds another library and makes another asynchronous request. In any case, I'll paste it here for those who don't want to hack the p5 library directly!

    function fileExists(path) {
      $.get(path)
      .done(function() {
        var thisSound = loadSound(path, storeSound);
      }).fail(function() {
        // do something with the error
      });
    }
    
    var list = ["horn", "bell", "guitar"];
    var allSounds = [];
    function preload() {
      for (i=0; i<list.length; i++) {
        var path = "audio/" + list[i] + ".mp3";
        fileExists(path);
      }
    }
    
    function storeSound(soundFile) {
      allSounds.push(soundFile);
    }
    
  • @ncc1989 - you can indeed apply Chrisir's suggestion, but there's a better library to use than jQuery. It's called vanillajs :P

    Seriously though, these days a simple request like this isn't too difficult to implement without a library and it's a useful learning exercise. And do submit an issue to the p5 sound team: they seem pretty receptive and responsive...

  • @blindfish – agreed, using vanilla js would definitely be quicker, but the syntax for this kind of request drives me so kind of crazy that I even forget sometimes it is possible :p

    But thanks for the tip! I've just opened an issue for the missing error handling here: https://github.com/processing/p5.js-sound/issues/83

  • Just to let you all know that the issue was closed: https://github.com/processing/p5.js-sound/issues/83

    Here's the new loadSound() function, as updated by @therewasaguy:

    loadSound(path, [on load callback], [on file not found callback], [while loading callback]);

Sign In or Register to comment.