216 lines
5.3 KiB
Rust
216 lines
5.3 KiB
Rust
use async_channel::{self, Receiver, Sender};
|
|
use async_std::prelude::*;
|
|
use async_std::task::{self, JoinHandle};
|
|
use rosc::OscMessage;
|
|
use std::collections::VecDeque;
|
|
use thiserror::Error;
|
|
|
|
use async_osc::prelude::*;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum SwitcherError {
|
|
#[error("Channel for switcher events full")]
|
|
CannotSendEvent(#[from] async_channel::SendError<SwitcherEvent>),
|
|
#[error("Channel for OSC messages full")]
|
|
CannotSendMessage(#[from] async_channel::SendError<OscMessage>),
|
|
#[error("Decode OSC packet failed")]
|
|
Osc(rosc::OscError),
|
|
|
|
#[error("IO error")]
|
|
Io(#[from] std::io::Error),
|
|
}
|
|
|
|
impl From<rosc::OscError> for SwitcherError {
|
|
fn from(error: rosc::OscError) -> Self {
|
|
Self::Osc(error)
|
|
}
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, SwitcherError>;
|
|
|
|
pub type ChannelId = usize;
|
|
|
|
#[derive(Debug)]
|
|
pub enum SwitcherEvent {
|
|
Enable(ChannelId),
|
|
Disable(ChannelId),
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct SwitcherState {
|
|
// osc_tx: Sender<OscMessage>,
|
|
channels: Vec<ChannelState>,
|
|
active_channel: usize,
|
|
messages: Messages,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct ChannelState {
|
|
id: ChannelId,
|
|
pub name: String,
|
|
pub locked: bool,
|
|
pub enabled: bool,
|
|
}
|
|
|
|
impl ChannelState {
|
|
pub fn new(id: ChannelId) -> Self {
|
|
Self {
|
|
id,
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct SwitcherHandle {
|
|
events_tx: Sender<SwitcherEvent>,
|
|
osc_rx: Option<Receiver<OscMessage>>,
|
|
task: JoinHandle<Result<()>>,
|
|
}
|
|
|
|
impl SwitcherHandle {
|
|
pub fn run() -> Self {
|
|
let (osc_tx, osc_rx) = async_channel::unbounded();
|
|
let (events_tx, events_rx) = async_channel::unbounded();
|
|
let state = SwitcherState::new();
|
|
let task = task::spawn(run_loop(state, events_rx, osc_tx));
|
|
SwitcherHandle {
|
|
events_tx,
|
|
osc_rx: Some(osc_rx),
|
|
task,
|
|
}
|
|
}
|
|
pub async fn join(self) -> Result<()> {
|
|
self.task.await
|
|
}
|
|
|
|
pub async fn send(&self, event: SwitcherEvent) -> Result<()> {
|
|
log::debug!("switcher got event: {:?}", event);
|
|
self.events_tx.send(event).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn sender(&self) -> Sender<SwitcherEvent> {
|
|
self.events_tx.clone()
|
|
}
|
|
|
|
pub fn take_receiver(&mut self) -> Option<Receiver<OscMessage>> {
|
|
self.osc_rx.take()
|
|
}
|
|
}
|
|
|
|
pub async fn run_loop(
|
|
mut state: SwitcherState,
|
|
mut events_rx: Receiver<SwitcherEvent>,
|
|
osc_tx: Sender<OscMessage>,
|
|
) -> Result<()> {
|
|
state.init();
|
|
loop {
|
|
while let Some(message) = state.pop_message() {
|
|
osc_tx.send(message).await?;
|
|
}
|
|
if let Some(event) = events_rx.next().await {
|
|
state.on_event(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SwitcherState {
|
|
pub fn new() -> Self {
|
|
Default::default()
|
|
}
|
|
|
|
pub fn init(&mut self) {
|
|
let num_channels = 4;
|
|
for i in 0..num_channels {
|
|
let channel = ChannelState::new(i);
|
|
self.channels.push(channel);
|
|
}
|
|
self.messages.push("/*", ("xmit", 1));
|
|
self.messages.push("/mixer/out/0/bus/0/on", (1.0,));
|
|
self.messages.push("/mixer/bus/0/ch/0/on", (1.0,));
|
|
self.update_state();
|
|
}
|
|
|
|
pub fn on_event(&mut self, event: SwitcherEvent) {
|
|
match event {
|
|
SwitcherEvent::Enable(channel) => self.enable_channel(channel),
|
|
SwitcherEvent::Disable(channel) => self.disable_channel(channel),
|
|
}
|
|
self.update_state();
|
|
}
|
|
pub fn enable_channel(&mut self, channel_id: ChannelId) {
|
|
if let Some(channel) = self.channels.iter_mut().find(|c| c.id == channel_id) {
|
|
if !channel.locked {
|
|
channel.enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn disable_channel(&mut self, channel_id: ChannelId) {
|
|
if let Some(channel) = self.channels.iter_mut().find(|c| c.id == channel_id) {
|
|
if !channel.locked {
|
|
channel.enabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update_state(&mut self) {
|
|
let next_active_channel = self
|
|
.channels
|
|
.iter()
|
|
.rev()
|
|
.find(|c| c.enabled)
|
|
.or(self.channels.get(0))
|
|
.unwrap()
|
|
.id;
|
|
if next_active_channel != self.active_channel {
|
|
self.active_channel = next_active_channel;
|
|
self.on_channel_change();
|
|
}
|
|
}
|
|
|
|
fn on_channel_change(&mut self) {
|
|
for channel in self.channels.iter() {
|
|
if channel.id == self.active_channel {
|
|
self.messages
|
|
.push(format!("/mixer/bus/0/ch/{}/on", channel.id), (1.0,));
|
|
} else {
|
|
self.messages
|
|
.push(format!("/mixer/bus/0/ch/{}/on", channel.id), (0.0,));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pop_message(&mut self) -> Option<OscMessage> {
|
|
self.messages.pop()
|
|
}
|
|
}
|
|
|
|
pub struct Messages {
|
|
inner: VecDeque<OscMessage>,
|
|
}
|
|
|
|
impl Default for Messages {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Messages {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
inner: VecDeque::new(),
|
|
}
|
|
}
|
|
|
|
fn push<T>(&mut self, addr: impl ToString, args: T)
|
|
where
|
|
T: IntoOscArgs,
|
|
{
|
|
self.inner.push_back(OscMessage::new(addr, args));
|
|
}
|
|
|
|
fn pop(&mut self) -> Option<OscMessage> {
|
|
self.inner.pop_front()
|
|
}
|
|
}
|