m8_file_parser/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 constants 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 constants 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 constants with important order vertical for maintenance
75pub(crate) const 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 port_names(_ver: Version) -> &'static [&'static str] {
102        &PORTS
103    }
104
105    pub fn command_name(&self, _ver: Version) -> &'static [&'static str] {
106        &MIDI_OUT_COMMAND_NAMES
107    }
108
109    pub fn destination_names(&self, _ver: Version) -> &'static [&'static str] {
110        &DESTINATIONS
111    }
112
113    pub fn human_readable_port(&self) -> &'static str {
114        PORTS[self.port as usize]
115    }
116
117    pub fn write(&self, _ver: Version, w: &mut Writer) {
118        w.write_string(&self.name, 12);
119        w.write(if self.transpose { 1 } else { 0 });
120        w.write(self.table_tick);
121        w.write(self.port);
122        w.write(self.channel);
123        w.write(self.bank_select);
124        w.write(self.program_change);
125
126        w.skip(3);
127
128        for cc in self.custom_cc {
129            cc.write(w);
130        }
131
132        self.mods.write_modes(w, MIDIOut::MOD_OFFSET)
133    }
134
135    pub fn from_reader(
136        _ver: Version,
137        reader: &mut Reader,
138        number: u8,
139        version: Version,
140    ) -> M8Result<Self> {
141        let name = reader.read_string(12);
142        let transpose = reader.read_bool();
143        let table_tick = reader.read();
144
145        let port = reader.read();
146        let channel = reader.read();
147        let bank_select = reader.read();
148        let program_change = reader.read();
149        reader.read_bytes(3); // discard
150        let custom_cc = arr![ControlChange::from_reader(reader)?; 10];
151        let mods = if version.at_least(3, 0) {
152            SynthParams::mod_only3(reader, MIDIOut::MOD_OFFSET)?
153        } else {
154            SynthParams::mod_only2(reader)?
155        };
156
157        Ok(MIDIOut {
158            number,
159            name,
160            transpose,
161            table_tick,
162
163            port,
164            channel,
165            bank_select,
166            program_change,
167            custom_cc,
168            mods,
169        })
170    }
171}