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