use super::*;
pub trait DeviceSpec {
const BOUNDING_BOX_WIDTH: u32;
const BOUNDING_BOX_HEIGHT: u32;
const COLOR_PRECISION: u16;
type Input: crate::InputDevice;
type Output: crate::OutputDevice;
fn is_valid(x: u32, y: u32) -> bool;
fn flush(
canvas: &mut crate::DeviceCanvas<Self>,
changes: &[(u32, u32, (u8, u8, u8))],
) -> Result<(), crate::MidiError>
where
Self: Sized;
fn convert_message(msg: <Self::Input as crate::InputDevice>::Message) -> Option<CanvasMessage>;
fn setup(output: &mut Self::Output) -> Result<(), crate::MidiError> {
let _ = output;
Ok(())
}
}
pub struct DeviceCanvasPoller {
receiver: std::sync::mpsc::Receiver<CanvasMessage>,
}
impl crate::MsgPollingWrapper for DeviceCanvasPoller {
type Message = CanvasMessage;
fn receiver(&self) -> &std::sync::mpsc::Receiver<Self::Message> {
&self.receiver
}
}
pub struct DeviceCanvas<Spec: DeviceSpec> {
_input: crate::InputDeviceHandler,
pub(crate) output: Spec::Output,
curr_state: crate::util::Array2d<crate::Color>,
new_state: crate::util::Array2d<crate::Color>,
num_sent_changes: usize,
}
impl<Spec: DeviceSpec> DeviceCanvas<Spec> {
pub fn guess(
mut callback: impl FnMut(CanvasMessage) + Send + 'static,
) -> Result<Self, crate::MidiError> {
use crate::midi_io::{InputDevice, OutputDevice};
let _input = Spec::Input::guess(move |msg| {
if let Some(msg) = Spec::convert_message(msg) {
(callback)(msg);
}
})?;
let mut output = Spec::Output::guess()?;
Spec::setup(&mut output)?;
let curr_state =
crate::util::Array2d::new(Spec::BOUNDING_BOX_WIDTH, Spec::BOUNDING_BOX_HEIGHT);
let new_state =
crate::util::Array2d::new(Spec::BOUNDING_BOX_WIDTH, Spec::BOUNDING_BOX_HEIGHT);
Ok(Self {
_input,
output,
curr_state,
new_state,
num_sent_changes: 0,
})
}
pub fn guess_polling() -> Result<(Self, DeviceCanvasPoller), crate::MidiError> {
let (sender, receiver) = std::sync::mpsc::channel();
let canvas = Self::guess(move |msg| {
sender
.send(msg)
.expect("Message receiver has hung up (this shouldn't happen)")
})?;
let poller = DeviceCanvasPoller { receiver };
Ok((canvas, poller))
}
}
#[doc(hidden)] pub trait DeviceCanvasTrait {
type Spec: DeviceSpec;
}
impl<S: DeviceSpec> DeviceCanvasTrait for DeviceCanvas<S> {
type Spec = S;
}
impl_traits_for_canvas!(DeviceCanvas[S: DeviceSpec]);
impl<Spec: DeviceSpec> crate::Canvas for DeviceCanvas<Spec> {
fn bounding_box(&self) -> (u32, u32) {
(Spec::BOUNDING_BOX_WIDTH, Spec::BOUNDING_BOX_HEIGHT)
}
fn lowest_visible_brightness(&self) -> f32 {
1.0 / Spec::COLOR_PRECISION as f32
}
fn low_level_get(&self, x: u32, y: u32) -> Option<&Color> {
if !Spec::is_valid(x, y) {
return None;
}
self.curr_state.get(x, y)
}
fn low_level_get_pending_mut(&mut self, x: u32, y: u32) -> Option<&mut Color> {
if !Spec::is_valid(x, y) {
return None;
}
self.new_state.get_mut(x, y)
}
fn low_level_get_pending(&self, x: u32, y: u32) -> Option<&Color> {
if !Spec::is_valid(x, y) {
return None;
}
self.new_state.get(x, y)
}
fn flush(&mut self) -> Result<(), crate::MidiError> {
let mut changes: Vec<(u32, u32, (u8, u8, u8))> = Vec::with_capacity(9 * 9);
for pad in self.iter() {
let old = self.get(pad).unwrap();
let new = self.get_pending(pad).unwrap();
let old = old.quantize(Spec::COLOR_PRECISION as u8);
let new = new.quantize(Spec::COLOR_PRECISION as u8);
if new != old {
changes.push((pad.x as u32, pad.y as u32, new));
}
}
if !changes.is_empty() {
use crate::midi_io::OutputDevice;
self.num_sent_changes += changes.len();
if self.num_sent_changes / 1000 != (self.num_sent_changes - changes.len()) / 1000 {
println!(
"{}: we're at {} total transmitted changes now",
Spec::Output::MIDI_DEVICE_KEYWORD,
self.num_sent_changes,
);
}
Spec::flush(self, &changes)?;
}
self.curr_state = self.new_state.clone();
Ok(())
}
}