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