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 pub instrument_eq_count: usize,
34
35 pub instrument_file_eq_offset: Option<usize>,
38}
39
40impl Offsets {
41 pub fn eq_count(&self) -> usize {
42 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#[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 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); 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); 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#[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#[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#[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#[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") } 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#[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#[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#[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 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 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 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}