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