extern crate midi_msg;
extern crate midir;
use midi_msg::*;
use midir::{Ignore, MidiInput};
use std::error::Error;
use std::io::{stdin, stdout, Write};
fn main() {
match run() {
Ok(_) => (),
Err(err) => println!("Error: {}", err),
}
}
struct TimeCodeQuarterFrameBuffer {
buffer: [Option<TimeCode>; 8],
}
impl TimeCodeQuarterFrameBuffer {
fn new() -> Self {
Self { buffer: [None; 8] }
}
fn add(&mut self, message: MidiMsg) {
match message {
MidiMsg::SystemCommon { msg } => {
let (index, tc) = match msg {
SystemCommonMsg::TimeCodeQuarterFrame1(tc) => (0, tc),
SystemCommonMsg::TimeCodeQuarterFrame2(tc) => (1, tc),
SystemCommonMsg::TimeCodeQuarterFrame3(tc) => (2, tc),
SystemCommonMsg::TimeCodeQuarterFrame4(tc) => (3, tc),
SystemCommonMsg::TimeCodeQuarterFrame5(tc) => (4, tc),
SystemCommonMsg::TimeCodeQuarterFrame6(tc) => (5, tc),
SystemCommonMsg::TimeCodeQuarterFrame7(tc) => (6, tc),
SystemCommonMsg::TimeCodeQuarterFrame8(tc) => (7, tc),
_ => return (),
};
if self.buffer[index].is_none() {
self.buffer[index] = Some(tc);
}
}
_ => (),
}
}
fn is_filled(&self) -> bool {
self.buffer.iter().all(|b| b.is_some())
}
fn construct_timecode(&mut self) -> Option<TimeCode> {
if !self.is_filled() {
return None;
}
let frames: u8 = self.buffer[0].unwrap().frames ^ self.buffer[1].unwrap().frames;
let seconds: u8 = self.buffer[2].unwrap().seconds ^ self.buffer[3].unwrap().seconds;
let minutes: u8 = self.buffer[4].unwrap().minutes ^ self.buffer[5].unwrap().minutes;
let hours: u8 = self.buffer[6].unwrap().hours ^ self.buffer[7].unwrap().hours;
let code_type = self.buffer[7].unwrap().code_type;
self.buffer = [None; 8];
Some(TimeCode {
frames,
seconds,
minutes,
hours,
code_type,
})
}
}
fn run() -> Result<(), Box<dyn Error>> {
let mut input = String::new();
let mut midi_in = MidiInput::new("midir reading input")?;
midi_in.ignore(Ignore::None);
let in_ports = midi_in.ports();
let in_port = match in_ports.len() {
0 => return Err("no input port found".into()),
1 => {
println!(
"Choosing the only available input port: {}",
midi_in.port_name(&in_ports[0]).unwrap()
);
&in_ports[0]
}
_ => {
println!("\nAvailable input ports:");
for (i, p) in in_ports.iter().enumerate() {
println!("{}: {}", i, midi_in.port_name(p).unwrap());
}
print!("Please select input port: ");
stdout().flush()?;
let mut input = String::new();
stdin().read_line(&mut input)?;
in_ports
.get(input.trim().parse::<usize>()?)
.ok_or("invalid input port selected")?
}
};
println!("\nOpening connection");
let in_port_name = midi_in.port_name(in_port)?;
let mut quarter_frame_buffer = TimeCodeQuarterFrameBuffer::new();
let _conn_in = midi_in.connect(
in_port,
"midir-read-input",
move |_stamp, message, _| {
let (parsed_message, _len) = MidiMsg::from_midi(&message).expect("Not an error");
quarter_frame_buffer.add(parsed_message);
let maybe_timecode = quarter_frame_buffer.construct_timecode();
if let Some(tc) = maybe_timecode {
println!(
"{:0<2}:{:0<2}:{:0<2}.{} ({:?})",
tc.hours, tc.minutes, tc.seconds, tc.frames, tc.code_type
)
}
},
(),
)?;
println!(
"Connection open, reading input from '{}' (press enter to exit) ...",
in_port_name
);
input.clear();
stdin().read_line(&mut input)?;
println!("Closing connection");
Ok(())
}