Skip to main content

midi_msg/system_exclusive/
key_based_instrument_control.rs

1use crate::message::Channel;
2use crate::parse_error::*;
3use crate::util::*;
4use alloc::vec::Vec;
5
6/// Intended to act like Control Change messages, but targeted at an individual key.
7/// For e.g. Drum sounds that have configurable attack/release/decay per key.
8/// Used by [`UniversalRealTimeMsg::KeyBasedInstrumentControl`](crate::UniversalRealTimeMsg::KeyBasedInstrumentControl).
9///
10/// Defined in CA-023.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct KeyBasedInstrumentControl {
13    pub channel: Channel,
14    /// The MIDI key number.
15    pub key: u8,
16    /// Any number of (control number, value) pairs.
17    ///
18    /// Any controller number may be used except Bank Select MSB/LSB (`0x00`, `0x20`),
19    /// Data Entry MSB/LSB (`0x06`, `0x26`), RPN/NRPN messages (`0x60` – `0x65`),
20    /// and Mode Change messages(`0x78`-`0x7F`).
21    ///
22    /// Disallowed values will be set to `0x01` (targeting the mod wheel, which probably has no meaning).
23    pub control_values: Vec<(u8, u8)>,
24}
25
26impl KeyBasedInstrumentControl {
27    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
28        v.push(self.channel as u8);
29        push_u7(self.key, v);
30        for (cc, x) in self.control_values.iter().cloned() {
31            if cc == 0x06 || cc == 0x26 || cc == 0x60 || cc == 0x65 || cc >= 0x78 {
32                v.push(1);
33            } else {
34                v.push(cc);
35            }
36            push_u7(x, v);
37        }
38    }
39
40    #[allow(dead_code)]
41    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
42        Err(ParseError::NotImplemented("KeyBasedInstrumentControl"))
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use crate::*;
49    use alloc::vec;
50
51    #[test]
52    fn serialize_controller_destination() {
53        assert_eq!(
54            MidiMsg::SystemExclusive {
55                msg: SystemExclusiveMsg::UniversalRealTime {
56                    device: DeviceID::AllCall,
57                    msg: UniversalRealTimeMsg::KeyBasedInstrumentControl(
58                        KeyBasedInstrumentControl {
59                            channel: Channel::Ch2,
60                            key: 0x60,
61                            control_values: vec![
62                                (0x06, 0x40), // CC not allowed, should become 0x01
63                                (ControlNumber::Effects4Depth as u8, 0x20),
64                            ]
65                        }
66                    ),
67                }
68            }
69            .to_midi(),
70            vec![
71                0xF0, 0x7F, 0x7F, // Receiver device
72                0xA, 0x1, // Sysex IDs
73                0x1, 0x60, 0x01, 0x40, 94, 0x20, 0xF7
74            ]
75        );
76    }
77}