1use crate::reader::*;
2use crate::version::*;
3
4use std::fmt;
5
6use arr_macro::arr;
7use byteorder::{ByteOrder, LittleEndian};
8
9#[derive(PartialEq, Clone)]
10pub struct Scale {
11 pub number: u8,
12 pub name: String,
13 pub notes: [NoteOffset; 12], }
15
16impl Scale {
17 const SIZE: usize = 32;
18
19 pub fn read(reader: &mut impl std::io::Read) -> M8Result<Self> {
20 let mut buf: Vec<u8> = vec![];
21 reader.read_to_end(&mut buf).unwrap();
22 let len = buf.len();
23 let mut reader = Reader::new(buf);
24
25 if len < Self::SIZE + Version::SIZE {
26 return Err(ParseError(
27 "File is not long enough to be a M8 Scale".to_string(),
28 ));
29 }
30 Version::from_reader(&mut reader)?;
31 Self::from_reader(&mut reader, 0)
32 }
33
34 pub(crate) fn from_reader(reader: &mut Reader, number: u8) -> M8Result<Self> {
35 let map = LittleEndian::read_u16(reader.read_bytes(2));
36 let mut notes = arr![NoteOffset::default(); 12];
37
38 for (i, note) in notes.iter_mut().enumerate() {
39 note.enabled = ((map >> i) & 0x1) == 1;
40 let offset = f32::from(reader.read()) + (f32::from(reader.read()) / 100.0);
41 note.semitones = offset;
42 }
43
44 let name = reader.read_string(16);
45 Ok(Self {
46 number,
47 name,
48 notes,
49 })
50 }
51}
52
53impl Default for Scale {
54 fn default() -> Self {
55 Self {
56 number: 0,
57 name: "CHROMATIC".to_string(),
58 notes: arr![NoteOffset::default(); 12],
59 }
60 }
61}
62
63impl fmt::Display for Scale {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 let notes = vec![
66 "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
67 ];
68 let offsets = self
69 .notes
70 .iter()
71 .zip(notes.iter())
72 .map(|(offset, note)| -> String {
73 let s = if offset.enabled {
74 let sign = if offset.semitones < 0.0 { "-" } else { " " };
75 format!(" ON{}{:02.2}", sign, offset.semitones.abs())
76 } else {
77 " -- -- --".to_string()
78 };
79 format!("{:<2}{}", note, &s)
80 })
81 .collect::<Vec<String>>()
82 .join("\n");
83
84 write!(
85 f,
86 "Scale {}\nKEY C\n\n EN OFFSET\n{}\n\nNAME {}",
87 self.number, offsets, &self.name
88 )
89 }
90}
91impl fmt::Debug for Scale {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "{}", &self)
94 }
95}
96
97#[derive(PartialEq, Debug, Clone, Copy)]
98pub struct NoteOffset {
99 pub enabled: bool,
100 pub semitones: f32, }
102impl NoteOffset {
103 fn default() -> Self {
104 Self {
105 enabled: true,
106 semitones: 0.0,
107 }
108 }
109}