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