1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use core::{convert, fmt, hash::Hash};
use serde::{Deserialize, Serialize};
/// Represents 1 note of the chromatic scale.
#[derive(Debug, Default, Clone, PartialEq, Copy, Eq, Deserialize, Serialize, Hash)]
pub enum Note {
#[default]
/// C
C,
/// C# or Db
CS,
/// D
D,
/// D# or Eb
DS,
/// E
E,
/// F
F,
/// F# or Gb
FS,
/// G
G,
/// G# or Ab
GS,
/// A
A,
/// A# or Bb
AS,
/// B
B,
}
impl convert::From<Note> for u8 {
fn from(note: Note) -> Self {
match note {
Note::C => 0,
Note::CS => 1,
Note::D => 2,
Note::DS => 3,
Note::E => 4,
Note::F => 5,
Note::FS => 6,
Note::G => 7,
Note::GS => 8,
Note::A => 9,
Note::AS => 10,
Note::B => 11,
}
}
}
impl From<u8> for Note {
fn from(midi: u8) -> Self {
let n = midi % 12;
match n {
0 => Note::C,
1 => Note::CS,
2 => Note::D,
3 => Note::DS,
4 => Note::E,
5 => Note::F,
6 => Note::FS,
7 => Note::G,
8 => Note::GS,
9 => Note::A,
10 => Note::AS,
11 => Note::B,
_ => unreachable!(),
}
}
}
impl fmt::Display for Note {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Note::C => "C",
Note::CS => "C#",
Note::D => "D",
Note::DS => "D#",
Note::E => "E",
Note::F => "F",
Note::FS => "F#",
Note::G => "G",
Note::GS => "G#",
Note::A => "A",
Note::AS => "A#",
Note::B => "B",
};
write!(f, "{str}")
}
}
impl Note {
/// Add semitones to a Note and an octave. Returns the new note and octave.
/// Works with negative semitones.
///
/// # Example
/// ```
/// use mseq_core::Note;
///
/// let note = Note::G;
/// let octave = 4;
/// let (new_note, new_octave) = note.add_semitone(octave, 5);
/// assert!(new_note == Note::C);
/// assert!(new_octave == 5);
/// ```
pub fn add_semitone(self, octave: u8, semi: i8) -> (Self, u8) {
let new_note = u8::from(self) as i8 + semi;
let q = new_note.div_euclid(12);
let r = new_note.rem_euclid(12);
((r as u8).into(), (octave as i8 + q) as u8)
}
/// Number of semitones required to transpose from root to note. The results range from 6 to -5
/// to minimize pitch difference with the original note.
pub fn transpose(root: Note, note: Note) -> i8 {
let root_m: u8 = root.into();
let note_m: u8 = note.into();
let n = (note_m as i8 - root_m as i8).rem_euclid(12);
if n > 6 { n - 12 } else { n }
}
}