m8_files/instruments/
midi.rs

1use crate::instruments::common::*;
2use crate::reader::*;
3use crate::version::*;
4use crate::writer::Writer;
5
6use arr_macro::arr;
7
8use super::dests;
9use super::params;
10use super::CommandPack;
11
12#[derive(PartialEq, Debug, Clone, Copy)]
13pub struct ControlChange {
14    /// CC number (target)
15    pub number: u8,
16
17    /// Value to be sent via MIDI CC message
18    pub value: u8,
19}
20
21impl ControlChange {
22    pub fn write(self, writer: &mut Writer) {
23        writer.write(self.number);
24        writer.write(self.value);
25    }
26
27    pub fn from_reader(reader: &mut Reader) -> M8Result<Self> {
28        Ok(Self {
29            number: reader.read(),
30            value: reader.read(),
31        })
32    }
33}
34
35#[rustfmt::skip] // Keep constats with important order vertical for maintenance
36const MIDI_OUT_COMMAND_NAMES : [&'static str; CommandPack::BASE_INSTRUMENT_COMMAND_COUNT - 2] = [
37    "VOL",
38    "PIT",
39    "MPG",
40    "MPB",
41    "ADD",
42    "CHD",
43    "CCA",
44    "CCB",
45    "CCC",
46    "CCD",
47    "CCE",
48    "CCF",
49    "CCG",
50    "CCH",
51    "CCI",
52    "CCJ",
53];
54
55#[rustfmt::skip] // Keep constats with important order vertical for maintenance
56const DESTINATIONS : [&'static str; 15] = [
57    dests::OFF,
58    params::CCA,
59    params::CCB,
60    params::CCC,
61    params::CCD,
62    "CCE",
63    "CCF",
64    "CCG",
65    "CCH",
66    "CCI",
67    "CCJ",
68    dests::MOD_AMT,
69    dests::MOD_RATE,
70    dests::MOD_BOTH,
71    dests::MOD_BINV,
72];
73
74#[rustfmt::skip] // Keep constats with important order vertical for maintenance
75const PORTS : [&'static str; 4] = [
76    "MIDI + USB",
77    "MIDI",
78    "USB",
79    "INTERNAL"
80];
81
82#[derive(PartialEq, Debug, Clone)]
83pub struct MIDIOut {
84    pub number: u8,
85    pub name: String,
86    pub transpose: bool,
87    pub table_tick: u8,
88
89    pub port: u8,
90    pub channel: u8,
91    pub bank_select: u8,
92    pub program_change: u8,
93    pub custom_cc: [ControlChange; 10],
94
95    pub mods: SynthParams,
96}
97
98impl MIDIOut {
99    const MOD_OFFSET: usize = 21;
100
101    pub fn command_name(&self, _ver: Version) -> &'static [&'static str] {
102        &MIDI_OUT_COMMAND_NAMES
103    }
104
105    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
106        &DESTINATIONS
107    }
108
109    pub fn human_readable_port(&self) -> &'static str {
110        PORTS[self.port as usize]
111    }
112
113    pub fn write(&self, _ver: Version, w: &mut Writer) {
114        w.write_string(&self.name, 12);
115        w.write(if self.transpose { 1 } else { 0 });
116        w.write(self.table_tick);
117        w.write(self.port);
118        w.write(self.channel);
119        w.write(self.bank_select);
120        w.write(self.program_change);
121
122        w.skip(3);
123
124        for cc in self.custom_cc {
125            cc.write(w);
126        }
127
128        self.mods.write_modes(w, MIDIOut::MOD_OFFSET)
129    }
130
131    pub fn from_reader(
132        _ver: Version,
133        reader: &mut Reader,
134        number: u8,
135        version: Version,
136    ) -> M8Result<Self> {
137        let name = reader.read_string(12);
138        let transpose = reader.read_bool();
139        let table_tick = reader.read();
140
141        let port = reader.read();
142        let channel = reader.read();
143        let bank_select = reader.read();
144        let program_change = reader.read();
145        reader.read_bytes(3); // discard
146        let custom_cc = arr![ControlChange::from_reader(reader)?; 10];
147        let mods = if version.at_least(3, 0) {
148            SynthParams::mod_only3(reader, MIDIOut::MOD_OFFSET)?
149        } else {
150            SynthParams::mod_only2(reader)?
151        };
152
153        Ok(MIDIOut {
154            number,
155            name,
156            transpose,
157            table_tick,
158
159            port,
160            channel,
161            bank_select,
162            program_change,
163            custom_cc,
164            mods,
165        })
166    }
167}