Scrungepipes
Scrungepipes

Reputation: 37581

html5 audio plays if src set statically but not if set dynamically

I have a problem where I can play an audio file if it is added directly to the html5 source code as the src attribute, but it doesn't play if I try to set the src at runtime.

Originally I had the following piece of HTML containing a few audio tags:

<audio id="audio1" controls preload="none" src="http:/theurl/clip1.mp3"></audio>
<audio id="audio2" controls preload="none" src="http:/theurl/clip2.mp3"></audio>

With the clips being played with the following function that gets called with the id and clip url:

function audioClick(id, clip) {
    var audio = document.getElementById(id);
    if (audio.paused) {
        audio.play();
    }
    else {
        audio.pause();
        audio.currentTime = 0;
    }
}

This works and when the user clicks on the relevant part of the page the appropriate audio file plays, however the problem is that the browser takes about 3-4 seconds to load the page which is too long. I have tracked down the reason for the delay to be due to the src attribute being remote files. And setting preload to "none" or "metadata" doesn't do anything to speed things up.

So to workaround this I wanted to set the src at runtime, so changed the function to this:

function audioClick(id, clip) {
    var audio = document.getElementById(id);
    if (audio.paused) {
        audio.setSrc(clip);
        audio.load();
        audio.play();
    }
    else {
        audio.pause();
        audio.currentTime = 0;
    }
}

I tried setting the html to this

<audio id="audio1" controls></audio>
<audio id="audio2" controls></audio>

But there were two problems: 1) the audio doesn't play 2) the controls displays an initial message saying the file can't be played

I don't there to be a message in the controls saying the file can't be played so I changed the html to:

<audio id="audio1"></audio>
<audio id="audio2"></audio>

However the problem with the file not playing remains.

Why doesn't the audio file play when done this way?

Upvotes: 3

Views: 4189

Answers (2)

Rob Lynch
Rob Lynch

Reputation: 611

There are a few pitfalls when your target is iOS. The first being that setting the attribute preload of the audio tag does nothing. iOS will just do what it wants and there isn't much we can do about that.

I think the reason Yoshi's code works great in chrome but not in iOS is because iOS requires that all calls to audio.play() be in the same call stack as a click event. Since the call to play results from the canplay event I think it is being ignored by iOS. Also, iOS won't preload any of the audio when you set the src. You need to call audio.load() to do that, but just as before, you'd need the user to click something to have iOS actually execute that function.

To make it work in iOS you need to avoid the canplay callback and simply use:

audio.src = src;
audio.play();

It is less than ideal. There may be a second or two while Safari loads the file, but it should work. iOS handling of HTML5 audio is fairly limited compared with the android browser or better yet, the new mobile chrome. Mobile Safari kinda throws the spec out the window and just does what it wants.

Upvotes: 3

Yoshi
Yoshi

Reputation: 54649

try something like:

function audioClick(id, src) {
  var audio = document.getElementById(id);
  if (audio.paused) {
    audio.addEventListener('canplay', function canplay() {
      this.removeEventListener('canplay', canplay, false);
      this.play();
    }, false);

    audio.src = src;
  }
  else {
    audio.pause();
    audio.currentTime = 0;
  }
}

and have a look at other available media events.

Upvotes: 0

Related Questions