midi-control 0.2.0

Communicate with MIDI controllers
Documentation
//
// (c) 2020 Hubert Figuière
//
// License: LGPL-3.0-or-later

//! MIDI control is a crate to allow creating MIDI message to control
//! MIDI messages.
//!
//! ## Example
//!
//! To simply prepare a Note On message for note 60 (Middle C) at
//! maximum velocity:
//!
//! ```rust
//! use midi_control::*;
//! let message = midi_control::note_on(Channel::Ch1, 60, 127);
//! let buffer: Vec<u8> = message.into();
//! /* send the buffer to your MIDI device */
//! ```
//!
//! If you have the `transport` feature enabled, you can use midir directly
//! to send the same message.
//!
//! ```rust
//! use midi_control::*;
//! use midi_control::transport::MidiMessageSend;  // to use the trait
//! # use midir;
//! # let out = midir::MidiOutput::new("MIDITest").unwrap();
//! # let port = &out.ports()[0];
//! # let mut midi_out: midir::MidiOutputConnection = out.connect(port, "MIDI device").unwrap();
//! let message = midi_control::note_on(Channel::Ch1, 60, 127);
//! // midi_out is a midir::MidiOutputConnection
//! midi_out.send_message(message);
//! ```

pub mod consts;
pub mod message;
pub mod note;
pub mod sysex;
#[cfg(feature = "transport")]
pub mod transport;
pub mod vendor;

pub use crate::message::{Channel, ControlEvent, KeyEvent, MidiMessage, SysExEvent};
pub use crate::note::MidiNote;
#[cfg(feature = "transport")]
pub use crate::transport::MidiMessageSend;

/// Create a Note On message
pub fn note_on(channel: Channel, note: MidiNote, velocity: u8) -> MidiMessage {
    MidiMessage::NoteOn(
        channel,
        KeyEvent {
            key: note,
            value: velocity,
        },
    )
}

/// Create a Note Off message
pub fn note_off(channel: Channel, note: MidiNote, velocity: u8) -> MidiMessage {
    MidiMessage::NoteOff(
        channel,
        KeyEvent {
            key: note,
            value: velocity,
        },
    )
}

/// Create a Poly Key Pressure message
pub fn poly_key_pressure(channel: Channel, note: MidiNote, pressure: u8) -> MidiMessage {
    MidiMessage::PolyKeyPressure(
        channel,
        KeyEvent {
            key: note,
            value: pressure,
        },
    )
}

/// Create a Control Change message
pub fn control_change(channel: Channel, control: u8, value: u8) -> MidiMessage {
    MidiMessage::ControlChange(channel, ControlEvent { control, value })
}

/// Create a Program Change message
pub fn program_change(channel: Channel, program: u8) -> MidiMessage {
    MidiMessage::ProgramChange(channel, program)
}

/// Create a Channel Pressure message
pub fn channel_pressure(channel: Channel, pressure: u8) -> MidiMessage {
    MidiMessage::ProgramChange(channel, pressure)
}

/// Create a Pitch Bend message
///
/// # Error
/// Will return an `MidiMessage::Invalid` message if `change` is more than 14bits
pub fn pitch_bend(channel: Channel, change: u16) -> MidiMessage {
    if change > 0x3fff {
        return MidiMessage::Invalid;
    }
    let lsb: u8 = (change & 0b0111_1111) as u8;
    let msb: u8 = ((change >> 7) & 0b0111_1111) as u8;
    MidiMessage::PitchBend(channel, lsb, msb)
}

pub fn non_rt_usysex(device: u8, subid: [u8; 2], data: &[u8]) -> MidiMessage {
    MidiMessage::SysEx(SysExEvent::new_non_realtime(device, subid, data))
}

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

    #[test]
    pub fn test_simple_api() {
        let msg = MidiMessage::NoteOn(Channel::Ch1, KeyEvent { key: 59, value: 88 });
        assert_eq!(note_on(Channel::Ch1, 59, 88), msg);

        let msg = MidiMessage::NoteOff(Channel::Ch1, KeyEvent { key: 60, value: 0 });
        assert_eq!(note_off(Channel::Ch1, 60, 0), msg);

        let msg = MidiMessage::ControlChange(
            Channel::Ch1,
            ControlEvent {
                control: 114,
                value: 65,
            },
        );
        assert_eq!(control_change(Channel::Ch1, 114, 65), msg);

        let msg = MidiMessage::PitchBend(Channel::Ch1, 0, 76);
        assert_eq!(pitch_bend(Channel::Ch1, 0x2600), msg);

        // pitch bend value is too large
        assert_eq!(pitch_bend(Channel::Ch1, 0x8000), MidiMessage::Invalid);

        let msg = MidiMessage::SysEx(SysExEvent::new_non_realtime(
            consts::usysex::ALL_CALL,
            [0x06, 0x01],
            &[0xf7],
        ));
        assert_eq!(
            non_rt_usysex(consts::usysex::ALL_CALL, [0x06, 0x01], &[0xf7]),
            msg
        );
    }
}