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

/// Allows for the selection of the destination of a channel pressure/poly key pressure message.
/// Used by [`UniversalRealTimeMsg`](crate::UniversalRealTimeMsg).
///
/// Defined in CA-022.
#[derive(Debug, Clone, PartialEq)]
pub struct ControllerDestination {
    pub channel: Channel,
    /// Any number of (ControlledParameter, range) pairs
    pub param_ranges: Vec<(ControlledParameter, u8)>,
}

impl ControllerDestination {
    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
        v.push(self.channel as u8);
        for (p, r) in self.param_ranges.iter() {
            v.push(*p as u8);
            push_u7(*r, v);
        }
    }

    #[allow(dead_code)]
    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
        Err(ParseError::Invalid(format!("TODO: Not implemented")))
    }
}

/// Allows for the selection of the destination of a control change message.
/// Used by [`UniversalRealTimeMsg::GlobalParameterControl`](crate::UniversalRealTimeMsg::GlobalParameterControl).
///
/// Defined in CA-022.
#[derive(Debug, Clone, PartialEq)]
pub struct ControlChangeControllerDestination {
    pub channel: Channel,
    /// A control number between `0x01` - `0x1F` or `0x40` - `0x5F`
    /// Values outside these ranges will be coerced
    pub control_number: u8,
    /// Any number of (ControlledParameter, range) pairs
    pub param_ranges: Vec<(ControlledParameter, u8)>,
}

impl ControlChangeControllerDestination {
    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
        v.push(self.channel as u8);
        if self.control_number < 0x40 {
            v.push(self.control_number.max(0x01).min(0x1F));
        } else {
            v.push(self.control_number.max(0x40).min(0x5F));
        }
        for (p, r) in self.param_ranges.iter() {
            v.push(*p as u8);
            push_u7(*r, v);
        }
    }

    #[allow(dead_code)]
    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
        Err(ParseError::Invalid(format!("TODO: Not implemented")))
    }
}
/// The parameters that can be controlled by [`ControllerDestination`] or
/// [`ControlChangeControllerDestination`].
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlledParameter {
    PitchControl = 0,
    FilterCutoffControl = 1,
    AmplitudeControl = 2,
    LFOPitchDepth = 3,
    LFOFilterDepth = 4,
    LFOAmplitudeDepth = 5,
}

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

    #[test]
    fn serialize_controller_destination() {
        assert_eq!(
            MidiMsg::SystemExclusive {
                msg: SystemExclusiveMsg::UniversalRealTime {
                    device: DeviceID::AllCall,
                    msg: UniversalRealTimeMsg::ControlChangeControllerDestination(
                        ControlChangeControllerDestination {
                            channel: Channel::Ch2,
                            control_number: 0x50,
                            param_ranges: vec![
                                (ControlledParameter::PitchControl, 0x42),
                                (ControlledParameter::FilterCutoffControl, 0x60)
                            ]
                        }
                    ),
                }
            }
            .to_midi(),
            vec![
                0xF0, 0x7F, 0x7F, // Receiver device
                09, 03, // Sysex IDs
                01, 0x50, 0, 0x42, 1, 0x60, 0xF7
            ]
        );
    }
}