1use std::fmt;
2use std::sync::atomic::{AtomicU8, Ordering};
3
4use midly::MidiMessage;
5use nodi::{Event, Moment};
6
7static STYLE: AtomicU8 = AtomicU8::new(1);
8
9pub fn toggle_style() {
10 STYLE
11 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |n| {
12 Some(n.wrapping_add(1) % (NoteStyle::VALUES.len() as u8))
13 })
14 .unwrap();
15}
16
17pub fn style() -> NoteStyle {
18 NoteStyle::VALUES[STYLE.load(Ordering::Relaxed) as usize]
19}
20
21struct NoteName {
22 abc: &'static str,
23 doremi: &'static str,
24}
25
26const NOTES: [NoteName; 12] = [
27 NoteName {
28 abc: "C",
29 doremi: "Do",
30 },
31 NoteName {
32 abc: "C#",
33 doremi: "Do#",
34 },
35 NoteName {
36 abc: "D",
37 doremi: "Re",
38 },
39 NoteName {
40 abc: "E♭",
41 doremi: "Mi♭",
42 },
43 NoteName {
44 abc: "E",
45 doremi: "Mi",
46 },
47 NoteName {
48 abc: "F",
49 doremi: "Fa",
50 },
51 NoteName {
52 abc: "F#",
53 doremi: "Fa#",
54 },
55 NoteName {
56 abc: "G",
57 doremi: "Sol",
58 },
59 NoteName {
60 abc: "A♭",
61 doremi: "La♭",
62 },
63 NoteName {
64 abc: "A",
65 doremi: "La",
66 },
67 NoteName {
68 abc: "B♭",
69 doremi: "Si♭",
70 },
71 NoteName {
72 abc: "B",
73 doremi: "Si",
74 },
75];
76
77#[derive(Copy, Clone, PartialEq, Eq)]
78pub struct Note {
79 offset: u8,
80 octave: u8,
81}
82
83impl From<u8> for Note {
84 fn from(n: u8) -> Self {
85 let offset = n % 12;
86 let octave = if offset == 0 { n / 12 } else { n / 12 + 1 };
87 Self { offset, octave }
88 }
89}
90
91impl fmt::Display for Note {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 style().display_note(*self, f)
94 }
95}
96
97pub fn moment_notes(moment: &Moment, shift: i8) -> Option<Vec<Note>> {
98 match &moment {
99 Moment::Empty => None,
100 Moment::Events(events) => {
101 let mut buf = Vec::new();
102 for e in events {
103 if let Event::Midi(m) = e {
104 if m.channel == 9 {
105 continue;
107 }
108 match m.message {
109 MidiMessage::NoteOn { key, vel } if vel > 0 => {
110 let key = key.as_int() as i32 + shift as i32;
111 if (0..=127).contains(&key) {
112 let k = Note::from(key as u8);
113 if !buf.contains(&k) {
114 buf.push(k);
115 }
116 }
117 }
118 _ => {}
119 };
120 }
121 }
122
123 if buf.is_empty() {
124 None
125 } else {
126 Some(buf)
127 }
128 }
129 }
130}
131
132#[derive(Copy, Clone)]
133pub enum NoteStyle {
134 Abc,
136 AbcN,
138 Doremi,
140 DoremiN,
142}
143
144impl NoteStyle {
145 pub const VALUES: [Self; 4] = [Self::Abc, Self::AbcN, Self::Doremi, Self::DoremiN];
146
147 #[inline]
148 pub fn display_note(self, n: Note, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 let display = &NOTES[n.offset as usize];
150 match self {
151 Self::Abc => f.write_str(display.abc),
152 Self::AbcN => write!(f, "{}{}", display.abc, n.octave),
153 Self::Doremi => f.write_str(display.doremi),
154 Self::DoremiN => write!(f, "{}{}", display.doremi, n.octave),
155 }
156 }
157}