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