music_note/note/
accidental.rs

1use crate::{Interval, Natural, Pitch};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum AccidentalKind {
5    Natural,
6    Single,
7    Double,
8}
9
10pub trait Accidental {
11    fn into_pitch(kind: AccidentalKind, natural: Natural) -> Pitch;
12
13    fn from_pitch(natural: Natural, pitch: Pitch) -> AccidentalKind;
14}
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Sharp {}
18
19impl Accidental for Sharp {
20    fn into_pitch(kind: AccidentalKind, natural: Natural) -> Pitch {
21        let pitch = Pitch::natural(natural);
22        match kind {
23            AccidentalKind::Natural => pitch,
24            AccidentalKind::Single => pitch + Interval::MINOR_SECOND,
25            AccidentalKind::Double => pitch + Interval::MAJOR_SECOND,
26        }
27    }
28
29    fn from_pitch(natural: Natural, pitch: Pitch) -> AccidentalKind {
30        let natural_pitch = Pitch::natural(natural);
31        if pitch >= natural_pitch {
32            match pitch.sub(natural_pitch) {
33                Interval::UNISON => AccidentalKind::Natural,
34                Interval::MINOR_SECOND => AccidentalKind::Single,
35                Interval::MAJOR_SECOND => AccidentalKind::Double,
36                _ => unimplemented!(),
37            }
38        } else {
39            match natural_pitch.sub(pitch) {
40                Interval::MAJOR_SEVENTH => AccidentalKind::Single,
41                _ => unimplemented!(),
42            }
43        }
44    }
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum Flat {}
49
50impl Accidental for Flat {
51    fn into_pitch(kind: AccidentalKind, natural: Natural) -> Pitch {
52        let pitch = Pitch::natural(natural);
53        match kind {
54            AccidentalKind::Natural => pitch,
55            AccidentalKind::Single => pitch - Interval::MINOR_SECOND,
56            AccidentalKind::Double => pitch - Interval::MAJOR_SECOND,
57        }
58    }
59
60    fn from_pitch(natural: Natural, pitch: Pitch) -> AccidentalKind {
61        let natural_pitch = Pitch::natural(natural);
62        if pitch >= natural_pitch {
63            match pitch.sub(natural_pitch) {
64                Interval::UNISON => AccidentalKind::Natural,
65                Interval::MINOR_SECOND => AccidentalKind::Single,
66                Interval::MAJOR_SEVENTH => AccidentalKind::Single,
67                x => panic!("{:?}", x),
68            }
69        } else {
70            match natural_pitch.sub(pitch) {
71                Interval::MINOR_SECOND => AccidentalKind::Single,
72                Interval::MAJOR_SECOND => AccidentalKind::Double,
73                x => panic!("{:?}", x),
74            }
75        }
76    }
77}