HTML5 audio object does not play on iPad (when called from setTimeout)

I have a page with a hidden <audio>object that starts and stops using a custom button through javascript. (The reason is that I want to customize the button, and that drawing an audio player seems to still render rendering performance on the iPad anyway). Simplified example (in coffeescript):

// Works fine on all browsers

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play          // Bind button click event with jQuery

_play: (e) =>
  @_audio[0].play()                    // Call play() on audio element

The sound plays perfectly when the function associated with the event is triggered click, but I really want the animation to complete before the file plays, so I put it .play()inside setTimeout. However, I just can't get this to work:

// Will not play on iPad

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play          // Bind button click event with jQuery

_play: (e) =>
  setTimeout (=>                       // Declare a 300ms timeout
    @_audio[0].play()                  // Call play() on audio element
  ), 300

, @_audio (this._audio) play(). iPad?

. , . @apsillers .

+5
3

. iOS Apple:

... JavaScript play() load() , , play() load() . , , onLoad="play()" .

, setTimeout() , , , setTimeout() .

: iOS , , , /, , . play(), , setTimeout() play(). , iOS , .

+10

, , , , , , , , , ,

, , . ,

+1

@apsillers , , iPad, . , .

The requirement, apparently, is that the call play()can only be inside one setTimeout(Therefore, the simplified example that I gave in the original question really works - initially I had a play()few setTimeoutdeep ones).

So this will work:

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>
  setTimeout (=>
    @_audio[0].play()                  // play() is only inside one setTimeout
  ), 300

And this will also work:

constructor: (@_button, @_audio) ->
  @_button.on 'click', =>
    setTimeout ((e) =>
      @_play(e)
    ), 300

_play: (e) =>
  @_audio[0].play()                    // Still only inside one setTimeout

But this will not work:

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>
  setTimeout (=>
    // Something useful
    setTimeout (=>
      @_audio[0].play()                // play() is inside two setTimeouts
    ), 300
  ), 300

And this will be (my original setup):

constructor: (@_button, @_audio) ->
  @_button.on 'click', @_play

_play: (e) =>

  @_button
    .animate { prop: value }, 300, =>

      setTimeout (=>
        @_audio[0].play()              // play() still 'too deep'
      ), 300

In the last example, it seems that jQuery animate callback is being called from another setTimeoutinside the library, so play()again "too deep".

+1
source

All Articles