use std::{collections::BTreeSet, sync::Arc};
use gled_midi::{
HostEvent, MidiRuntimeSnapshot, MidiSource, SnapshotCache, live_values, native::NativeHost,
};
use gled_protocol::midi::TestCommand;
use kanal::Receiver;
use super::{ClientId, Engine, LearnLog, MidiLog};
use crate::action::UiAction;
type SnapshotKey = (Option<usize>, u64);
#[derive(Debug)]
pub(super) struct MidiTools {
host: NativeHost,
events: Receiver<HostEvent>,
snapshots: SnapshotCache<SnapshotKey>,
output_ports: Vec<String>,
input_ports: Vec<String>,
watchers: BTreeSet<ClientId>,
learners: BTreeSet<ClientId>,
pub(super) monitor_log: Arc<MidiLog>,
pub(super) learn_log: Arc<LearnLog>,
}
impl MidiTools {
pub(super) fn new(host: NativeHost, events: Receiver<HostEvent>) -> Self {
Self {
host,
events,
snapshots: SnapshotCache::default(),
output_ports: Vec::new(),
input_ports: Vec::new(),
watchers: BTreeSet::new(),
learners: BTreeSet::new(),
monitor_log: Arc::default(),
learn_log: Arc::default(),
}
}
pub(super) fn watch(&mut self, id: ClientId, watch: bool) {
set(&mut self.watchers, id, watch);
self.update_gates();
}
pub(super) fn learning(&mut self, id: ClientId, learning: bool) {
set(&mut self.learners, id, learning);
self.update_gates();
}
pub(super) fn disconnect(&mut self, id: ClientId) {
self.watchers.remove(&id);
self.learners.remove(&id);
self.update_gates();
}
fn update_gates(&self) {
self.host
.gates()
.set(!self.watchers.is_empty(), !self.learners.is_empty());
}
pub(super) fn test(&self, command: TestCommand) {
self.host.test(command);
}
pub(super) fn output_ports(&self) -> &[String] {
&self.output_ports
}
pub(super) fn input_ports(&self) -> &[String] {
&self.input_ports
}
pub(super) fn tick(&mut self) {
while let Ok(Some(event)) = self.events.try_recv() {
match event {
HostEvent::Actions(actions) => actions.into_iter().for_each(UiAction::enqueue),
HostEvent::Monitor(event) => Arc::make_mut(&mut self.monitor_log).push(event),
HostEvent::Learn(message) => Arc::make_mut(&mut self.learn_log).push(message),
HostEvent::Error(message) => UiAction::Error(message).enqueue(),
HostEvent::InputPorts(ports) => self.input_ports = ports,
HostEvent::OutputPorts(ports) => {
if ports.is_empty() != self.output_ports.is_empty() {
UiAction::MidiOutputActive(!ports.is_empty()).enqueue();
}
self.output_ports = ports;
}
}
}
}
}
impl Engine {
pub(super) fn publish_midi(&mut self) {
let project = self.project.as_deref();
let key = (
self.project
.as_ref()
.map(|project| Arc::as_ptr(project) as usize),
self.collections.generation(),
);
let collections = &self.collections;
let snapshot = self
.midi
.snapshots
.get(key, || MidiRuntimeSnapshot::build(project, collections));
let beat_progression = self.timing.beat_progression();
let live = live_values::selected_scene_live_values(
project,
self.selected_scene_instance,
collections,
&self.sound_data,
beat_progression,
);
let source = MidiSource {
project: self.project.clone(),
selected_scene_instance: self.selected_scene_instance,
selected_scene_effect: self
.effect_selection
.effect_in(self.selected_scene_instance),
selected_scene_live: Arc::new(live),
blackout: self.blackout || self.blackout_hold,
beats_per_minute: self.timing.beats_per_minute(),
beat_progression,
midi_modes: self.controllers.modes().clone(),
};
self.midi.host.publish(source, snapshot);
}
}
fn set(clients: &mut BTreeSet<ClientId>, id: ClientId, member: bool) {
if member {
clients.insert(id);
} else {
clients.remove(&id);
}
}