m8_file_parser/
songs.rs

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    /// Number of eq for the song (different between 4.0 & 4.1)
36    pub instrument_eq_count: usize,
37
38    /// For instrument size, where is the EQ information written
39    /// (if any)
40    pub instrument_file_eq_offset: Option<usize>,
41}
42
43impl Offsets {
44    pub fn eq_count(&self) -> usize {
45        // general EQ + 3 for effects + 1 global
46        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////////////////////////////////////////////////////////////////////////////////////
81// MARK: Song
82////////////////////////////////////////////////////////////////////////////////////
83#[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.after(&FIRMWARE_5_0_SONG_VERSION) {
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.after(&FIRMWARE_4_0_SONG_VERSION) {
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        // TODO read groove, scale
263        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); // Skip
271        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.after(&FIRMWARE_4_0_SONG_VERSION) {
316            let ofs = if version.after(&FIRMWARE_5_0_SONG_VERSION ) {
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////////////////////////////////////////////////////////////////////////////////////
355// MARK: SongSteps
356////////////////////////////////////////////////////////////////////////////////////
357#[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////////////////////////////////////////////////////////////////////////////////////
436// MARK: Chains
437////////////////////////////////////////////////////////////////////////////////////
438#[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////////////////////////////////////////////////////////////////////////////////////
562// MARK: Phrase
563////////////////////////////////////////////////////////////////////////////////////
564#[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    /// Initialize an empty phrase with a given version
578    pub fn default_ver(version: Version) -> Phrase {
579        Phrase {
580            version,
581            steps: [
582                Step::default(),
583                Step::default(),
584                Step::default(),
585                Step::default(),
586
587                Step::default(),
588                Step::default(),
589                Step::default(),
590                Step::default(),
591
592                Step::default(),
593                Step::default(),
594                Step::default(),
595                Step::default(),
596
597                Step::default(),
598                Step::default(),
599                Step::default(),
600                Step::default(),
601            ]
602        }
603    }
604
605    pub fn clear(&mut self) {
606        for s in &mut self.steps {
607            s.clear();
608        }
609    }
610
611    pub(crate) fn print_screen(
612        &self,
613        f: &mut fmt::Formatter<'_>,
614        instruments: &[Instrument],
615        templates: &ReferenceTemplating) -> std::fmt::Result {
616        let mut cmd_pack = CommandPack::default();
617        let fx_commands = FX::fx_command_names(self.version);
618
619        write!(f, "  N   V  I  FX1   FX2   FX3  \n")?;
620
621        for i in 0..16 {
622            let step = &self.steps[i];
623            let instrument = step.instrument as usize;
624
625            if instrument < Song::N_INSTRUMENTS {
626                cmd_pack = instruments[instrument].instr_command_text(self.version);
627            }
628
629            step.print(f, i as u8, fx_commands, cmd_pack, templates)?;
630            write!(f, "\n")?;
631        }
632
633        Ok(())
634    }
635
636    pub fn map_instruments(
637        &self,
638        instrument_mapping: &InstrumentMapping,
639        table_mapping: &TableMapping,
640        eq_mapping: &EqMapping,
641    ) -> Self {
642        let mut steps = self.steps.clone();
643        for i in 0..steps.len() {
644            steps[i] = steps[i].map_instr(&instrument_mapping, table_mapping, eq_mapping);
645        }
646
647        Self {
648            steps,
649            version: self.version,
650        }
651    }
652
653    pub fn write(&self, w: &mut Writer) {
654        for s in &self.steps {
655            s.write(w);
656        }
657    }
658
659    pub fn from_reader(reader: &mut Reader, version: Version) -> M8Result<Self> {
660        Ok(Self {
661            steps: arr![Step::from_reader(reader)?; 16],
662            version,
663        })
664    }
665}
666
667pub struct PhraseView<'a> {
668    phrase: &'a Phrase,
669    phrase_id: usize,
670    instruments: &'a [Instrument],
671    templates: ReferenceTemplating
672}
673
674impl<'a> fmt::Display for PhraseView<'a> {
675    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
676        write!(f, "PHRASE {:02X}\n\n", self.phrase_id)?;
677        self.phrase.print_screen(f, self.instruments, &self.templates)
678    }
679}
680
681impl<'a> fmt::Debug for PhraseView<'a> {
682    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683        write!(f, "{}", &self)
684    }
685}
686
687#[derive(PartialEq, Debug, Clone, Default)]
688pub struct Step {
689    pub note: Note,
690    pub velocity: u8,
691    pub instrument: u8,
692    pub fx1: FX,
693    pub fx2: FX,
694    pub fx3: FX,
695}
696
697impl Step {
698    pub const V4_SIZE: usize = 3 + 3 * FX::V4_SIZE;
699
700    pub fn all_fx(&self) -> [FX; 3] {
701        [self.fx1, self.fx2, self.fx3]
702    }
703
704    pub fn print(&self,
705        f: &mut fmt::Formatter<'_>,
706        row: u8,
707        fx_cmds: FxCommands,
708        cmd_pack: CommandPack,
709        templates: &ReferenceTemplating) -> std::fmt::Result {
710        let velocity = if self.velocity == 255 {
711            format!("--")
712        } else {
713            format!("{:02x}", self.velocity)
714        };
715
716        let instrument = if self.instrument == 255 {
717            format!("--")
718        } else {
719            templates.try_instrument_templating(self.instrument)
720                .unwrap_or_else(|| format!("{:02x}", self.instrument))
721        };
722
723        write!(
724            f,
725            "{:x} {} {} {} {} {} {}",
726            row,
727            self.note,
728            velocity,
729            instrument,
730            self.fx1.print(fx_cmds, cmd_pack, templates),
731            self.fx2.print(fx_cmds, cmd_pack, templates),
732            self.fx3.print(fx_cmds, cmd_pack, templates)
733        )
734    }
735
736    pub fn clear(&mut self) {
737        self.note = Note::default();
738        self.velocity = 0xFF;
739        self.instrument = 0xFF;
740        self.fx1 = FX::default();
741        self.fx2 = FX::default();
742        self.fx3 = FX::default();
743    }
744
745    pub fn is_empty(&self) -> bool {
746        self.note.is_empty()
747            && self.velocity == 0xFF
748            && self.instrument == 0xFF
749            && self.fx1.is_empty()
750            && self.fx2.is_empty()
751            && self.fx3.is_empty()
752    }
753
754    pub fn map_instr(
755        &self,
756        instrument_mapping: &InstrumentMapping,
757        table_mapping: &TableMapping,
758        eq_mapping: &EqMapping,
759    ) -> Step {
760        let instrument = if (self.instrument as usize) >= Song::N_INSTRUMENTS {
761            self.instrument
762        } else {
763            instrument_mapping.mapping[self.instrument as usize]
764        };
765
766        Self {
767            note: self.note,
768            velocity: self.velocity,
769            instrument,
770            fx1: self
771                .fx1
772                .map_instr(instrument_mapping, table_mapping, eq_mapping),
773            fx2: self
774                .fx2
775                .map_instr(instrument_mapping, table_mapping, eq_mapping),
776            fx3: self
777                .fx3
778                .map_instr(instrument_mapping, table_mapping, eq_mapping),
779        }
780    }
781
782    pub fn write(&self, w: &mut Writer) {
783        w.write(self.note.0);
784        w.write(self.velocity);
785        w.write(self.instrument);
786        self.fx1.write(w);
787        self.fx2.write(w);
788        self.fx3.write(w);
789    }
790
791    fn from_reader(reader: &mut Reader) -> M8Result<Self> {
792        Ok(Self {
793            note: Note(reader.read()),
794            velocity: reader.read(),
795            instrument: reader.read(),
796            fx1: FX::from_reader(reader)?,
797            fx2: FX::from_reader(reader)?,
798            fx3: FX::from_reader(reader)?,
799        })
800    }
801}
802
803////////////////////////////////////////////////////////////////////////////////////
804// MARK: Note
805////////////////////////////////////////////////////////////////////////////////////
806#[derive(PartialEq, Debug, Clone, Copy)]
807pub struct Note(pub u8);
808
809impl Note {
810    pub fn is_empty(self) -> bool {
811        self.0 == 0xFF
812    }
813}
814
815impl Default for Note {
816    fn default() -> Self {
817        Note(255)
818    }
819}
820
821const NOTES : [&'static str; 12] =
822    [
823        "C-",
824        "C#",
825        "D-",
826        "D#",
827        "E-",
828        "F-",
829        "F#",
830        "G-",
831        "G#",
832        "A-",
833        "A#",
834        "B-"
835    ];
836
837impl fmt::Display for Note {
838    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
839        if self.0 == 255 {
840            write!(f, "---")
841        } else if self.0 >= 0x80 {
842            write!(f, "OFF") // This isn't really true for < V3
843        } else {
844            let oct = (self.0 / 12) + 1;
845            let n = NOTES[(self.0 % 12) as usize];
846            write!(f, "{}{:X}", n, oct)
847        }
848    }
849}
850
851////////////////////////////////////////////////////////////////////////////////////
852// MARK: Table
853////////////////////////////////////////////////////////////////////////////////////
854#[derive(PartialEq, Clone)]
855pub struct Table {
856    pub steps: [TableStep; 16],
857    version: Version,
858}
859impl Table {
860    pub const V4_SIZE: usize = 16 * TableStep::V4_SIZE;
861
862    /// Initialize an empty table with a given version
863    pub fn default_ver(version: Version) -> Table {
864        Table {
865            version,
866            steps: [
867                TableStep::default(),
868                TableStep::default(),
869                TableStep::default(),
870                TableStep::default(),
871
872                TableStep::default(),
873                TableStep::default(),
874                TableStep::default(),
875                TableStep::default(),
876
877                TableStep::default(),
878                TableStep::default(),
879                TableStep::default(),
880                TableStep::default(),
881
882                TableStep::default(),
883                TableStep::default(),
884                TableStep::default(),
885                TableStep::default(),
886            ]
887        }
888    }
889
890    pub fn is_empty(&self) -> bool {
891        self.steps.iter().all(|s| s.is_empty())
892    }
893
894    pub fn clear(&mut self) {
895        let dflt = TableStep::default();
896
897        for s in &mut self.steps {
898            *s = dflt.clone();
899        }
900    }
901
902    pub fn map_instr(
903        &self,
904        instr_mapping: &InstrumentMapping,
905        table_mapping: &TableMapping,
906        eq_mapping: &EqMapping,
907    ) -> Self {
908        let mut steps = self.steps.clone();
909
910        for i in 0..16 {
911            steps[i] = steps[i].map_instr(instr_mapping, table_mapping, eq_mapping);
912        }
913
914        Self {
915            version: self.version,
916            steps,
917        }
918    }
919
920    pub(crate) fn print_screen(
921        &self,
922        f: &mut fmt::Formatter<'_>,
923        cmd: CommandPack,
924        templates: &ReferenceTemplating) -> std::fmt::Result {
925        let fx_cmd = FX::fx_command_names(self.version);
926        write!(f, "  N  V  FX1   FX2   FX3  \n")?;
927
928        for i in 0..16 {
929            let step = &self.steps[i];
930
931            step.print(f, i as u8, fx_cmd, cmd, templates)?;
932            write!(f, "\n")?
933        }
934
935        Ok(())
936    }
937
938    pub fn write(&self, w: &mut Writer) {
939        for ts in &self.steps {
940            ts.write(w);
941        }
942    }
943
944    pub fn from_reader(reader: &mut Reader, version: Version) -> M8Result<Self> {
945        Ok(Self {
946            steps: arr![TableStep::from_reader(reader)?; 16],
947            version,
948        })
949    }
950}
951
952pub enum ReferenceTemplating {
953  NoTemplate,
954  WithTemplates {
955    instrument: Option<String>,
956    instrument_command: Option<String>,
957    table: Option<String>,
958    eq: Option<String>,
959  }
960}
961
962impl ReferenceTemplating {
963    fn apply_cmd_template(template: &str, cmd: &str, value: u8) -> Option<String> {
964        Some(template
965            .replace("{HEX}", &format!("{:02X}", value))
966            .replace("{CMD}", cmd))
967    }
968
969    pub fn try_instrument_templating(&self, value: u8) -> Option<String> {
970        match self {
971            ReferenceTemplating::NoTemplate => None,
972            ReferenceTemplating::WithTemplates { instrument: Some(it), instrument_command: _, table: _, eq: _ } => {
973                Some(it.replace("{HEX}", &format!("{:02X}", value)))
974            }
975            ReferenceTemplating::WithTemplates { instrument: _, instrument_command: _, table: _, eq: _ } => None
976        }
977    }
978
979    pub fn try_template(&self, command: &str, value: u8) -> Option<String> {
980        let (instr, table, eqq) = match self {
981            ReferenceTemplating::NoTemplate => return None,
982            ReferenceTemplating::WithTemplates {
983                instrument: _,
984                instrument_command,
985                table,
986                eq } =>
987                (instrument_command, table, eq)
988        };
989
990        if let Some(it) = instr {
991          if INSTRUMENT_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
992            return ReferenceTemplating::apply_cmd_template(&it, command, value)
993          }
994        }
995
996        if let Some(tt) = table {
997          if TABLE_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
998            return ReferenceTemplating::apply_cmd_template(&tt, command, value)
999          }
1000        }
1001
1002        if let Some(et) = eqq {
1003          if EQ_TRACKING_COMMAND_NAMES.iter().any(|ic| *ic == command) {
1004            return ReferenceTemplating::apply_cmd_template(&et, command, value)
1005          }
1006        }
1007
1008        None
1009    }
1010}
1011
1012impl Default for ReferenceTemplating {
1013    fn default() -> Self { Self::NoTemplate }
1014}
1015
1016pub struct TableView<'a> {
1017    pub(crate) table: &'a Table,
1018    pub(crate) table_index: usize,
1019    pub(crate) instrument: CommandPack,
1020
1021    pub(crate) templates: ReferenceTemplating,
1022}
1023
1024impl<'a> fmt::Display for TableView<'a> {
1025    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1026        write!(f, "TABLE {:02X}\n\n", self.table_index)?;
1027        self.table.print_screen(f,self.instrument, &self.templates)
1028    }
1029}
1030
1031impl<'a> fmt::Debug for TableView<'a> {
1032    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1033        write!(f, "{}", &self)
1034    }
1035}
1036
1037#[derive(PartialEq, Debug, Clone)]
1038pub struct TableStep {
1039    pub transpose: u8,
1040    pub velocity: u8,
1041    pub fx1: FX,
1042    pub fx2: FX,
1043    pub fx3: FX,
1044}
1045
1046impl Default for TableStep {
1047    fn default() -> Self {
1048        Self {
1049            transpose: 0,
1050            velocity: 0xFF,
1051            fx1: Default::default(),
1052            fx2: Default::default(),
1053            fx3: Default::default(),
1054        }
1055    }
1056}
1057
1058impl TableStep {
1059    pub const V4_SIZE: usize = 2 + 3 * FX::V4_SIZE;
1060
1061    pub fn all_fx(&self) -> [FX; 3] {
1062        [self.fx1, self.fx2, self.fx3]
1063    }
1064
1065    pub fn map_instr(
1066        &self,
1067        instr_mapping: &InstrumentMapping,
1068        table_mapping: &TableMapping,
1069        eq_mapping: &EqMapping,
1070    ) -> TableStep {
1071        Self {
1072            transpose: self.transpose,
1073            velocity: self.velocity,
1074            fx1: self.fx1.map_instr(instr_mapping, table_mapping, eq_mapping),
1075            fx2: self.fx2.map_instr(instr_mapping, table_mapping, eq_mapping),
1076            fx3: self.fx3.map_instr(instr_mapping, table_mapping, eq_mapping),
1077        }
1078    }
1079
1080    pub fn is_empty(&self) -> bool {
1081        self.transpose == 0
1082            && self.velocity == 0xFF
1083            && self.fx1.is_empty()
1084            && self.fx2.is_empty()
1085            && self.fx3.is_empty()
1086    }
1087
1088    pub fn print(
1089        &self,
1090        f: &mut fmt::Formatter<'_>,
1091        row: u8,
1092        fx_cmd: FxCommands,
1093        cmds: CommandPack,
1094        templates: &ReferenceTemplating) -> std::fmt::Result {
1095
1096        let transpose = if self.transpose == 255 {
1097            format!("--")
1098        } else {
1099            format!("{:02x}", self.transpose)
1100        };
1101
1102        let velocity = if self.velocity == 255 {
1103            format!("--")
1104        } else {
1105            format!("{:02x}", self.velocity)
1106        };
1107
1108        write!(
1109            f,
1110            "{:x} {} {} {} {} {}",
1111            row,
1112            transpose,
1113            velocity,
1114            self.fx1.print(fx_cmd, cmds, templates),
1115            self.fx2.print(fx_cmd, cmds, templates),
1116            self.fx3.print(fx_cmd, cmds, templates)
1117        )
1118    }
1119
1120    pub fn write(&self, w: &mut Writer) {
1121        w.write(self.transpose);
1122        w.write(self.velocity);
1123        self.fx1.write(w);
1124        self.fx2.write(w);
1125        self.fx3.write(w);
1126    }
1127
1128    fn from_reader(reader: &mut Reader) -> M8Result<Self> {
1129        Ok(Self {
1130            transpose: reader.read(),
1131            velocity: reader.read(),
1132            fx1: FX::from_reader(reader)?,
1133            fx2: FX::from_reader(reader)?,
1134            fx3: FX::from_reader(reader)?,
1135        })
1136    }
1137}
1138
1139////////////////////////////////////////////////////////////////////////////////////
1140// MARK: Groove
1141////////////////////////////////////////////////////////////////////////////////////
1142#[derive(PartialEq, Clone)]
1143pub struct Groove {
1144    pub number: u8,
1145    pub steps: [u8; 16],
1146}
1147
1148impl Groove {
1149    fn from_reader(reader: &mut Reader, number: u8) -> M8Result<Self> {
1150        Ok(Self {
1151            number,
1152            steps: reader.read_bytes(16).try_into().unwrap(),
1153        })
1154    }
1155
1156    pub fn write(&self, w: &mut Writer) {
1157        w.write_bytes(&self.steps);
1158    }
1159
1160    pub fn active_steps(&self) -> &[u8] {
1161        let end = (&self.steps).iter().position(|&x| x == 255).unwrap_or(15);
1162        &self.steps[0..end]
1163    }
1164}
1165
1166impl fmt::Display for Groove {
1167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1168        write!(f, "Groove {}:{:?}", self.number, self.active_steps())
1169    }
1170}
1171impl fmt::Debug for Groove {
1172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1173        write!(f, "{}", &self)
1174    }
1175}
1176
1177////////////////////////////////////////////////////////////////////////////////////
1178// MARK: Tests
1179////////////////////////////////////////////////////////////////////////////////////
1180#[cfg(test)]
1181mod tests {
1182    use crate::songs::*;
1183    use std::fs::File;
1184
1185    fn test_file() -> Song {
1186        let mut f = File::open("./examples/songs/TEST-FILE.m8s").expect("Could not open TEST-FILE");
1187        Song::read(&mut f).expect("Could not parse TEST-FILE")
1188    }
1189
1190    #[test]
1191    fn test_instrument_reading() {
1192        let test_file = test_file();
1193        // dbg!(&test_file.instruments[0..8]);
1194        assert!(match &test_file.instruments[0] {
1195            Instrument::None => true,
1196            _ => false,
1197        });
1198        assert!(
1199            match &test_file.instruments[1] {
1200                Instrument::WavSynth(s) => {
1201                    assert_eq!(s.transpose, true);
1202                    assert_eq!(s.size, 0x20);
1203                    assert_eq!(s.synth_params.mixer_reverb, 0xD0);
1204                    assert!(match s.synth_params.mods[0] {
1205                        Mod::AHDEnv(_) => true,
1206                        _ => false,
1207                    });
1208                    assert!(match s.synth_params.mods[1] {
1209                        Mod::ADSREnv(_) => true,
1210                        _ => false,
1211                    });
1212                    assert!(match s.synth_params.mods[2] {
1213                        Mod::DrumEnv(_) => true,
1214                        _ => false,
1215                    });
1216                    assert!(match s.synth_params.mods[3] {
1217                        Mod::LFO(_) => true,
1218                        _ => false,
1219                    });
1220
1221                    true
1222                }
1223                _ => false,
1224            },
1225            "Should be a WavSynth"
1226        );
1227        assert!(match &test_file.instruments[2] {
1228            Instrument::MacroSynth(s) => {
1229                assert_eq!(s.transpose, false);
1230                assert!(match s.synth_params.mods[0] {
1231                    Mod::TrigEnv(_) => true,
1232                    _ => false,
1233                });
1234                assert!(match s.synth_params.mods[1] {
1235                    Mod::TrackingEnv(_) => true,
1236                    _ => false,
1237                });
1238                assert!(match s.synth_params.mods[2] {
1239                    Mod::LFO(_) => true,
1240                    _ => false,
1241                });
1242                assert!(match s.synth_params.mods[3] {
1243                    Mod::LFO(_) => true,
1244                    _ => false,
1245                });
1246
1247                true
1248            }
1249            _ => false,
1250        });
1251        assert!(match &test_file.instruments[3] {
1252            Instrument::Sampler(s) => {
1253                assert!(match s.synth_params.mods[0] {
1254                    Mod::AHDEnv(_) => true,
1255                    _ => false,
1256                });
1257                assert!(match s.synth_params.mods[1] {
1258                    Mod::AHDEnv(_) => true,
1259                    _ => false,
1260                });
1261                assert!(match s.synth_params.mods[2] {
1262                    Mod::LFO(_) => true,
1263                    _ => false,
1264                });
1265                assert!(match s.synth_params.mods[3] {
1266                    Mod::LFO(_) => true,
1267                    _ => false,
1268                });
1269
1270                assert_eq!(&s.name, "SAMP");
1271                assert_eq!(
1272                    &s.sample_path,
1273                    "/Samples/Drums/Hits/TR505/bass drum 505.wav"
1274                );
1275
1276                true
1277            }
1278            _ => false,
1279        });
1280        assert!(match &test_file.instruments[4] {
1281            Instrument::FMSynth(s) => {
1282                assert!(match s.synth_params.mods[0] {
1283                    Mod::AHDEnv(_) => true,
1284                    _ => false,
1285                });
1286                assert!(match s.synth_params.mods[1] {
1287                    Mod::AHDEnv(_) => true,
1288                    _ => false,
1289                });
1290                assert!(match s.synth_params.mods[2] {
1291                    Mod::LFO(_) => true,
1292                    _ => false,
1293                });
1294                assert!(match s.synth_params.mods[3] {
1295                    Mod::LFO(_) => true,
1296                    _ => false,
1297                });
1298
1299                true
1300            }
1301            _ => false,
1302        });
1303        assert!(match &test_file.instruments[5] {
1304            Instrument::HyperSynth(s) => {
1305                assert!(match s.synth_params.mods[0] {
1306                    Mod::AHDEnv(_) => true,
1307                    _ => false,
1308                });
1309                assert!(match s.synth_params.mods[1] {
1310                    Mod::AHDEnv(_) => true,
1311                    _ => false,
1312                });
1313                assert!(match s.synth_params.mods[2] {
1314                    Mod::LFO(_) => true,
1315                    _ => false,
1316                });
1317                assert!(match s.synth_params.mods[3] {
1318                    Mod::LFO(_) => true,
1319                    _ => false,
1320                });
1321                assert_eq!(s.scale, 0xFF);
1322                assert_eq!(s.default_chord[0], 0x01);
1323                assert_eq!(s.default_chord[6], 0x3C);
1324
1325                true
1326            }
1327            _ => false,
1328        });
1329        assert!(match &test_file.instruments[6] {
1330            Instrument::MIDIOut(s) => {
1331                assert!(match s.mods.mods[0] {
1332                    Mod::AHDEnv(_) => true,
1333                    _ => false,
1334                });
1335                assert!(match s.mods.mods[1] {
1336                    Mod::AHDEnv(_) => true,
1337                    _ => false,
1338                });
1339                assert!(match s.mods.mods[2] {
1340                    Mod::LFO(_) => true,
1341                    _ => false,
1342                });
1343                assert!(match s.mods.mods[3] {
1344                    Mod::LFO(_) => true,
1345                    _ => false,
1346                });
1347                true
1348            }
1349            _ => false,
1350        });
1351        assert!(match &test_file.instruments[7] {
1352            Instrument::External(s) => {
1353                assert!(match s.synth_params.mods[0] {
1354                    Mod::AHDEnv(_) => true,
1355                    _ => false,
1356                });
1357                assert!(match s.synth_params.mods[1] {
1358                    Mod::AHDEnv(_) => true,
1359                    _ => false,
1360                });
1361                assert!(match s.synth_params.mods[2] {
1362                    Mod::LFO(_) => true,
1363                    _ => false,
1364                });
1365                assert!(match s.synth_params.mods[3] {
1366                    Mod::LFO(_) => true,
1367                    _ => false,
1368                });
1369
1370                assert_eq!(s.cca.number, 1);
1371                assert_eq!(s.ccb.number, 2);
1372                assert_eq!(s.ccd.number, 4);
1373                true
1374            }
1375            _ => false,
1376        });
1377    }
1378
1379    #[test]
1380    fn test_mixer_reading() {
1381        let test_file = test_file();
1382        // dbg!(&test_file.mixer_settings);
1383        assert_eq!(test_file.mixer_settings.track_volume[0], 0xE0);
1384        assert_eq!(test_file.mixer_settings.track_volume[7], 0xE0);
1385        assert_eq!(test_file.mixer_settings.dj_filter, 0x80);
1386        assert_eq!(test_file.mixer_settings.dj_filter_type, 0x02);
1387    }
1388
1389    #[test]
1390    fn test_song_reading() {
1391        let test_file = test_file();
1392        // dbg!(&test_file);
1393        assert_eq!(test_file.name, "TEST-FILE");
1394        assert_eq!(test_file.tempo, 120.0);
1395        assert_eq!(test_file.transpose, 0x0C);
1396        assert_eq!(test_file.quantize, 0x02);
1397    }
1398}