use std::io::{BufReader, Result, Read, BufRead, ErrorKind};
use std::fs::File;
use crate::platform::Midi;
use flume::Sender;
use lookit::It;
use std::os::unix::io::AsRawFd;
use smelling_salts::linux::{Device, Watcher, RawDevice};
struct Instrument {
listen: BufReader<File>,
sender: Sender<Midi>,
device: RawDevice,
}
pub(crate) fn connect(it: It) -> Option<Device<Midi>> {
let file = it.file_open_r().ok()?;
let device = file.as_raw_fd();
let listen = BufReader::new(file);
let constructor = |sender| Instrument { listen, sender, device };
let watcher = Watcher::new().input();
Some(super::platform().driver.device(constructor, device, callback, watcher))
}
unsafe fn callback(inst: &mut Instrument) -> Option<()> {
let should_discard = match midi(&mut inst.listen) {
Ok(midi) => inst.sender.send(midi).is_err(),
Err(e) => e.kind() != ErrorKind::WouldBlock,
};
if should_discard {
let _ = inst.sender.send(Midi([0xFF; 4]));
super::platform().driver.discard(inst.device);
drop(std::ptr::read(inst));
return None;
}
Some(())
}
fn midi(stream: &mut BufReader<File>) -> Result<Midi> {
let mut midi = Midi([0; 4]);
let mut cmd = 0;
while let Some(command) = stream.fill_buf()?.iter().next() {
if command & 0x80 != 0 {
cmd = *command;
break;
}
stream.consume(1);
};
let bytes = match cmd {
0x80..=0xCF => 3,
0xD0..=0xDF => 2,
0xE0..=0xEF => 3,
0xF0..=0xFF => 1,
_ => unreachable!(),
};
stream.read_exact(&mut midi.0[..bytes])?;
Ok(midi)
}