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.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        // 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.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////////////////////////////////////////////////////////////////////////////////////
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    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////////////////////////////////////////////////////////////////////////////////////
776// MARK: Note
777////////////////////////////////////////////////////////////////////////////////////
778#[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") // This isn't really true for < V3
815        } 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////////////////////////////////////////////////////////////////////////////////////
824// MARK: Table
825////////////////////////////////////////////////////////////////////////////////////
826#[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////////////////////////////////////////////////////////////////////////////////////
1084// MARK: Groove
1085////////////////////////////////////////////////////////////////////////////////////
1086#[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////////////////////////////////////////////////////////////////////////////////////
1122// MARK: Tests
1123////////////////////////////////////////////////////////////////////////////////////
1124#[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        // dbg!(&test_file.instruments[0..8]);
1138        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        // dbg!(&test_file.mixer_settings);
1327        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        // dbg!(&test_file);
1337        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}