ruffle-rs / ruffle

A Flash Player emulator written in Rust
https://ruffle.rs
Other
15.58k stars 809 forks source link

Autoplay behavior needs improvement #8361

Open n0samu opened 1 year ago

n0samu commented 1 year ago

Issue The behavior of web builds of Ruffle with "autoplay": "on" is less than ideal in several ways:

Improvement

ActionWavele commented 1 year ago

I thought browsers break autoplay?

Herschel commented 1 year ago

The animation stops because the audio syncing code is trying to sync the animation to the non-playing sounds. The easiest fix is for the web audio backend to simply bail out of playing any sounds if it the audio context isn't playing. This would fix the all-sounds-playing-at-once issue on resume, too. The one downside is that any background music won't start playing once resumed.

n0samu commented 1 year ago

The one downside is that any background music won't start playing once resumed.

I feel like that's a pretty significant downside because, for example, when you unmute an autoplaying HTML5 video it wouldn't be missing its background music. Users and webmasters wouldn't understand why this should be any different. But still, it would be an improvement from how things are now!

I wonder if there's any way to play audio into some kind of "null backend" if the audio context isn't available? And switch to the web audio backend once the audio context is available? Sorry if that doesn't make sense.

n0samu commented 1 year ago

I just noticed that part of this issue was already described in #6800.

n0samu commented 1 year ago

The animation stops because the audio syncing code is trying to sync the animation to the non-playing sounds. The easiest fix is for the web audio backend to simply bail out of playing any sounds if it the audio context isn't playing. This would fix the all-sounds-playing-at-once issue on resume, too. The one downside is that any background music won't start playing once resumed.

Actually I now think this is a good suggestion. To address the background music issue, I think we should check if the sound has looping enabled, and if so, we should store a reference to it for later when bailing. And then when the audio context resumes, we can call a method on the web audio backend to tell it to check if those looping sounds would still be playing, and play them now if so.

Edit: That last part may not even be necessary, we might be able to get away with just pretending nothing is wrong and playing the sound as usual if it's a looping sound, as we're currently doing for all sounds

n0samu commented 1 year ago

I tried this approach and it was very easy to implement, but it didn't work for games like Home Sheep Home or Volt Connect 2 because they expect to receive events from the sounds after they play. For example, Volt Connect 2 plays its music in non-looping segments, and adds a completion listener to each segment that makes the next segment start. If we don't add the sound, events don't fire. Both games end up never playing any music.

After discussion with @torokati44 on Discord, it seems like the proper approach would be to keep pumping our audio buffers even if the browser isn't playing any sound from them. That would mean adding an interval in JS that periodically calls play() on our buffer, just like the onended handler does. But this means we're passing the buffer in 2 separate places, and we can't just clone the buffer because pumping the clone won't affect the original. So @torokati44 suggested that we probably need to use an Arc to manage access to the buffer. This feels quite a bit beyond my skill level, and it seems like no one else is going to pick up this issue :(