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

View File

@@ -0,0 +1,24 @@
use crate::{
driver::connection::error::Error,
events::EventData,
tracks::Track,
Bitrate,
ConnectionInfo,
};
use flume::Sender;
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum CoreMessage {
ConnectWithResult(ConnectionInfo, Sender<Result<(), Error>>),
Disconnect,
SetTrack(Option<Track>),
AddTrack(Track),
SetBitrate(Bitrate),
AddEvent(EventData),
Mute(bool),
Reconnect,
FullReconnect,
RebuildInterconnect,
Poison,
}

View File

@@ -0,0 +1,31 @@
use crate::{
events::{CoreContext, EventData, EventStore},
tracks::{LoopState, PlayMode, TrackHandle, TrackState},
};
use std::time::Duration;
pub(crate) enum EventMessage {
// Event related.
// Track events should fire off the back of state changes.
AddGlobalEvent(EventData),
AddTrackEvent(usize, EventData),
FireCoreEvent(CoreContext),
AddTrack(EventStore, TrackState, TrackHandle),
ChangeState(usize, TrackStateChange),
RemoveTrack(usize),
RemoveAllTracks,
Tick,
Poison,
}
#[derive(Debug)]
pub enum TrackStateChange {
Mode(PlayMode),
Volume(f32),
Position(Duration),
// Bool indicates user-set.
Loops(LoopState, bool),
Total(TrackState),
}

View File

@@ -0,0 +1,32 @@
use super::{Interconnect, UdpRxMessage, UdpTxMessage, WsMessage};
use crate::{tracks::Track, Bitrate};
use flume::Sender;
use xsalsa20poly1305::XSalsa20Poly1305 as Cipher;
pub(crate) struct MixerConnection {
pub cipher: Cipher,
pub udp_rx: Sender<UdpRxMessage>,
pub udp_tx: Sender<UdpTxMessage>,
}
impl Drop for MixerConnection {
fn drop(&mut self) {
let _ = self.udp_rx.send(UdpRxMessage::Poison);
let _ = self.udp_tx.send(UdpTxMessage::Poison);
}
}
pub(crate) enum MixerMessage {
AddTrack(Track),
SetTrack(Option<Track>),
SetBitrate(Bitrate),
SetMute(bool),
SetConn(MixerConnection, u32),
DropConn,
ReplaceInterconnect(Interconnect),
RebuildEncoder,
Ws(Option<Sender<WsMessage>>),
Poison,
}

View File

@@ -0,0 +1,49 @@
mod core;
mod events;
mod mixer;
mod udp_rx;
mod udp_tx;
mod ws;
pub(crate) use self::{core::*, events::*, mixer::*, udp_rx::*, udp_tx::*, ws::*};
use flume::Sender;
use tracing::info;
#[derive(Clone, Debug)]
pub(crate) struct Interconnect {
pub core: Sender<CoreMessage>,
pub events: Sender<EventMessage>,
pub mixer: Sender<MixerMessage>,
}
impl Interconnect {
pub fn poison(&self) {
let _ = self.events.send(EventMessage::Poison);
}
pub fn poison_all(&self) {
self.poison();
let _ = self.mixer.send(MixerMessage::Poison);
}
pub fn restart_volatile_internals(&mut self) {
self.poison();
let (evt_tx, evt_rx) = flume::unbounded();
self.events = evt_tx;
let ic = self.clone();
tokio::spawn(async move {
info!("Event processor restarted.");
super::events::runner(ic, evt_rx).await;
info!("Event processor finished.");
});
// Make mixer aware of new targets...
let _ = self
.mixer
.send(MixerMessage::ReplaceInterconnect(self.clone()));
}
}

View File

@@ -0,0 +1,7 @@
use super::Interconnect;
pub(crate) enum UdpRxMessage {
ReplaceInterconnect(Interconnect),
Poison,
}

View File

@@ -0,0 +1,4 @@
pub enum UdpTxMessage {
Packet(Vec<u8>), // TODO: do something cheaper.
Poison,
}

View File

@@ -0,0 +1,12 @@
use super::Interconnect;
use crate::ws::WsStream;
#[allow(dead_code)]
pub(crate) enum WsMessage {
Ws(Box<WsStream>),
ReplaceInterconnect(Interconnect),
SetKeepalive(f64),
Speaking(bool),
Poison,
}