m8_files/
songs.rs

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