12

i use the code below to preload an array of audio files (after user interacts with a button starting the process). After all audio files fired "canplaythrough" the code proceeds:

var loaded = 0;
function loadedAudio() {
    // this will be called every time an audio file is loaded
    // we keep track of the loaded files vs the requested files
    loaded++;
    console.log(loaded + " audio files loaded!");
    if (loaded == audioFiles.length){
      // all have loaded
      main();
    }
}

function preloadsounds()
{
  $("#loader").show();
  console.log(level.config);
  audioFiles = level.config.soundfiles;

  // we start preloading all the audio files with html audio
  for (var i in audioFiles) {
      preloadAudio(audioFiles[i]);
  }

}

function preloadAudio(url)
{
  console.log("trying to preload "+ url);
  var audio = new Audio();
  // once this file loads, it will call loadedAudio()
  // the file will be kept by the browser as cache
  audio.addEventListener('canplaythrough', loadedAudio, false);

  audio.addEventListener('error', function failed(e)
  {
    console.log("COULD NOT LOAD AUDIO");
    $("#NETWORKERROR").show();
  });
  audio.src = url;
}

works great on Android (Chrome and Firefox) but not a single canplaythrough event is fired on iOs Safari (tested live on 5s and emulated X both 11.x). All files are served from the same Origin. I also don't get any Error messages in my log.

What am I missing?

( the basis for the code above i go from: https://stackoverflow.com/a/31351186/2602592 )

gauguerilla
  • 675
  • 8
  • 28

3 Answers3

30

Try calling the load() method after setting the src.

function preloadAudio(url)
{
  console.log("trying to preload "+ url);
  var audio = new Audio();
  // once this file loads, it will call loadedAudio()
  // the file will be kept by the browser as cache
  audio.addEventListener('canplaythrough', loadedAudio, false);

  audio.addEventListener('error', function failed(e)
  {
    console.log("COULD NOT LOAD AUDIO");
    $("#NETWORKERROR").show();
  });
  audio.src = url;

  audio.load();  // add this line
}
Gabriele Petrioli
  • 183,160
  • 33
  • 252
  • 304
  • excellent! that did the trickt! for future reference: https://www.w3schools.com/tags/av_met_load.asp "The load() method is used to update the audio/video element after changing the source or other settings." Thank you! – gauguerilla Apr 12 '18 at 10:30
  • 1
    Not working for me in iOS. No error or warning, just never fires the event even after adding `load()`. Testing in iphone 8 11.4 simulator and IRL iphone X iOS 12. I do however see the request cancelled for some reason, code 206, in the inspector network tab for mobile safari. Damn, safari is such a trash browser. – Stephen Tetreault Sep 25 '18 at 22:53
  • 1
    @StephenTetreault [206 status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206) means that the browser successfully received partial content for the file. Is this streaming audio ? The code above waits for the whole file to be loaded so that might be a problem with streaming content. Try using the `canplay` or `loadedmetadata` event instead of the `canplaythrough`. – Gabriele Petrioli Sep 26 '18 at 07:29
  • Was an easy fix in my scenario @GabrielePetrioli. just for iOS browsers, using a few hundred millisecond timeout resolved it. not the prettiest solution but it works w/o any noticeable issue. – Stephen Tetreault Sep 27 '18 at 14:05
  • Adding the audio to the DOM (before declaration of event listener) solved the issue for me: document.body.append(audio); – Max Oct 30 '19 at 15:09
  • 1
    I had a similar problem with Safari on MacOS. This solved it for me too. – Manngo Apr 10 '20 at 00:04
2

Have you checked Network/Server and confirmed Safari is downloading the audio files?

If Safari's not downloading the audio (or only loading metadata instead of the full file), you could try setting audio.preload = 'auto' before setting audio.src

searlea
  • 7,713
  • 4
  • 31
  • 36
1

I see a lot of claims to make an audio object work, especially on Safari!

To go quickly I can tell you that you do not need much, just know to run everything on Safari 5 and more, and all browsers.

  1. Force the call to trough by reloading the first file in your list or file if you only use one. The Trough event is the one that will allow the user to read the file since it allows to know if the file is loaded and ready to be read. If you build a player, you must plan and let the user click on Play only if the through has occurred.

               ObjectAudio.src = file;

  2. Use trough to update your player

    ObjectAudio.addEventListener ('canplaythrough', Function, false);

  3. Use Progress to update the percentage buffering bar.

    ObjectAudio.addEventListener ('progress', Function, false);

  4. Use the timeupdate event to update the read bar.

    ObjectAudio.addEventListener ('timeupdate', Function, false);

You do not need complex things as I see what you are doing.


** Just one worry about Safari 5 Widows. For the object to work, QuickTime must be installed on the user, otherwise Safari 5 will not recognize the HTML 5 Audio object.

Cherif
  • 53
  • 2