1use std::cmp::min;
2use std::fmt;
3
4use crate::eq::Equ;
5use crate::fx::*;
6use crate::instruments::*;
7use crate::reader::*;
8use crate::remapper::EqMapping;
9use crate::remapper::InstrumentMapping;
10use crate::remapper::PhraseMapping;
11use crate::remapper::TableMapping;
12use crate::remapper::EQ_TRACKING_COMMAND_NAMES;
13use crate::remapper::INSTRUMENT_TRACKING_COMMAND_NAMES;
14use crate::remapper::TABLE_TRACKING_COMMAND_NAMES;
15use crate::scale::*;
16use crate::settings::*;
17use crate::version::*;
18use crate::writer::Writer;
19
20use arr_macro::arr;
21use byteorder::{ByteOrder, LittleEndian};
22
23pub struct Offsets {
24 pub groove: usize,
25 pub song: usize,
26 pub phrases: usize,
27 pub chains: usize,
28 pub table: usize,
29 pub instruments: usize,
30 pub effect_settings: usize,
31 pub midi_mapping: usize,
32 pub scale: usize,
33 pub eq: usize,
34
35 pub instrument_eq_count: usize,
37
38 pub instrument_file_eq_offset: Option<usize>,
41}
42
43impl Offsets {
44 pub fn eq_count(&self) -> usize {
45 self.instrument_eq_count + 3 + 1
47 }
48}
49
50pub const V4_OFFSETS: Offsets = Offsets {
51 groove: 0xEE,
52 song: 0x2EE,
53 phrases: 0xAEE,
54 chains: 0x9A5E,
55 table: 0xBA3E,
56 instruments: 0x13A3E,
57 effect_settings: 0x1A5C1,
58 midi_mapping: 0x1A5FE,
59 scale: 0x1AA7E,
60 eq: 0x1AD5A + 4,
61 instrument_eq_count: 32,
62 instrument_file_eq_offset: None,
63};
64
65pub const V4_1_OFFSETS: Offsets = Offsets {
66 groove: 0xEE,
67 song: 0x2EE,
68 phrases: 0xAEE,
69 chains: 0x9A5E,
70 table: 0xBA3E,
71 instruments: 0x13A3E,
72 effect_settings: 0x1A5C1,
73 midi_mapping: 0x1A5FE,
74 scale: 0x1AA7E,
75 eq: 0x1AD5A + 4,
76 instrument_eq_count: 0x80,
77 instrument_file_eq_offset: Some(0x165),
78};
79
80#[derive(PartialEq, Clone)]
84pub struct Song {
85 pub version: Version,
86 pub directory: String,
87 pub transpose: u8,
88 pub tempo: f32,
89 pub quantize: u8,
90 pub name: String,
91 pub key: u8,
92
93 pub song: SongSteps,
94 pub phrases: Vec<Phrase>,
95 pub chains: Vec<Chain>,
96 pub instruments: Vec<Instrument>,
97 pub tables: Vec<Table>,
98 pub grooves: Vec<Groove>,
99 pub scales: Vec<Scale>,
100
101 pub mixer_settings: MixerSettings,
102 pub effects_settings: EffectsSettings,
103 pub midi_settings: MidiSettings,
104 pub midi_mappings: Vec<MidiMapping>,
105 pub eqs: Vec<Equ>,
106}
107
108impl fmt::Debug for Song {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.debug_struct("Song")
111 .field("version", &self.version)
112 .field("directory", &self.directory)
113 .field("name", &self.name)
114 .field("tempo", &self.tempo)
115 .field("transpose", &self.transpose)
116 .field("quantize", &self.quantize)
117 .field("key", &self.key)
118 .field("song", &self.song)
119 .field("chains", self.chains.get(0).unwrap_or(&Chain::default()))
120 .field("phrases", &self.phrase_view(0))
121 .field(
122 "instruments",
123 self.instruments.get(0).unwrap_or(&Instrument::default()),
124 )
125 .field("tables", &self.table_view(0))
126 .field("grooves", &self.grooves[0])
127 .field("scales", &self.scales[0])
128 .field("eqs", self.eqs.get(0).unwrap_or(&Equ::default()))
129 .field("mixer_settings", &self.mixer_settings)
130 .field("effects_settings", &self.effects_settings)
131 .field("midi_settings", &self.midi_settings)
132 .finish()
133 }
134}
135
136impl Song {
137 const SIZE_PRIOR_TO_2_5: usize = 0x1A970;
138 const SIZE: usize = 0x1AD09;
139 pub const N_PHRASES: usize = 255;
140 pub const N_CHAINS: usize = 255;
141 pub const N_INSTRUMENTS: usize = 128;
142 pub const N_TABLES: usize = 256;
143 pub const N_GROOVES: usize = 32;
144 pub const N_SCALES: usize = 16;
145
146 pub const N_MIDI_MAPPINGS: usize = 128;
147
148 pub fn phrase_view(&self, ix: usize) -> PhraseView<'_> {
149 self.phrase_view_with_templates(ix, ReferenceTemplating::default())
150 }
151
152 pub fn phrase_view_with_templates(&self, ix: usize, templates: ReferenceTemplating) -> PhraseView<'_> {
153 PhraseView {
154 phrase: &self.phrases[ix],
155 phrase_id: ix,
156 instruments: &self.instruments,
157 templates
158 }
159 }
160
161
162 pub fn offsets(&self) -> &'static Offsets {
163 if self.version.at_least(4, 1) {
164 &V4_1_OFFSETS
165 } else {
166 &V4_OFFSETS
167 }
168 }
169
170 pub fn eq_count(&self) -> usize {
171 self.offsets().eq_count()
172 }
173
174 pub fn table_view_with_templates(&self, ix: usize, templates: ReferenceTemplating) -> TableView<'_> {
175 TableView {
176 table: &self.tables[ix],
177 table_index: ix,
178 instrument: if ix < Song::N_INSTRUMENTS {
179 self.instruments[ix].instr_command_text(self.version)
180 } else {
181 CommandPack::default()
182 },
183 templates
184 }
185 }
186
187 pub fn table_view(&self, ix: usize) -> TableView<'_> {
188 self.table_view_with_templates(ix, ReferenceTemplating::default())
189 }
190
191 pub fn eq_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 f.debug_list().entries(self.eqs.iter()).finish()
193 }
194
195 pub fn read(reader: &mut impl std::io::Read) -> M8Result<Self> {
196 let mut buf: Vec<u8> = vec![];
197 reader.read_to_end(&mut buf).unwrap();
198 let mut reader = Reader::new(buf);
199 Self::read_from_reader(&mut reader)
200 }
201
202 pub fn read_from_reader(mut reader: &mut Reader) -> M8Result<Self> {
203 if reader.len() < Self::SIZE_PRIOR_TO_2_5 + Version::SIZE {
204 return Err(ParseError(
205 "File is not long enough to be a M8 song".to_string(),
206 ));
207 }
208 let version = Version::from_reader(&mut reader)?;
209 if version.at_least(2, 5) && reader.len() < Self::SIZE + Version::SIZE {
210 return Err(ParseError(
211 "File is not long enough to be a M8 song".to_string(),
212 ));
213 }
214
215 Self::from_reader(&mut reader, version)
216 }
217
218 pub fn write(&self, w: &mut Writer) -> Result<(), String> {
219 if !self.version.at_least(4, 0) {
220 Err(String::from(
221 "Only version 4.0 or above song can be rewritten",
222 ))
223 } else {
224 self.write_patterns(V4_OFFSETS, w);
225 Ok(())
226 }
227 }
228
229 fn write_patterns(&self, ofs: Offsets, w: &mut Writer) {
230 w.seek(ofs.song);
231 w.write_bytes(&self.song.steps);
232
233 w.seek(ofs.phrases);
234 for ph in &self.phrases {
235 ph.write(w);
236 }
237
238 w.seek(ofs.chains);
239 for ch in &self.chains {
240 ch.write(w);
241 }
242
243 w.seek(ofs.table);
244 for table in &self.tables {
245 table.write(w);
246 }
247
248 w.seek(ofs.instruments);
249 for instr in &self.instruments {
250 let pos = w.pos();
251 instr.write(self.version, w);
252 w.seek(pos + Instrument::INSTRUMENT_MEMORY_SIZE);
253 }
254
255 w.seek(ofs.eq);
256 for eq in &self.eqs {
257 eq.write(w);
258 }
259 }
260
261 fn from_reader(reader: &mut Reader, version: Version) -> M8Result<Self> {
262 let directory = reader.read_string(128);
264 let transpose = reader.read();
265 let tempo = LittleEndian::read_f32(reader.read_bytes(4));
266 let quantize = reader.read();
267 let name = reader.read_string(12);
268 let midi_settings = MidiSettings::try_from(&mut *reader)?;
269 let key = reader.read();
270 reader.read_bytes(18); let mixer_settings = MixerSettings::from_reader(reader, version)?;
272
273 reader.set_pos(V4_OFFSETS.groove);
274 let grooves = (0..Self::N_GROOVES)
275 .map(|i| Groove::from_reader(reader, i as u8))
276 .collect::<M8Result<Vec<Groove>>>()?;
277 let song = SongSteps::from_reader(reader)?;
278 let phrases = (0..Self::N_PHRASES)
279 .map(|_| Phrase::from_reader(reader, version))
280 .collect::<M8Result<Vec<Phrase>>>()?;
281 let chains = (0..Self::N_CHAINS)
282 .map(|_| Chain::from_reader(reader))
283 .collect::<M8Result<Vec<Chain>>>()?;
284 let tables = (0..Self::N_TABLES)
285 .map(|_| Table::from_reader(reader, version))
286 .collect::<M8Result<Vec<Table>>>()?;
287
288 let instruments = (0..Self::N_INSTRUMENTS)
289 .map(|i| Instrument::from_reader(reader, i as u8, version))
290 .collect::<M8Result<Vec<Instrument>>>()?;
291
292 reader.set_pos(V4_OFFSETS.effect_settings);
293 let effects_settings = EffectsSettings::from_reader(reader, version)?;
294
295 reader.set_pos(V4_OFFSETS.midi_mapping);
296 let midi_mappings = (0..Self::N_MIDI_MAPPINGS)
297 .map(|_| MidiMapping::from_reader(reader))
298 .collect::<M8Result<Vec<MidiMapping>>>()?;
299
300 let scales: Vec<Scale> = if version.at_least(2, 5) {
301 reader.set_pos(V4_OFFSETS.scale);
302 (0..Self::N_SCALES)
303 .map(|i| Scale::from_reader(reader, i as u8))
304 .collect::<M8Result<Vec<Scale>>>()?
305 } else {
306 (0..Self::N_SCALES)
307 .map(|i| -> Scale {
308 let mut s = Scale::default();
309 s.number = i as u8;
310 s
311 })
312 .collect()
313 };
314
315 let eqs = if version.at_least(4, 0) {
316 let ofs = if version.at_least(4, 1) {
317 &V4_1_OFFSETS
318 } else {
319 &V4_OFFSETS
320 };
321
322 reader.set_pos(ofs.eq);
323 (0..ofs.instrument_eq_count)
324 .map(|_i| Equ::from_reader(reader))
325 .collect::<Vec<Equ>>()
326 } else {
327 vec![]
328 };
329
330 Ok(Self {
331 version,
332 directory,
333 transpose,
334 tempo,
335 quantize,
336 name,
337 midi_settings,
338 key,
339 mixer_settings,
340 grooves,
341 song,
342 phrases,
343 chains,
344 tables,
345 instruments,
346 scales,
347 effects_settings,
348 midi_mappings,
349 eqs,
350 })
351 }
352}
353
354#[derive(PartialEq, Clone)]
358pub struct SongSteps {
359 pub steps: [u8; SongSteps::TRACK_COUNT * SongSteps::ROW_COUNT],
360}
361
362impl SongSteps {
363 pub const TRACK_COUNT: usize = 8;
364 pub const ROW_COUNT: usize = 0x100;
365
366 pub fn print_screen(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
367 self.print_screen_from_to(f, 0, self.last_modified_row() as u8)
368 }
369
370 pub fn last_modified_row(&self) -> usize {
371 for row in (0 .. SongSteps::ROW_COUNT).rev() {
372 let ix = row * SongSteps::TRACK_COUNT;
373
374 let is_empty =
375 self.steps[ix .. ix + SongSteps::TRACK_COUNT].iter().all(|v| *v == 0xFF);
376
377 if !is_empty {
378 return row;
379 }
380 }
381
382 SongSteps::ROW_COUNT
383 }
384
385 pub fn print_screen_from_to(&self, f: &mut fmt::Formatter<'_>, start: u8, end: u8) -> std::fmt::Result {
386 write!(f, " 1 2 3 4 5 6 7 8 \n")?;
387
388 for row in start as usize .. min(0xFF, (end as usize) + 1) {
389 self.print_row(f, row as u8)?;
390 writeln!(f, "")?;
391 };
392
393 Ok(())
394 }
395
396 pub fn print_row(&self, f: &mut fmt::Formatter<'_>, row: u8) -> std::fmt::Result {
397 let start = row as usize * 8;
398
399 write!(f, "{row:02x} ")?;
400
401
402 for bix in start .. start + 8 {
403 let v = self.steps[bix];
404
405 if v == 0xFF{
406 write!(f, "-- ")?
407 } else {
408 write!(f, "{:02x} ", v)?
409 }
410 };
411
412 Ok(())
413 }
414
415 fn from_reader(reader: &mut Reader) -> M8Result<Self> {
416 Ok(Self {
417 steps: reader.read_bytes(2048).try_into().unwrap(),
418 })
419 }
420}
421
422impl fmt::Display for SongSteps {
423 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424 write!(f, "SONG\n\n")?;
425 self.print_screen(f)
426 }
427}
428
429impl fmt::Debug for SongSteps {
430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431 write!(f, "{}", &self)
432 }
433}
434
435#[derive(PartialEq, Clone, Default)]
439pub struct Chain {
440 pub steps: [ChainStep; 16],
441}
442
443impl Chain {
444 pub const V4_SIZE: usize = ChainStep::V4_SIZE * 16;
445
446 pub fn is_empty(&self) -> bool {
447 self.steps.iter().all(|s| s.is_empty())
448 }
449
450 pub fn clear(&mut self) {
451 let dflt = ChainStep::default();
452
453 for s in &mut self.steps {
454 *s = dflt;
455 }
456 }
457
458 pub fn print_screen(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
459 write!(f, " PH TSP\n")?;
460
461 for row in 0 .. 16 {
462 self.steps[row].print(f, row as u8)?;
463 write!(f, "\n")?;
464 }
465
466 Ok(())
467 }
468
469 pub fn map(&self, mapping: &PhraseMapping) -> Self {
470 let mut nc = self.clone();
471
472 for i in 0..16 {
473 nc.steps[i] = nc.steps[i].map(mapping);
474 }
475
476 nc
477 }
478
479 pub fn write(&self, w: &mut Writer) {
480 for cs in &self.steps {
481 cs.write(w)
482 }
483 }
484
485 pub fn from_reader(reader: &mut Reader) -> M8Result<Self> {
486 Ok(Self {
487 steps: arr![ChainStep::from_reader(reader)?; 16],
488 })
489 }
490}
491
492impl fmt::Display for Chain {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 write!(f, "CHAIN\n\n")?;
495 self.print_screen(f)
496 }
497}
498impl fmt::Debug for Chain {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 write!(f, "{}", &self)
501 }
502}
503
504#[derive(PartialEq, Debug, Clone, Copy)]
505pub struct ChainStep {
506 pub phrase: u8,
507 pub transpose: u8,
508}
509
510impl Default for ChainStep {
511 fn default() -> Self {
512 Self {
513 phrase: 255,
514 transpose: 0,
515 }
516 }
517}
518
519impl ChainStep {
520 pub const V4_SIZE: usize = 2;
521
522 pub fn is_empty(self) -> bool {
523 self.phrase == 0xFF
524 }
525
526 pub fn print(&self, f: &mut fmt::Formatter<'_>, row: u8) -> std::fmt::Result {
527 if self.is_empty() {
528 write!(f, "{:x} -- 00", row)
529 } else {
530 write!(f, "{:x} {:02x} {:02x}", row, self.phrase, self.transpose)
531 }
532 }
533
534 pub fn map(&self, mapping: &PhraseMapping) -> Self {
535 let phrase_ix = self.phrase as usize;
536 let phrase = if phrase_ix >= Song::N_PHRASES {
537 self.phrase
538 } else {
539 mapping.mapping[phrase_ix]
540 };
541
542 Self {
543 phrase,
544 transpose: self.transpose,
545 }
546 }
547
548 pub fn write(&self, w: &mut Writer) {
549 w.write(self.phrase);
550 w.write(self.transpose);
551 }
552
553 fn from_reader(reader: &mut Reader) -> M8Result<Self> {
554 Ok(Self {
555 phrase: reader.read(),
556 transpose: reader.read(),
557 })
558 }
559}
560
561#[derive(PartialEq, Clone, Default)]
565pub struct Phrase {
566 pub steps: [Step; 16],
567 version: Version,
568}
569
570impl Phrase {
571 pub const V4_SIZE: usize = 16 * Step::V4_SIZE;
572
573 pub fn is_empty(&self) -> bool {
574 self.steps.iter().all(|s| s.is_empty())
575 }
576
577 pub fn clear(&mut self) {
578 for s in &mut self.steps {
579 s.clear();
580 }
581 }
582
583 pub(crate) fn print_screen(
584 &self,
585 f: &mut fmt::Formatter<'_>,
586 instruments: &[Instrument],
587 templates: &ReferenceTemplating) -> std::fmt::Result {
588 let mut cmd_pack = CommandPack::default();
589 let fx_commands = FX::fx_command_names(self.version);
590
591 write!(f, " N V I FX1 FX2 FX3 \n")?;
592
593 for i in 0..16 {
594 let step = &self.steps[i];
595 let instrument = step.instrument as usize;
596
597 if instrument < Song::N_INSTRUMENTS {
598 cmd_pack = instruments[instrument].instr_command_text(self.version);
599 }
600
601 step.print(f, i as u8, fx_commands, cmd_pack, templates)?;
602 write!(f, "\n")?;
603 }
604
605 Ok(())
606 }
607
608 pub fn map_instruments(
609 &self,
610 instrument_mapping: &InstrumentMapping,
611 table_mapping: &TableMapping,
612 eq_mapping: &EqMapping,
613 ) -> Self {
614 let mut steps = self.steps.clone();
615 for i in 0..steps.len() {
616 steps[i] = steps[i].map_instr(&instrument_mapping, table_mapping, eq_mapping);
617 }
618
619 Self {
620 steps,
621 version: self.version,
622 }
623 }
624
625 pub fn write(&self, w: &mut Writer) {
626 for s in &self.steps {
627 s.write(w);
628 }
629 }
630
631 pub fn from_reader(reader: &mut Reader, version: Version) -> M8Result<Self> {
632 Ok(Self {
633 steps: arr![Step::from_reader(reader)?; 16],
634 version,
635 })
636 }
637}
638
639pub struct PhraseView<'a> {
640 phrase: &'a Phrase,
641 phrase_id: usize,
642 instruments: &'a [Instrument],
643 templates: ReferenceTemplating
644}
645
646impl<'a> fmt::Display for PhraseView<'a> {
647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648 write!(f, "PHRASE {:02X}\n\n", self.phrase_id)?;
649 self.phrase.print_screen(f, self.instruments, &self.templates)
650 }
651}
652
653impl<'a> fmt::Debug for PhraseView<'a> {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 write!(f, "{}", &self)
656 }
657}
658
659#[derive(PartialEq, Debug, Clone, Default)]
660pub struct Step {
661 pub note: Note,
662 pub velocity: u8,
663 pub instrument: u8,
664 pub fx1: FX,
665 pub fx2: FX,
666 pub fx3: FX,
667}
668
669impl Step {
670 pub const V4_SIZE: usize = 3 + 3 * FX::V4_SIZE;
671
672 pub fn all_fx(&self) -> [FX; 3] {
673 [self.fx1, self.fx2, self.fx3]
674 }
675
676 pub fn print(&self,
677 f: &mut fmt::Formatter<'_>,
678 row: u8,
679 fx_cmds: FxCommands,
680 cmd_pack: CommandPack,
681 templates: &ReferenceTemplating) -> std::fmt::Result {
682 let velocity = if self.velocity == 255 {
683 format!("--")
684 } else {
685 format!("{:02x}", self.velocity)
686 };
687
688 let instrument = if self.instrument == 255 {
689 format!("--")
690 } else {
691 templates.try_instrument_templating(self.instrument)
692 .unwrap_or_else(|| format!("{:02x}", self.instrument))
693 };
694
695 write!(
696 f,
697 "{:x} {} {} {} {} {} {}",
698 row,
699 self.note,
700 velocity,
701 instrument,
702 self.fx1.print(fx_cmds, cmd_pack, templates),
703 self.fx2.print(fx_cmds, cmd_pack, templates),
704 self.fx3.print(fx_cmds, cmd_pack, templates)
705 )
706 }
707
708 pub fn clear(&mut self) {
709 self.note = Note::default();
710 self.velocity = 0xFF;
711 self.instrument = 0xFF;
712 self.fx1 = FX::default();
713 self.fx2 = FX::default();
714 self.fx3 = FX::default();
715 }
716
717 pub fn is_empty(&self) -> bool {
718 self.note.is_empty()
719 && self.velocity == 0xFF
720 && self.instrument == 0xFF
721 && self.fx1.is_empty()
722 && self.fx2.is_empty()
723 && self.fx3.is_empty()
724 }
725
726 pub fn map_instr(
727 &self,
728 instrument_mapping: &InstrumentMapping,
729 table_mapping: &TableMapping,
730 eq_mapping: &EqMapping,
731 ) -> Step {
732 let instrument = if (self.instrument as usize) >= Song::N_INSTRUMENTS {
733 self.instrument
734 } else {
735 instrument_mapping.mapping[self.instrument as usize]
736 };
737
738 Self {
739 note: self.note,
740 velocity: self.velocity,
741 instrument,
742 fx1: self
743 .fx1
744 .map_instr(instrument_mapping, table_mapping, eq_mapping),
745 fx2: self
746 .fx2
747 .map_instr(instrument_mapping, table_mapping, eq_mapping),
748 fx3: self
749 .fx3
750 .map_instr(instrument_mapping, table_mapping, eq_mapping),
751 }
752 }
753
754 pub fn write(&self, w: &mut Writer) {
755 w.write(self.note.0);
756 w.write(self.velocity);
757 w.write(self.instrument);
758 self.fx1.write(w);
759 self.fx2.write(w);
760 self.fx3.write(w);
761 }
762
763 fn from_reader(reader: &mut Reader) -> M8Result<Self> {
764 Ok(Self {
765 note: Note(reader.read()),
766 velocity: reader.read(),
767 instrument: reader.read(),
768 fx1: FX::from_reader(reader)?,
769 fx2: FX::from_reader(reader)?,
770 fx3: FX::from_reader(reader)?,
771 })
772 }
773}
774
775#[derive(PartialEq, Debug, Clone, Copy)]
779pub struct Note(pub u8);
780
781impl Note {
782 pub fn is_empty(self) -> bool {
783 self.0 == 0xFF
784 }
785}
786
787impl Default for Note {
788 fn default() -> Self {
789 Note(255)
790 }
791}
792
793const NOTES : [&'static str; 12] =
794 [
795 "C-",
796 "C#",
797 "D-",
798 "D#",
799 "E-",
800 "F-",
801 "F#",
802 "G-",
803 "G#",
804 "A-",
805 "A#",
806 "B-"
807 ];
808
809impl fmt::Display for Note {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 if self.0 == 255 {
812 write!(f, "---")
813 } else if self.0 >= 0x80 {
814 write!(f, "OFF") } else {
816 let oct = (self.0 / 12) + 1;
817 let n = NOTES[(self.0 % 12) as usize];
818 write!(f, "{}{:X}", n, oct)
819 }
820 }
821}
822
823#[derive(PartialEq, Clone)]
827pub struct Table {
828 pub steps: [TableStep; 16],
829 version: Version,
830}
831impl Table {
832 pub const V4_SIZE: usize = 16 * TableStep::V4_SIZE;
833
834 pub fn is_empty(&self) -> bool {
835 self.steps.iter().all(|s| s.is_empty())
836 }
837
838 pub fn clear(&mut self) {
839 let dflt = TableStep::default();
840
841 for s in &mut self.steps {
842 *s = dflt.clone();
843 }
844 }
845
846 pub fn map_instr(
847 &self,
848 instr_mapping: &InstrumentMapping,
849 table_mapping: &TableMapping,
850 eq_mapping: &EqMapping,
851 ) -> Self {
852 let mut steps = self.steps.clone();
853
854 for i in 0..16 {
855 steps[i] = steps[i].map_instr(instr_mapping, table_mapping, eq_mapping);
856 }
857
858 Self {
859 version: self.version,
860 steps,
861 }
862 }
863
864 pub(crate) fn print_screen(
865 &self,
866 f: &mut fmt::Formatter<'_>,
867 cmd: CommandPack,
868 templates: &ReferenceTemplating) -> std::fmt::Result {
869 let fx_cmd = FX::fx_command_names(self.version);
870 write!(f, " N V FX1 FX2 FX3 \n")?;
871
872 for i in 0..16 {
873 let step = &self.steps[i];
874
875 step.print(f, i as u8, fx_cmd, cmd, templates)?;
876 write!(f, "\n")?
877 }
878
879 Ok(())
880 }
881
882 pub fn write(&self, w: &mut Writer) {
883 for ts in &self.steps {
884 ts.write(w);
885 }
886 }
887
888 pub fn from_reader(reader: &mut Reader, version: Version) -> M8Result<Self> {
889 Ok(Self {
890 steps: arr![TableStep::from_reader(reader)?; 16],
891 version,
892 })
893 }
894}
895
896pub enum ReferenceTemplating {
897 NoTemplate,
898 WithTemplates {
899 instrument: Option<String>,
900 instrument_command: Option<String>,
901 table: Option<String>,
902 eq: Option<String>,
903 }
904}
905
906impl ReferenceTemplating {
907 fn apply_cmd_template(template: &str, cmd: &str, value: u8) -> Option<String> {
908 Some(template
909 .replace("{HEX}", &format!("{:02X}", value))
910 .replace("{CMD}", cmd))
911 }
912
913 pub fn try_instrument_templating(&self, value: u8) -> Option<String> {
914 match self {
915 ReferenceTemplating::NoTemplate => None,
916 ReferenceTemplating::WithTemplates { instrument: Some(it), instrument_command: _, table: _, eq: _ } => {
917 Some(it.replace("{HEX}", &format!("{:02X}", value)))
918 }
919 ReferenceTemplating::WithTemplates { instrument: _, instrument_command: _, table: _, eq: _ } => None
920 }
921 }
922
923 pub fn try_template(&self, command: &str, value: u8) -> Option<String> {
924 let (instr, table, eqq) = match self {
925 ReferenceTemplating::NoTemplate => return None,
926 ReferenceTemplating::WithTemplates {
927 instrument: _,
928 instrument_command,
929 table,
930 eq } =>
931 (instrument_command, table, eq)
932 };
933
934 if let Some(it) = instr {
935 if INSTRUMENT_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
936 return ReferenceTemplating::apply_cmd_template(&it, command, value)
937 }
938 }
939
940 if let Some(tt) = table {
941 if TABLE_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
942 return ReferenceTemplating::apply_cmd_template(&tt, command, value)
943 }
944 }
945
946 if let Some(et) = eqq {
947 if EQ_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
948 return ReferenceTemplating::apply_cmd_template(&et, command, value)
949 }
950 }
951
952 None
953 }
954}
955
956impl Default for ReferenceTemplating {
957 fn default() -> Self { Self::NoTemplate }
958}
959
960pub struct TableView<'a> {
961 pub(crate) table: &'a Table,
962 pub(crate) table_index: usize,
963 pub(crate) instrument: CommandPack,
964
965 pub(crate) templates: ReferenceTemplating,
966}
967
968impl<'a> fmt::Display for TableView<'a> {
969 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
970 write!(f, "TABLE {:02X}\n\n", self.table_index)?;
971 self.table.print_screen(f,self.instrument, &self.templates)
972 }
973}
974
975impl<'a> fmt::Debug for TableView<'a> {
976 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
977 write!(f, "{}", &self)
978 }
979}
980
981#[derive(PartialEq, Debug, Clone)]
982pub struct TableStep {
983 pub transpose: u8,
984 pub velocity: u8,
985 pub fx1: FX,
986 pub fx2: FX,
987 pub fx3: FX,
988}
989
990impl Default for TableStep {
991 fn default() -> Self {
992 Self {
993 transpose: 0,
994 velocity: 0xFF,
995 fx1: Default::default(),
996 fx2: Default::default(),
997 fx3: Default::default(),
998 }
999 }
1000}
1001
1002impl TableStep {
1003 pub const V4_SIZE: usize = 2 + 3 * FX::V4_SIZE;
1004
1005 pub fn all_fx(&self) -> [FX; 3] {
1006 [self.fx1, self.fx2, self.fx3]
1007 }
1008
1009 pub fn map_instr(
1010 &self,
1011 instr_mapping: &InstrumentMapping,
1012 table_mapping: &TableMapping,
1013 eq_mapping: &EqMapping,
1014 ) -> TableStep {
1015 Self {
1016 transpose: self.transpose,
1017 velocity: self.velocity,
1018 fx1: self.fx1.map_instr(instr_mapping, table_mapping, eq_mapping),
1019 fx2: self.fx2.map_instr(instr_mapping, table_mapping, eq_mapping),
1020 fx3: self.fx3.map_instr(instr_mapping, table_mapping, eq_mapping),
1021 }
1022 }
1023
1024 pub fn is_empty(&self) -> bool {
1025 self.transpose == 0
1026 && self.velocity == 0xFF
1027 && self.fx1.is_empty()
1028 && self.fx2.is_empty()
1029 && self.fx3.is_empty()
1030 }
1031
1032 pub fn print(
1033 &self,
1034 f: &mut fmt::Formatter<'_>,
1035 row: u8,
1036 fx_cmd: FxCommands,
1037 cmds: CommandPack,
1038 templates: &ReferenceTemplating) -> std::fmt::Result {
1039
1040 let transpose = if self.transpose == 255 {
1041 format!("--")
1042 } else {
1043 format!("{:02x}", self.transpose)
1044 };
1045
1046 let velocity = if self.velocity == 255 {
1047 format!("--")
1048 } else {
1049 format!("{:02x}", self.velocity)
1050 };
1051
1052 write!(
1053 f,
1054 "{:x} {} {} {} {} {}",
1055 row,
1056 transpose,
1057 velocity,
1058 self.fx1.print(fx_cmd, cmds, templates),
1059 self.fx2.print(fx_cmd, cmds, templates),
1060 self.fx3.print(fx_cmd, cmds, templates)
1061 )
1062 }
1063
1064 pub fn write(&self, w: &mut Writer) {
1065 w.write(self.transpose);
1066 w.write(self.velocity);
1067 self.fx1.write(w);
1068 self.fx2.write(w);
1069 self.fx3.write(w);
1070 }
1071
1072 fn from_reader(reader: &mut Reader) -> M8Result<Self> {
1073 Ok(Self {
1074 transpose: reader.read(),
1075 velocity: reader.read(),
1076 fx1: FX::from_reader(reader)?,
1077 fx2: FX::from_reader(reader)?,
1078 fx3: FX::from_reader(reader)?,
1079 })
1080 }
1081}
1082
1083#[derive(PartialEq, Clone)]
1087pub struct Groove {
1088 pub number: u8,
1089 pub steps: [u8; 16],
1090}
1091
1092impl Groove {
1093 fn from_reader(reader: &mut Reader, number: u8) -> M8Result<Self> {
1094 Ok(Self {
1095 number,
1096 steps: reader.read_bytes(16).try_into().unwrap(),
1097 })
1098 }
1099
1100 pub fn write(&self, w: &mut Writer) {
1101 w.write_bytes(&self.steps);
1102 }
1103
1104 pub fn active_steps(&self) -> &[u8] {
1105 let end = (&self.steps).iter().position(|&x| x == 255).unwrap_or(15);
1106 &self.steps[0..end]
1107 }
1108}
1109
1110impl fmt::Display for Groove {
1111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1112 write!(f, "Groove {}:{:?}", self.number, self.active_steps())
1113 }
1114}
1115impl fmt::Debug for Groove {
1116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1117 write!(f, "{}", &self)
1118 }
1119}
1120
1121#[cfg(test)]
1125mod tests {
1126 use crate::songs::*;
1127 use std::fs::File;
1128
1129 fn test_file() -> Song {
1130 let mut f = File::open("./examples/songs/TEST-FILE.m8s").expect("Could not open TEST-FILE");
1131 Song::read(&mut f).expect("Could not parse TEST-FILE")
1132 }
1133
1134 #[test]
1135 fn test_instrument_reading() {
1136 let test_file = test_file();
1137 assert!(match &test_file.instruments[0] {
1139 Instrument::None => true,
1140 _ => false,
1141 });
1142 assert!(
1143 match &test_file.instruments[1] {
1144 Instrument::WavSynth(s) => {
1145 assert_eq!(s.transpose, true);
1146 assert_eq!(s.size, 0x20);
1147 assert_eq!(s.synth_params.mixer_reverb, 0xD0);
1148 assert!(match s.synth_params.mods[0] {
1149 Mod::AHDEnv(_) => true,
1150 _ => false,
1151 });
1152 assert!(match s.synth_params.mods[1] {
1153 Mod::ADSREnv(_) => true,
1154 _ => false,
1155 });
1156 assert!(match s.synth_params.mods[2] {
1157 Mod::DrumEnv(_) => true,
1158 _ => false,
1159 });
1160 assert!(match s.synth_params.mods[3] {
1161 Mod::LFO(_) => true,
1162 _ => false,
1163 });
1164
1165 true
1166 }
1167 _ => false,
1168 },
1169 "Should be a WavSynth"
1170 );
1171 assert!(match &test_file.instruments[2] {
1172 Instrument::MacroSynth(s) => {
1173 assert_eq!(s.transpose, false);
1174 assert!(match s.synth_params.mods[0] {
1175 Mod::TrigEnv(_) => true,
1176 _ => false,
1177 });
1178 assert!(match s.synth_params.mods[1] {
1179 Mod::TrackingEnv(_) => true,
1180 _ => false,
1181 });
1182 assert!(match s.synth_params.mods[2] {
1183 Mod::LFO(_) => true,
1184 _ => false,
1185 });
1186 assert!(match s.synth_params.mods[3] {
1187 Mod::LFO(_) => true,
1188 _ => false,
1189 });
1190
1191 true
1192 }
1193 _ => false,
1194 });
1195 assert!(match &test_file.instruments[3] {
1196 Instrument::Sampler(s) => {
1197 assert!(match s.synth_params.mods[0] {
1198 Mod::AHDEnv(_) => true,
1199 _ => false,
1200 });
1201 assert!(match s.synth_params.mods[1] {
1202 Mod::AHDEnv(_) => true,
1203 _ => false,
1204 });
1205 assert!(match s.synth_params.mods[2] {
1206 Mod::LFO(_) => true,
1207 _ => false,
1208 });
1209 assert!(match s.synth_params.mods[3] {
1210 Mod::LFO(_) => true,
1211 _ => false,
1212 });
1213
1214 assert_eq!(&s.name, "SAMP");
1215 assert_eq!(
1216 &s.sample_path,
1217 "/Samples/Drums/Hits/TR505/bass drum 505.wav"
1218 );
1219
1220 true
1221 }
1222 _ => false,
1223 });
1224 assert!(match &test_file.instruments[4] {
1225 Instrument::FMSynth(s) => {
1226 assert!(match s.synth_params.mods[0] {
1227 Mod::AHDEnv(_) => true,
1228 _ => false,
1229 });
1230 assert!(match s.synth_params.mods[1] {
1231 Mod::AHDEnv(_) => true,
1232 _ => false,
1233 });
1234 assert!(match s.synth_params.mods[2] {
1235 Mod::LFO(_) => true,
1236 _ => false,
1237 });
1238 assert!(match s.synth_params.mods[3] {
1239 Mod::LFO(_) => true,
1240 _ => false,
1241 });
1242
1243 true
1244 }
1245 _ => false,
1246 });
1247 assert!(match &test_file.instruments[5] {
1248 Instrument::HyperSynth(s) => {
1249 assert!(match s.synth_params.mods[0] {
1250 Mod::AHDEnv(_) => true,
1251 _ => false,
1252 });
1253 assert!(match s.synth_params.mods[1] {
1254 Mod::AHDEnv(_) => true,
1255 _ => false,
1256 });
1257 assert!(match s.synth_params.mods[2] {
1258 Mod::LFO(_) => true,
1259 _ => false,
1260 });
1261 assert!(match s.synth_params.mods[3] {
1262 Mod::LFO(_) => true,
1263 _ => false,
1264 });
1265 assert_eq!(s.scale, 0xFF);
1266 assert_eq!(s.default_chord[0], 0x01);
1267 assert_eq!(s.default_chord[6], 0x3C);
1268
1269 true
1270 }
1271 _ => false,
1272 });
1273 assert!(match &test_file.instruments[6] {
1274 Instrument::MIDIOut(s) => {
1275 assert!(match s.mods.mods[0] {
1276 Mod::AHDEnv(_) => true,
1277 _ => false,
1278 });
1279 assert!(match s.mods.mods[1] {
1280 Mod::AHDEnv(_) => true,
1281 _ => false,
1282 });
1283 assert!(match s.mods.mods[2] {
1284 Mod::LFO(_) => true,
1285 _ => false,
1286 });
1287 assert!(match s.mods.mods[3] {
1288 Mod::LFO(_) => true,
1289 _ => false,
1290 });
1291 true
1292 }
1293 _ => false,
1294 });
1295 assert!(match &test_file.instruments[7] {
1296 Instrument::External(s) => {
1297 assert!(match s.synth_params.mods[0] {
1298 Mod::AHDEnv(_) => true,
1299 _ => false,
1300 });
1301 assert!(match s.synth_params.mods[1] {
1302 Mod::AHDEnv(_) => true,
1303 _ => false,
1304 });
1305 assert!(match s.synth_params.mods[2] {
1306 Mod::LFO(_) => true,
1307 _ => false,
1308 });
1309 assert!(match s.synth_params.mods[3] {
1310 Mod::LFO(_) => true,
1311 _ => false,
1312 });
1313
1314 assert_eq!(s.cca.number, 1);
1315 assert_eq!(s.ccb.number, 2);
1316 assert_eq!(s.ccd.number, 4);
1317 true
1318 }
1319 _ => false,
1320 });
1321 }
1322
1323 #[test]
1324 fn test_mixer_reading() {
1325 let test_file = test_file();
1326 assert_eq!(test_file.mixer_settings.track_volume[0], 0xE0);
1328 assert_eq!(test_file.mixer_settings.track_volume[7], 0xE0);
1329 assert_eq!(test_file.mixer_settings.dj_filter, 0x80);
1330 assert_eq!(test_file.mixer_settings.dj_filter_type, 0x02);
1331 }
1332
1333 #[test]
1334 fn test_song_reading() {
1335 let test_file = test_file();
1336 assert_eq!(test_file.name, "TEST-FILE");
1338 assert_eq!(test_file.tempo, 120.0);
1339 assert_eq!(test_file.transpose, 0x0C);
1340 assert_eq!(test_file.quantize, 0x02);
1341 }
1342}