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
use crate::message::Channel;
use crate::parse_error::*;
use crate::util::*;
use alloc::vec::Vec;

/// Intended to act like Control Change messages, but targeted at an individual key.
/// For e.g. Drum sounds that have configurable attack/release/decay per key.
/// Used by [`UniversalRealTimeMsg::KeyBasedInstrumentControl`](crate::UniversalRealTimeMsg::KeyBasedInstrumentControl).
///
/// Defined in CA-023.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyBasedInstrumentControl {
    pub channel: Channel,
    /// The MIDI key number.
    pub key: u8,
    /// Any number of (control number, value) pairs.
    ///
    /// Any controller number may be used except Bank Select MSB/LSB (`0x00`, `0x20`),
    /// Data Entry MSB/LSB (`0x06`, `0x26`), RPN/NRPN messages (`0x60` – `0x65`),
    /// and Mode Change messages(`0x78`-`0x7F`).
    ///
    /// Disallowed values will be set to `0x01` (targeting the mod wheel, which probably has no meaning).
    pub control_values: Vec<(u8, u8)>,
}

impl KeyBasedInstrumentControl {
    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
        v.push(self.channel as u8);
        push_u7(self.key, v);
        for (cc, x) in self.control_values.iter().cloned() {
            if cc == 0x06 || cc == 0x26 || cc == 0x60 || cc == 0x65 || cc >= 0x78 {
                v.push(1);
            } else {
                v.push(cc);
            }
            push_u7(x, v);
        }
    }

    #[allow(dead_code)]
    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
        Err(ParseError::NotImplemented("KeyBasedInstrumentControl"))
    }
}

#[cfg(test)]
mod tests {
    use crate::*;
    use alloc::vec;

    #[test]
    fn serialize_controller_destination() {
        assert_eq!(
            MidiMsg::SystemExclusive {
                msg: SystemExclusiveMsg::UniversalRealTime {
                    device: DeviceID::AllCall,
                    msg: UniversalRealTimeMsg::KeyBasedInstrumentControl(
                        KeyBasedInstrumentControl {
                            channel: Channel::Ch2,
                            key: 0x60,
                            control_values: vec![
                                (0x06, 0x40), // CC not allowed, should become 0x01
                                (ControlNumber::Effects4Depth as u8, 0x20),
                            ]
                        }
                    ),
                }
            }
            .to_midi(),
            vec![
                0xF0, 0x7F, 0x7F, // Receiver device
                0x0A, 01, // Sysex IDs
                01, 0x60, 0x01, 0x40, 94, 0x20, 0xF7
            ]
        );
    }
}