launchkey_sdk/midi/
input.rs

1use crate::midi::to_hex::ToHexString;
2use midir::{Ignore, MidiInput, MidiInputConnection};
3use std::io;
4use std::io::Write;
5use std::sync::mpsc;
6use wmidi::{ControlFunction, MidiMessage, U7};
7
8pub fn get_named_midi_ports(midi_in: &MidiInput) -> Vec<(usize, String)> {
9    midi_in
10        .ports()
11        .iter()
12        .enumerate()
13        .filter_map(|(i, port)| midi_in.port_name(port).ok().map(|name| (i, name)))
14        .collect()
15}
16
17pub fn select_port(ports: &[(usize, String)]) -> Option<usize> {
18    println!("Select a MIDI input port:");
19    for (i, name) in ports.iter().enumerate() {
20        println!("{}: {}", i, name.1);
21    }
22
23    print!("Enter port index: ");
24    io::stdout().flush().unwrap();
25
26    let mut input = String::new();
27    io::stdin().read_line(&mut input).unwrap();
28
29    input.trim().parse::<usize>().ok().and_then(
30        |idx| {
31            if idx < ports.len() {
32                Some(idx)
33            } else {
34                None
35            }
36        },
37    )
38}
39
40pub fn connect_to_port(
41    midi_in: MidiInput,
42    port_index: usize,
43    tx: mpsc::Sender<Vec<u8>>,
44) -> Result<MidiInputConnection<()>, String> {
45    let ports = midi_in.ports();
46    let port = ports
47        .get(port_index)
48        .ok_or_else(|| "Invalid port index".to_string())?;
49
50    // Connect to the MIDI port
51    let conn_in = midi_in
52        .connect(
53            port,
54            "midi-input",
55            move |_timestamp, message, _context| {
56                tx.send(message.to_vec()).unwrap();
57            },
58            (),
59        )
60        .map_err(|_| "Failed to connect to MIDI port".to_string())?;
61
62    Ok(conn_in)
63}
64
65pub fn connect_all_midi_ports(
66    ports: &[(usize, String)],
67    tx: mpsc::Sender<Vec<u8>>,
68) -> Vec<MidiInputConnection<()>> {
69    ports
70        .iter()
71        .filter_map(|(i, name)| {
72            let mut midi_in = MidiInput::new(&format!("{} Listener", name)).ok()?;
73            midi_in.ignore(Ignore::None);
74            connect_to_port(midi_in, *i, tx.clone()).ok()
75        })
76        .collect()
77}
78
79pub fn log_midi_message(message: MidiMessage) {
80    match message {
81        MidiMessage::NoteOff(channel, note, _) => println!("Note Off: {:?} {}", channel, note),
82        MidiMessage::NoteOn(channel, note, velocity) => {
83            println!("Note On: {:?} {} (Velocity: {:?})", channel, note, velocity)
84        }
85        MidiMessage::PolyphonicKeyPressure(_, note, velocity) => println!(
86            "Polyphonic Key Pressure: {} (Velocity: {:?})",
87            note, velocity
88        ),
89        MidiMessage::ControlChange(channel, control_function, value) => println!(
90            "Control Change: {:?} Controller {:?} Value {:?}",
91            channel, control_function, value
92        ),
93        MidiMessage::ProgramChange(_, program_number) => {
94            println!("Program Change: Program {:?}", program_number)
95        }
96        MidiMessage::ChannelPressure(channel, velocity) => {
97            println!("Channel Pressure: {:?} (Velocity: {:?})", channel, velocity)
98        }
99        MidiMessage::PitchBendChange(_, pitch_bend) => println!("Pitch Bend: {:?}", pitch_bend),
100        MidiMessage::SysEx(data) => {
101            println!("SysEx Message: {}", data.to_hex_string());
102        }
103        MidiMessage::Start => println!("Start"),
104        MidiMessage::Continue => println!("Continue"),
105        MidiMessage::Stop => println!("Stop"),
106        _ => {}
107    }
108}
109
110pub trait ControlFunctionExt {
111    fn equals_u8(&self, value: u8) -> bool;
112}
113
114impl ControlFunctionExt for ControlFunction {
115    fn equals_u8(&self, value: u8) -> bool {
116        self.0 == U7::from_u8_lossy(value)
117    }
118}