Voice Rework -- Events, Track Queues (#806)

This implements a proof-of-concept for an improved audio frontend. The largest change is the introduction of events and event handling: both by time elapsed and by track events, such as ending or looping. Following on from this, the library now includes a basic, event-driven track queue system (which people seem to ask for unusually often). A new sample, `examples/13_voice_events`, demonstrates both the `TrackQueue` system and some basic events via the `~queue` and `~play_fade` commands.

Locks are removed from around the control of `Audio` objects, which should allow the backend to be moved to a more granular futures-based backend solution in a cleaner way.
This commit is contained in:
Kyle Simpson
2020-10-29 20:25:20 +00:00
committed by Alex M. M
commit 7e4392ae68
76 changed files with 8756 additions and 0 deletions

118
src/driver/tasks/events.rs Normal file
View File

@@ -0,0 +1,118 @@
use super::message::*;
use crate::{
events::{EventStore, GlobalEvents, TrackEvent},
tracks::{TrackHandle, TrackState},
};
use flume::Receiver;
use tracing::{debug, info, instrument, trace};
#[instrument(skip(_interconnect, evt_rx))]
pub(crate) async fn runner(_interconnect: Interconnect, evt_rx: Receiver<EventMessage>) {
let mut global = GlobalEvents::default();
let mut events: Vec<EventStore> = vec![];
let mut states: Vec<TrackState> = vec![];
let mut handles: Vec<TrackHandle> = vec![];
loop {
use EventMessage::*;
match evt_rx.recv_async().await {
Ok(AddGlobalEvent(data)) => {
info!("Global event added.");
global.add_event(data);
},
Ok(AddTrackEvent(i, data)) => {
info!("Adding event to track {}.", i);
let event_store = events
.get_mut(i)
.expect("Event thread was given an illegal store index for AddTrackEvent.");
let state = states
.get_mut(i)
.expect("Event thread was given an illegal state index for AddTrackEvent.");
event_store.add_event(data, state.position);
},
Ok(FireCoreEvent(ctx)) => {
let ctx = ctx.to_user_context();
let evt = ctx
.to_core_event()
.expect("Event thread was passed a non-core event in FireCoreEvent.");
trace!("Firing core event {:?}.", evt);
global.fire_core_event(evt, ctx).await;
},
Ok(AddTrack(store, state, handle)) => {
events.push(store);
states.push(state);
handles.push(handle);
info!("Event state for track {} added", events.len());
},
Ok(ChangeState(i, change)) => {
use TrackStateChange::*;
let max_states = states.len();
debug!(
"Changing state for track {} of {}: {:?}",
i, max_states, change
);
let state = states
.get_mut(i)
.expect("Event thread was given an illegal state index for ChangeState.");
match change {
Mode(mode) => {
let old = state.playing;
state.playing = mode;
if old != mode && mode.is_done() {
global.fire_track_event(TrackEvent::End, i);
}
},
Volume(vol) => {
state.volume = vol;
},
Position(pos) => {
// Currently, only Tick should fire time events.
state.position = pos;
},
Loops(loops, user_set) => {
state.loops = loops;
if !user_set {
global.fire_track_event(TrackEvent::Loop, i);
}
},
Total(new) => {
// Massive, unprecedented state changes.
*state = new;
},
}
},
Ok(RemoveTrack(i)) => {
info!("Event state for track {} of {} removed.", i, events.len());
events.remove(i);
states.remove(i);
handles.remove(i);
},
Ok(RemoveAllTracks) => {
info!("Event state for all tracks removed.");
events.clear();
states.clear();
handles.clear();
},
Ok(Tick) => {
// NOTE: this should fire saved up blocks of state change evts.
global.tick(&mut events, &mut states, &mut handles).await;
},
Err(_) | Ok(Poison) => {
break;
},
}
}
info!("Event thread exited.");
}