1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::enums::input_group::ChannelKind;
use crate::enums::message_filter::MessageFilter;
use crate::errors::io::ChannelCreationError;
use crate::io::channel::Channel;
use crate::io::input_data::MidiInputData;
use bon::bon;
use midi_msg::{MidiMsg, ReceiverContext};
use midir::{MidiInput, MidiInputConnection, MidiInputPort};
pub struct InputChannel {
midi_input: MidiInput,
input_port: MidiInputPort,
}
#[bon]
impl InputChannel {
#[builder]
pub fn new(
port: Option<usize>,
msg_to_ignore: Option<MessageFilter>,
) -> Result<Self, ChannelCreationError> {
let mut midi_in = Self::get_midi()?;
midi_in.ignore(msg_to_ignore.unwrap_or(MessageFilter::None).into());
let in_ports = midi_in.ports();
let chosen_port = port.unwrap_or(2);
Ok(Self {
midi_input: midi_in,
input_port: in_ports
.get(chosen_port)
.ok_or(ChannelCreationError::PortOutOfRange(chosen_port))?
.clone(),
})
}
/// Very biased listener method. It will try to decode the MIDI messages to a high level representation
/// and after that it will call the given closure
/// ```Rust
///let midi_in = ramidier::io::input::InputChannel::builder().build()?;
///let _conn_in = midi_in.listen(
/// Some("midir-input"),
/// move |stamp, received_input, data| listener_logic(&mut midi_out, stamp, &received_input, data),
/// MyDataStruct{}, // could also be () it there is no need for data
/// PadsAndKnobsChannel, // could also be KeyboardChannel depending on your needs
/// )?;
/// ```
/// # Errors
///
/// Will return `ChannelCreationError` if there are low-level issues communicating with the device
pub fn listen<F, T: Send, C>(
self,
port_name: Option<&str>,
mut input_handler_callback: F,
data: T,
_channel_type: C, // type inference to avoid awkward turbofish syntax on caller
) -> Result<MidiInputConnection<T>, ChannelCreationError>
where
C: ChannelKind + Send + 'static,
F: FnMut(u64, MidiInputData<C::Group>, &mut T) + Send + 'static,
{
let mut ctx = ReceiverContext::new();
let wrapper = move |timestamp: u64, midi_bytes: &[u8], user_data: &mut T| {
if let Some(input) = MidiMsg::from_midi_with_context(midi_bytes, &mut ctx)
.ok()
.and_then(|(msg, _)| C::decode(&msg))
{
input_handler_callback(timestamp, input, user_data);
}
};
self.listen_raw(port_name, wrapper, data)
}
/// Listener method that will try to decode the received bytes to the MIDI messages
/// and after that it will call the given closure
/// ```Rust
///let midi_in = ramidier::io::input::InputChannel::builder().build()?;
///let _conn_in = midi_in.listen_midi_msg(
/// Some("midi-input"),
/// move |stamp, midi_msg, data| listener_logic(&mut midi_out, stamp, &midi_mgs, data),
/// MyDataStruct{}, // could also be () it there is no need for data
/// )?;
/// ```
/// # Errors
///
/// Will return `ChannelCreationError` if there are low-level issues communicating with the device
pub fn listen_midi_msg<F, T: Send>(
self,
port_name: Option<&str>,
mut input_handler_callback: F,
data: T,
) -> Result<MidiInputConnection<T>, ChannelCreationError>
where
F: FnMut(u64, MidiMsg, &mut T) + Send + 'static,
{
let mut ctx = ReceiverContext::new();
let wrapper = move |timestamp: u64, midi_bytes: &[u8], user_data: &mut T| {
if let Ok((msg, _)) = MidiMsg::from_midi_with_context(midi_bytes, &mut ctx) {
input_handler_callback(timestamp, msg, user_data);
}
};
self.listen_raw(port_name, wrapper, data)
}
/// Listener method that will will call the given closure every time it receives a midi message. It does not decode the raw bytes.
/// ```Rust
///let midi_in = ramidier::io::input::InputChannel::builder().build()?;
///let _conn_in = midi_in.listen_raw(
/// Some("midi-input"),
/// move |stamp, midi_bytes, data| listener_logic(&mut midi_out, stamp, &midi_bytes, data),
/// MyDataStruct{}, // could also be () it there is no need for data
/// )?;
/// ```
/// # Errors
///
/// Will return `ChannelCreationError` if there are low-level issues communicating with the device
pub fn listen_raw<F, T: Send>(
self,
port_name: Option<&str>,
input_handler_callback: F,
data: T,
) -> Result<MidiInputConnection<T>, ChannelCreationError>
where
F: FnMut(u64, &[u8], &mut T) + Send + 'static,
{
self.midi_input
.connect(
&self.input_port,
port_name.unwrap_or("akai-midir-read-input"),
input_handler_callback,
data,
)
.map_err(|e| ChannelCreationError::EstablishingInputConnection { source: e })
}
}
impl Channel for InputChannel {
#[allow(refining_impl_trait)]
fn get_midi() -> Result<MidiInput, ChannelCreationError> {
MidiInput::new("akai midir reading input")
.map_err(|e| ChannelCreationError::InitializingChannel { source: e })
}
}