1use std::{
4 error::Error,
5 io::{Write, stdin, stdout},
6 sync::{mpsc, mpsc::Sender},
7 thread,
8 thread::JoinHandle,
9};
10
11use midir::{Ignore, MidiInput, MidiInputPort};
12
13use crate::message::{MidiMessage, MidiMessageStatus};
14
15pub struct MidiInputStream {
20 tx: Sender<MidiMessage>,
21 filters: Vec<MidiMessageStatus>,
22}
23
24impl MidiInputStream {
25 pub fn new(filters: Vec<MidiMessageStatus>, message_handler: fn(MidiMessage) -> ()) -> Self {
32 let (tx, rx) = mpsc::channel::<MidiMessage>();
33 thread::spawn(move || {
34 loop {
35 message_handler(rx.recv().unwrap());
36 }
37 });
38 MidiInputStream { tx, filters }
39 }
40}
41
42impl MidiInputStream {
43 pub fn init(self) -> JoinHandle<()> {
48 println!("Creating new MIDI input stream");
49 let mut midi_in = MidiInput::new("Reading MIDI input").unwrap();
50 midi_in.ignore(Ignore::None);
51
52 let in_ports = midi_in.ports();
53 let in_port: Option<MidiInputPort> = match in_ports.len() {
54 0 => None,
55 1 => {
56 println!(
57 "Choosing the only available MIDI input port:\n{}",
58 midi_in.port_name(&in_ports[0]).unwrap()
59 );
60 Some(in_ports[0].clone())
61 }
62 _ => {
63 println!("\nAvailable MIDI input ports:");
64 for (idx, port) in in_ports.iter().enumerate() {
65 println!("{}: {}", idx, midi_in.port_name(port).unwrap());
66 }
67 println!("\nPlease select input port: ");
68 stdout().flush().unwrap();
69
70 let mut input = String::new();
71 stdin().read_line(&mut input).unwrap();
72 Some(
73 in_ports
74 .get(input.trim().parse::<usize>().unwrap())
75 .ok_or("Invalid input port selected")
76 .unwrap()
77 .clone(),
78 )
79 }
80 };
81 thread::spawn(move || match self.create_midi_input_stream(midi_in, in_port.unwrap()) {
82 Ok(_) => (),
83 Err(err) => println!("Error : {err}"),
84 })
85 }
86
87 fn create_midi_input_stream(
88 self,
89 midi_in: MidiInput,
90 in_port: MidiInputPort,
91 ) -> std::result::Result<(), Box<dyn Error>> {
92 println!("\nOpening MIDI input stream for port");
93 let in_port_name = midi_in.port_name(&in_port)?;
94 let _connection = midi_in.connect(
95 &in_port,
96 "midir-read-input",
97 move |_stamp, message_bytes, _| {
98 let message = MidiMessage::from(message_bytes);
99 if self.is_passed_through_filters(&message) {
100 self.tx.send(message).unwrap();
101 } else {
102 }
104 },
105 (),
106 )?;
107
108 println!("Connection open, reading MIDI input from '{in_port_name}' (press enter to exit) ...");
109
110 let mut input = String::new();
111 input.clear();
112 stdin().read_line(&mut input).unwrap();
113 println!("Closing connection");
114
115 Ok(())
116 }
117
118 fn is_passed_through_filters(&self, message: &MidiMessage) -> bool {
119 if self.filters.is_empty() {
120 true
121 } else {
122 self.filters.contains(&message.get_status())
123 }
124 }
125}