-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkinds.rs
196 lines (185 loc) · 5.04 KB
/
kinds.rs
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
Appellation: kinds <module>
Contrib: FL03 <[email protected]>
*/
use crate::{IntoPitch, Pitch};
/// [Intervals] enumerates the various intervals used within music theory.
/// The system considers a semitone to be the smallest interval, while the octave
/// describe the maximum distance between any two pitches.
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
strum::AsRefStr,
strum::Display,
strum::EnumIs,
)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "lowercase")
)]
#[repr(u8)]
#[strum(serialize_all = "lowercase")]
pub enum Interval {
#[default]
Semitone = 1,
Tone = 2,
Thirds(Third),
Fourths(Fourth),
Fifths(Fifth),
Sevenths(Seventh),
Octave = 12,
}
impl Interval {
pub fn new<A, B, C>(lhs: A, rhs: B) -> Self
where
A: core::ops::Sub<B, Output = C>,
C: Into<Interval>,
{
(lhs - rhs).into()
}
/// Use the difference between two pitches to determine the interval.
pub fn from_value(value: impl IntoPitch) -> Self {
use Interval::*;
let pitch = value.into_pitch();
match *pitch.absmod() {
1 => Semitone,
2 => Tone,
3 => Thirds(Third::Minor),
4 => Thirds(Third::Major),
5 => Fourths(Fourth::Perfect),
6 => Fifths(Fifth::Diminished),
7 => Fifths(Fifth::Perfect),
8 => Fifths(Fifth::Augmented),
9 => Sevenths(Seventh::Diminished),
10 => Sevenths(Seventh::Minor),
11 => Sevenths(Seventh::Major),
12 => Sevenths(Seventh::Augmented),
_ => panic!("Invalid interval value: {}", pitch.value()),
}
}
/// A convenience method for constructing a new instance of the [Octave](Intervals::Octave) variant.
pub fn octave() -> Self {
Interval::Octave
}
/// A convenience method for constructing a new instance of the [Semitone](Intervals::Semitone) variant.
pub fn semitone() -> Self {
Interval::Semitone
}
/// A convenience method for constructing a new instance of the [Tone](Intervals::Tone) variant.
pub fn tone() -> Self {
Interval::Tone
}
/// A convenience method for constructing a new variant, [`Thirds`](Intervals::Thirds).
pub fn third(third: Third) -> Self {
Interval::Thirds(third)
}
/// A convenience method for constructing a new variant, [`Fourths`](Intervals::Fourths).
pub fn fourth(fourth: Fourth) -> Self {
Interval::Fourths(fourth)
}
/// A convenience method for constructing a new variant, [`Fifths`](Intervals::Fifths).
pub fn fifth(fifth: Fifth) -> Self {
Interval::Fifths(fifth)
}
/// A convenience method for constructing a new variant, [`Sevenths`](Intervals::Sevenths).
pub fn seventh(seventh: Seventh) -> Self {
Interval::Sevenths(seventh)
}
/// Interpret the current interval as a pitch.
pub fn as_pitch(&self) -> Pitch {
Pitch::from(self.value())
}
/// Returns the name of the selected interval.
pub fn name(&self) -> &str {
self.as_ref()
}
/// Returns the value of the selected interval.
pub fn value(&self) -> i8 {
match *self {
Interval::Semitone => 1,
Interval::Tone => 2,
Interval::Thirds(third) => third as i8,
Interval::Fourths(fourth) => fourth as i8,
Interval::Fifths(fifth) => fifth as i8,
Interval::Sevenths(seventh) => seventh as i8,
Interval::Octave => 12,
}
}
}
macro_rules! impl_from_value {
(@impl $name:ident::$variant:ident($T:ty)) => {
impl From<$T> for $name {
fn from(value: $T) -> Self {
$name::$variant(value)
}
}
};
($($name:ident::$variant:ident($T:ty)),* $(,)?) => {
$(
impl_from_value!(@impl $name::$variant($T));
)*
};
}
impl<P> From<P> for Interval
where
P: IntoPitch,
{
fn from(value: P) -> Self {
Interval::from_value(value)
}
}
impl_from_value! {
Interval::Thirds(Third),
Interval::Fourths(Fourth),
Interval::Fifths(Fifth),
Interval::Sevenths(Seventh),
}
interval! {
default: Major;
pub enum Third {
Minor = 3,
Major = 4,
}
}
interval! {
default: Perfect;
pub enum Fourth {
Perfect = 5,
}
}
interval! {
default: Perfect;
pub enum Fifth {
Diminished = 6,
Perfect = 7,
Augmented = 8,
}
}
interval! {
default: Diminished;
pub enum Seventh {
Diminished = 9,
Minor = 10,
Major = 11,
Augmented = 12,
}
}
impl Fifth {
pub fn from_thirds(lhs: Third, rhs: Third) -> Self {
let value = lhs as i8 + rhs as i8;
match value {
6 => Fifth::Diminished,
7 => Fifth::Perfect,
8 => Fifth::Augmented,
_ => panic!("Invalid fifth value: {}", value),
}
}
}