m8_file_parser/instruments/
mod.rs1use crate::eq::Equ;
2use crate::reader::*;
3use crate::version::*;
4use crate::writer::Writer;
5use crate::Table;
6use crate::TableView;
7use crate::V4_1_OFFSETS;
8
9mod common;
10mod external_inst;
11mod fmsynth;
12mod hypersynth;
13mod macrosynth;
14mod midi;
15mod modulator;
16mod sampler;
17mod wavsynth;
18
19pub use common::{LimitType, SynthParams};
20
21pub use external_inst::*;
22pub use fmsynth::*;
23pub use hypersynth::*;
24pub use macrosynth::*;
25pub use midi::*;
26pub use modulator::*;
27pub use sampler::*;
28pub use wavsynth::*;
29
30#[derive(PartialEq, Debug, Clone, Default)]
31pub enum Instrument {
32 WavSynth(WavSynth),
33 MacroSynth(MacroSynth),
34 Sampler(Sampler),
35 MIDIOut(MIDIOut),
36 FMSynth(FMSynth),
37 HyperSynth(HyperSynth),
38 External(ExternalInst),
39 #[default]
40 None,
41}
42
43pub mod params {
45 pub const NAME: &'static str = "NAME";
46 pub const TRANSPOSE: &'static str = "TRANSPOSE";
47 pub const TBLTIC: &'static str = "TBL. TIC";
48 pub const EQ: &'static str = "EQ";
49 pub const SCALE: &'static str = "SCALE";
50
51 pub const CCA: &'static str = "CCA";
52 pub const CCB: &'static str = "CCB";
53 pub const CCC: &'static str = "CCC";
54 pub const CCD: &'static str = "CCD";
55
56 pub const DEST: &'static str = "DEST";
57 pub const AMOUNT: &'static str = "AMT";
58 pub const ATTACK: &'static str = "ATK";
59 pub const DECAY: &'static str = "DEC";
60 pub const HOLD: &'static str = "HOLD";
61 pub const SUSTAIN: &'static str = "SUS";
62 pub const RELEASE: &'static str = "REL";
63 pub const PEAK: &'static str = "PEAK";
64 pub const BODY: &'static str = "BODY";
65 pub const FREQ: &'static str = "FREQ";
66 pub const TRIGGER: &'static str = "TRIG";
67 pub const LFOSHAPE: &'static str = "OSC";
68 pub const SOURCE: &'static str = "SRC";
69}
70
71pub mod dests {
73 pub const OFF: &'static str = "OFF";
74 pub const VOLUME: &'static str = "VOLUME";
75 pub const PITCH: &'static str = "PITCH";
76 pub const CUTOFF: &'static str = "CUTOFF";
77 pub const RES: &'static str = "RES";
78 pub const AMP: &'static str = "AMP";
79 pub const PAN: &'static str = "PAN";
80 pub const DEGRADE: &'static str = "DEGRADE";
81 pub const MOD_AMT: &'static str = "MOD AMT";
82 pub const MOD_RATE: &'static str = "MOD RATE";
83 pub const MOD_BOTH: &'static str = "MOD BOTH";
84 pub const MOD_BINV: &'static str = "MOD BINV";
85}
86
87#[derive(Clone, Copy)]
90pub struct CommandPack {
91 pub instr: &'static [&'static str],
93
94 pub mod_commands: [&'static [&'static str]; SynthParams::MODULATOR_COUNT],
97}
98
99impl Default for CommandPack {
100 fn default() -> Self {
101 Self {
102 instr: Default::default(),
103 mod_commands: Default::default(),
104 }
105 }
106}
107
108impl CommandPack {
109 pub const INSTRUMENT_COMMAND_OFFSET: usize = 0x80;
111
112 pub const BASE_INSTRUMENT_COMMAND_COUNT: usize = 18;
115
116 pub const BASE_INSTRUMENT_COMMAND_END: usize = CommandPack::INSTRUMENT_COMMAND_OFFSET
118 + Mod::COMMAND_PER_MOD * SynthParams::MODULATOR_COUNT;
119
120 pub fn accepts(self, cmd: u8) -> bool {
122 let cmd = cmd as usize;
123 CommandPack::INSTRUMENT_COMMAND_OFFSET <= cmd
124 && cmd <= (CommandPack::BASE_INSTRUMENT_COMMAND_END + self.instr.len())
125 }
126
127 pub fn try_render(self, cmd: u8) -> Option<&'static str> {
128 if self.instr.len() == 0 {
129 return None;
130 }
131 if (cmd as usize) < CommandPack::INSTRUMENT_COMMAND_OFFSET {
132 return None;
133 }
134
135 let cmd = cmd as usize - CommandPack::INSTRUMENT_COMMAND_OFFSET;
136
137 if cmd < CommandPack::BASE_INSTRUMENT_COMMAND_COUNT {
138 if cmd < self.instr.len() {
139 return Some(self.instr[cmd]);
140 } else {
141 return None;
142 }
143 }
144
145 let mod_cmd = cmd - CommandPack::BASE_INSTRUMENT_COMMAND_COUNT;
146 let mod_ix = mod_cmd / Mod::COMMAND_PER_MOD;
147
148 if mod_ix < self.mod_commands.len() {
149 let ix = mod_cmd - Mod::COMMAND_PER_MOD * mod_ix;
150 return Some(self.mod_commands[mod_ix][ix]);
151 }
152
153 let extra_cmd = cmd - (Mod::COMMAND_PER_MOD * SynthParams::MODULATOR_COUNT);
154 if extra_cmd < self.instr.len() {
155 return Some(self.instr[extra_cmd]);
156 }
157
158 None
159 }
160}
161
162pub struct InstrumentWithEq {
166 pub instrument: Instrument,
168
169 pub table: Table,
171
172 pub eq: Option<Equ>,
175
176 pub version: Version
178}
179
180impl InstrumentWithEq {
181 pub fn table_view(&self) -> TableView {
182 TableView {
183 table: &self.table,
184 table_index: 0,
185 instrument: self.instrument.instr_command_text(self.version)
186 }
187 }
188}
189
190impl Instrument {
191 pub const INSTRUMENT_MEMORY_SIZE: usize = 215;
192 pub const V4_SIZE: usize = Self::INSTRUMENT_MEMORY_SIZE;
193
194 pub fn is_empty(&self) -> bool {
195 match self {
196 Instrument::None => true,
197 _ => false,
198 }
199 }
200
201 pub fn instr_command_text(&self, ver: Version) -> CommandPack {
202 let (commands, mods) = match self {
203 Instrument::WavSynth(ws) => (ws.command_name(ver), &ws.synth_params.mods),
204 Instrument::MacroSynth(ms) => (ms.command_name(ver), &ms.synth_params.mods),
205 Instrument::Sampler(s) => (s.command_name(ver), &s.synth_params.mods),
206 Instrument::MIDIOut(mo) => (mo.command_name(ver), &mo.mods.mods),
207 Instrument::FMSynth(fs) => (fs.command_name(ver), &fs.synth_params.mods),
208 Instrument::HyperSynth(hs) => (hs.command_name(ver), &hs.synth_params.mods),
209 Instrument::External(ex) => (ex.command_name(ver), &ex.synth_params.mods),
210 Instrument::None => return CommandPack::default(),
211 };
212
213 CommandPack {
214 instr: commands,
215 mod_commands: [
216 mods[0].command_name(ver, 0),
217 mods[1].command_name(ver, 1),
218 mods[2].command_name(ver, 2),
219 mods[3].command_name(ver, 3),
220 ],
221 }
222 }
223
224 pub fn write(&self, ver: Version, w: &mut Writer) {
225 match self {
226 Instrument::WavSynth(ws) => {
227 w.write(0);
228 ws.write(ver, w);
229 }
230 Instrument::MacroSynth(ms) => {
231 w.write(1);
232 ms.write(ver, w);
233 }
234 Instrument::Sampler(s) => {
235 w.write(2);
236 s.write(ver, w);
237 }
238 Instrument::MIDIOut(mo) => {
239 w.write(3);
240 mo.write(ver, w);
241 }
242 Instrument::FMSynth(fs) => {
243 w.write(4);
244 fs.write(ver, w);
245 }
246 Instrument::HyperSynth(hs) => {
247 w.write(5);
248 hs.write(ver, w);
249 }
250 Instrument::External(ex) => {
251 w.write(6);
252 ex.write(ver, w);
253 }
254 Instrument::None => w.write(0xFF),
255 }
256 }
257
258 pub fn name(&self) -> Option<&str> {
259 match self {
260 Instrument::WavSynth(ws) => Some(&ws.name),
261 Instrument::MacroSynth(ms) => Some(&ms.name),
262 Instrument::Sampler(s) => Some(&s.name),
263 Instrument::MIDIOut(_) => None,
264 Instrument::FMSynth(fs) => Some(&fs.name),
265 Instrument::HyperSynth(hs) => Some(&hs.name),
266 Instrument::External(ex) => Some(&ex.name),
267 Instrument::None => None,
268 }
269 }
270
271 pub fn set_name(&mut self, name: String) {
272 match self {
273 Instrument::WavSynth(ws) => ws.name = name,
274 Instrument::MacroSynth(ms) => ms.name = name,
275 Instrument::Sampler(s) => s.name = name,
276 Instrument::MIDIOut(mo) => mo.name = name,
277 Instrument::FMSynth(fs) => fs.name = name,
278 Instrument::HyperSynth(hs) => hs.name = name,
279 Instrument::External(ex) => ex.name = name,
280 Instrument::None => {}
281 }
282 }
283
284 pub fn equ(&self) -> Option<u8> {
285 match self {
286 Instrument::WavSynth(ws) => Some(ws.synth_params.associated_eq),
287 Instrument::MacroSynth(ms) => Some(ms.synth_params.associated_eq),
288 Instrument::Sampler(s) => Some(s.synth_params.associated_eq),
289 Instrument::MIDIOut(_) => None,
290 Instrument::FMSynth(fs) => Some(fs.synth_params.associated_eq),
291 Instrument::HyperSynth(hs) => Some(hs.synth_params.associated_eq),
292 Instrument::External(ex) => Some(ex.synth_params.associated_eq),
293 Instrument::None => None,
294 }
295 }
296
297 pub fn set_eq(&mut self, eq_ix: u8) {
298 match self {
299 Instrument::WavSynth(ws) => ws.synth_params.set_eq(eq_ix),
300 Instrument::MacroSynth(ms) => ms.synth_params.set_eq(eq_ix),
301 Instrument::Sampler(s) => s.synth_params.set_eq(eq_ix),
302 Instrument::MIDIOut(_) => {}
303 Instrument::FMSynth(fs) => fs.synth_params.set_eq(eq_ix),
304 Instrument::HyperSynth(hs) => hs.synth_params.set_eq(eq_ix),
305 Instrument::External(ex) => ex.synth_params.set_eq(eq_ix),
306 Instrument::None => {}
307 }
308 }
309
310 pub fn read_from_reader(reader: &mut Reader) -> M8Result<InstrumentWithEq> {
312 let instrument_end_offset = Instrument::INSTRUMENT_MEMORY_SIZE + Version::SIZE;
313 if reader.len() < instrument_end_offset {
314 return Err(ParseError(
315 "File is not long enough to be a M8 Instrument".to_string(),
316 ));
317 }
318
319 let version = Version::from_reader(reader)?;
320 let instrument = Self::from_reader(reader, 0, version)?;
321 let table = Table::from_reader(reader, version)?;
322
323 let eq = match V4_1_OFFSETS.instrument_file_eq_offset {
324 None => None,
325 Some(ofs) if version.at_least(4, 0) => {
326 if reader.len() >= ofs + Equ::V4_SIZE {
327 reader.set_pos(ofs);
328 Some(Equ::from_reader(reader))
329 } else {
330 None
331 }
332 }
333 Some(_) => None,
334 };
335
336 Ok(InstrumentWithEq { instrument, eq, table, version })
337 }
338
339 pub fn read(reader: &mut impl std::io::Read) -> M8Result<InstrumentWithEq> {
341 let mut buf: Vec<u8> = vec![];
342 reader.read_to_end(&mut buf).unwrap();
343 let mut reader = Reader::new(buf);
344
345 Self::read_from_reader(&mut reader)
346 }
347
348 pub fn from_reader(reader: &mut Reader, number: u8, version: Version) -> M8Result<Self> {
349 let start_pos = reader.pos();
350 let kind = reader.read();
351
352 let instr = match kind {
353 0x00 => Self::WavSynth(WavSynth::from_reader(version, reader, number, version)?),
354 0x01 => Self::MacroSynth(MacroSynth::from_reader(version, reader, number, version)?),
355 0x02 => Self::Sampler(Sampler::from_reader(
356 version, reader, start_pos, number, version,
357 )?),
358 0x03 => Self::MIDIOut(MIDIOut::from_reader(version, reader, number, version)?),
359 0x04 => Self::FMSynth(FMSynth::from_reader(version, reader, number, version)?),
360 0x05 if version.at_least(3, 0) => {
361 Self::HyperSynth(HyperSynth::from_reader(version, reader, number)?)
362 }
363 0x06 if version.at_least(3, 0) => {
364 Self::External(ExternalInst::from_reader(version, reader, number)?)
365 }
366 0xFF => Self::None,
367 _ => {
368 return Err(ParseError(format!(
369 "Instrument type {} not supported",
370 kind
371 )))
372 }
373 };
374
375 reader.set_pos(start_pos + Instrument::INSTRUMENT_MEMORY_SIZE);
376
377 Ok(instr)
378 }
379}