Skip to main content

midi_msg/system_exclusive/
controller_destination.rs

1use crate::message::Channel;
2use crate::parse_error::*;
3use crate::util::*;
4use alloc::vec::Vec;
5
6/// Allows for the selection of the destination of a channel pressure/poly key pressure message.
7/// Used by [`UniversalRealTimeMsg`](crate::UniversalRealTimeMsg).
8///
9/// Defined in CA-022.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct ControllerDestination {
12    pub channel: Channel,
13    /// Any number of (ControlledParameter, range) pairs
14    pub param_ranges: Vec<(ControlledParameter, u8)>,
15}
16
17impl ControllerDestination {
18    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
19        v.push(self.channel as u8);
20        for (p, r) in self.param_ranges.iter() {
21            v.push(*p as u8);
22            push_u7(*r, v);
23        }
24    }
25
26    #[allow(dead_code)]
27    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
28        Err(ParseError::NotImplemented("ControllerDestination"))
29    }
30}
31
32/// Allows for the selection of the destination of a control change message.
33/// Used by [`UniversalRealTimeMsg::GlobalParameterControl`](crate::UniversalRealTimeMsg::GlobalParameterControl).
34///
35/// Defined in CA-022.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ControlChangeControllerDestination {
38    pub channel: Channel,
39    /// A control number between `0x01` - `0x1F` or `0x40` - `0x5F`
40    /// Values outside these ranges will be coerced
41    pub control_number: u8,
42    /// Any number of (ControlledParameter, range) pairs
43    pub param_ranges: Vec<(ControlledParameter, u8)>,
44}
45
46impl ControlChangeControllerDestination {
47    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
48        v.push(self.channel as u8);
49        if self.control_number < 0x40 {
50            v.push(self.control_number.clamp(0x01, 0x1F));
51        } else {
52            v.push(self.control_number.clamp(0x40, 0x5F));
53        }
54        for (p, r) in self.param_ranges.iter() {
55            v.push(*p as u8);
56            push_u7(*r, v);
57        }
58    }
59
60    #[allow(dead_code)]
61    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
62        Err(ParseError::NotImplemented(
63            "ControlChangeControllerDestination",
64        ))
65    }
66}
67/// The parameters that can be controlled by [`ControllerDestination`] or
68/// [`ControlChangeControllerDestination`].
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum ControlledParameter {
71    PitchControl = 0,
72    FilterCutoffControl = 1,
73    AmplitudeControl = 2,
74    LFOPitchDepth = 3,
75    LFOFilterDepth = 4,
76    LFOAmplitudeDepth = 5,
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::*;
82    use alloc::vec;
83
84    #[test]
85    fn serialize_controller_destination() {
86        assert_eq!(
87            MidiMsg::SystemExclusive {
88                msg: SystemExclusiveMsg::UniversalRealTime {
89                    device: DeviceID::AllCall,
90                    msg: UniversalRealTimeMsg::ControlChangeControllerDestination(
91                        ControlChangeControllerDestination {
92                            channel: Channel::Ch2,
93                            control_number: 0x50,
94                            param_ranges: vec![
95                                (ControlledParameter::PitchControl, 0x42),
96                                (ControlledParameter::FilterCutoffControl, 0x60)
97                            ]
98                        }
99                    ),
100                }
101            }
102            .to_midi(),
103            vec![
104                0xF0, 0x7F, 0x7F, // Receiver device
105                0x9, 0x3, // Sysex IDs
106                0x1, 0x50, 0x0, 0x42, 0x1, 0x60, 0xF7
107            ]
108        );
109    }
110}