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