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:
24
src/driver/tasks/message/core.rs
Normal file
24
src/driver/tasks/message/core.rs
Normal 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,
|
||||
}
|
||||
31
src/driver/tasks/message/events.rs
Normal file
31
src/driver/tasks/message/events.rs
Normal 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),
|
||||
}
|
||||
32
src/driver/tasks/message/mixer.rs
Normal file
32
src/driver/tasks/message/mixer.rs
Normal 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,
|
||||
}
|
||||
49
src/driver/tasks/message/mod.rs
Normal file
49
src/driver/tasks/message/mod.rs
Normal 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()));
|
||||
}
|
||||
}
|
||||
7
src/driver/tasks/message/udp_rx.rs
Normal file
7
src/driver/tasks/message/udp_rx.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use super::Interconnect;
|
||||
|
||||
pub(crate) enum UdpRxMessage {
|
||||
ReplaceInterconnect(Interconnect),
|
||||
|
||||
Poison,
|
||||
}
|
||||
4
src/driver/tasks/message/udp_tx.rs
Normal file
4
src/driver/tasks/message/udp_tx.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub enum UdpTxMessage {
|
||||
Packet(Vec<u8>), // TODO: do something cheaper.
|
||||
Poison,
|
||||
}
|
||||
12
src/driver/tasks/message/ws.rs
Normal file
12
src/driver/tasks/message/ws.rs
Normal 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,
|
||||
}
|
||||
Reference in New Issue
Block a user