core: Support start, stop, event sounds in audio backend

Event sounds on the timeline in Flash have a "sync" setting,
which allows them to stop other sounds or only play if the
sound is not already playing.
This commit is contained in:
Mike Welsh 2019-09-27 10:23:53 -07:00
parent 875f250b86
commit af163d9183
4 changed files with 74 additions and 4 deletions

View File

@ -37,6 +37,15 @@ pub trait AudioBackend {
/// Good ol' stopAllSounds() :-)
fn stop_all_sounds(&mut self);
/// Stops all active sound instances of a particular sound.
/// Used by SWF `StartSound` tag with `SoundEvent::Stop`.
fn stop_sounds_with_handle(&mut self, handle: SoundHandle);
/// Returns wheter a sound clip is playing.
/// Used by SWF `StartSouynd` tag with `SoundEvent:Start`,
/// which only plays a sound if that sound is not already playing.
fn is_sound_playing_with_handle(&mut self, handle: SoundHandle) -> bool;
// TODO: Eventually remove this/move it to library.
fn is_loading_complete(&self) -> bool {
true
@ -74,8 +83,11 @@ impl AudioBackend for NullAudioBackend {
) -> AudioStreamHandle {
self.streams.insert(())
}
fn stop_all_sounds(&mut self) {}
fn stop_sounds_with_handle(&mut self, _handle: SoundHandle) {}
fn is_sound_playing_with_handle(&mut self, _handle: SoundHandle) -> bool {
false
}
}
impl Default for NullAudioBackend {

View File

@ -1255,7 +1255,22 @@ impl<'gc, 'a> MovieClip<'gc> {
) -> DecodeResult {
let start_sound = reader.read_start_sound_1()?;
if let Some(handle) = context.library.get_sound(start_sound.id) {
context.audio.start_sound(handle, &start_sound.sound_info);
use swf::SoundEvent;
// The sound event type is controlled by the "Sync" setting in the Flash IDE.
match start_sound.sound_info.event {
// "Event" sounds always play, independent of the timeline.
SoundEvent::Event => context.audio.start_sound(handle, &start_sound.sound_info),
// "Start" sounds only play if an instance of the same sound is not already playing.
SoundEvent::Start => {
if !context.audio.is_sound_playing_with_handle(handle) {
context.audio.start_sound(handle, &start_sound.sound_info);
}
}
// "Stop" stops any active instances of a given sound.
SoundEvent::Stop => context.audio.stop_sounds_with_handle(handle),
}
}
Ok(())
}

View File

@ -351,6 +351,20 @@ impl AudioBackend for CpalAudioBackend {
sound_instances.clear();
}
fn stop_sounds_with_handle(&mut self, handle: SoundHandle) {
let mut sound_instances = self.sound_instances.lock().unwrap();
let handle = Some(handle);
sound_instances.retain(|_, instance| instance.handle == handle);
}
fn is_sound_playing_with_handle(&mut self, handle: SoundHandle) -> bool {
let sound_instances = self.sound_instances.lock().unwrap();
let handle = Some(handle);
sound_instances
.iter()
.any(|(_, instance)| instance.handle == handle && instance.active)
}
fn tick(&mut self) {}
}

View File

@ -54,10 +54,12 @@ type Decoder = Box<dyn Iterator<Item = [i16; 2]>>;
#[allow(dead_code)]
enum AudioStream {
Decoder {
handle: SoundHandle,
decoder: Decoder,
is_stereo: bool,
}, // closure: Option<Closure<Box<FnMut(web_sys::AudioProcessingEvent)>>> } ,
AudioBuffer {
handle: SoundHandle,
node: web_sys::AudioBufferSourceNode,
},
}
@ -125,7 +127,7 @@ impl WebAudioBackend {
}
}
let audio_stream = AudioStream::AudioBuffer { node };
let audio_stream = AudioStream::AudioBuffer { node, handle };
STREAMS.with(|streams| {
let mut streams = streams.borrow_mut();
streams.insert(audio_stream)
@ -161,6 +163,7 @@ impl WebAudioBackend {
};
let audio_stream = AudioStream::Decoder {
handle,
decoder,
is_stereo: sound.format.is_stereo,
//closure: None,
@ -487,7 +490,7 @@ impl AudioBackend for WebAudioBackend {
STREAMS.with(|streams| {
let mut streams = streams.borrow_mut();
for (_, stream) in streams.iter() {
if let AudioStream::AudioBuffer { node } = stream {
if let AudioStream::AudioBuffer { node, .. } = stream {
let _ = node.stop();
}
// TODO: Have to handle Decoder nodes. (These may just go into a different backend.)
@ -495,6 +498,32 @@ impl AudioBackend for WebAudioBackend {
streams.clear();
})
}
fn stop_sounds_with_handle(&mut self, handle: SoundHandle) {
STREAMS.with(|streams| {
let mut streams = streams.borrow_mut();
streams.retain(|_, instance| {
let instance_handle = match instance {
AudioStream::Decoder { handle, .. } => *handle,
AudioStream::AudioBuffer { handle, .. } => *handle,
};
instance_handle == handle
});
})
}
fn is_sound_playing_with_handle(&mut self, handle: SoundHandle) -> bool {
STREAMS.with(|streams| {
let streams = streams.borrow();
streams.iter().any(|(_, instance)| {
let instance_handle = match instance {
AudioStream::Decoder { handle, .. } => *handle,
AudioStream::AudioBuffer { handle, .. } => *handle,
};
instance_handle == handle
})
})
}
}
// Janky resmapling code.