notation_core/
octave.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5use crate::prelude::Semitones;
6
7// https://en.wikipedia.org/wiki/Scientific_pitch_notation
8#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
9pub enum Octave {
10    N1,
11    P0,
12    P1,
13    P2,
14    P3,
15    P4,
16    P5,
17    P6,
18    P7,
19    P8,
20    P9,
21    P10,
22}
23
24impl Octave {
25    pub const CENTER: Self = Self::P4;
26
27    pub fn is_even(&self) -> bool {
28        match self {
29            Octave::P0 |
30            Octave::P2 |
31            Octave::P4 |
32            Octave::P6 |
33            Octave::P8 |
34            Octave::P10 => true,
35            _ => false,
36        }
37    }
38}
39
40impl Default for Octave {
41    fn default() -> Self {
42        Self::CENTER
43    }
44}
45
46impl Display for Octave {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "{}", match self {
49            Octave::N1 => "N1",
50            Octave::P0 => "P0",
51            Octave::P1 => "P1",
52            Octave::P2 => "P2",
53            Octave::P3 => "P3",
54            Octave::P4 => "P4",
55            Octave::P5 => "P5",
56            Octave::P6 => "P6",
57            Octave::P7 => "P7",
58            Octave::P8 => "P8",
59            Octave::P9 => "P9",
60            Octave::P10 => "P10",
61        })
62    }
63}
64
65impl Octave {
66    pub fn to_ident(&self) -> String {
67        format!("{}", self)
68    }
69    pub fn from_ident(ident: &str) -> Self {
70        match ident {
71            "P0" => Self::P0,
72            "P1" => Self::P1,
73            "P2" => Self::P2,
74            "P3" => Self::P3,
75            "P4" => Self::P4,
76            "P5" => Self::P5,
77            "P6" => Self::P6,
78            "P7" => Self::P7,
79            "P8" => Self::P8,
80            "P9" => Self::P9,
81            "P10" => Self::P10,
82            _ => Self::N1,
83        }
84    }
85    pub fn get_higher(&self) -> Self {
86        (Semitones::from(*self) + Semitones(12)).into()
87    }
88    pub fn get_lower(&self) -> Self {
89        (Semitones::from(*self) - Semitones(12)).into()
90    }
91}
92
93impl From<Octave> for Semitones {
94    fn from(v: Octave) -> Self {
95        let v = match v {
96            Octave::N1 => -12,
97            Octave::P0 => 0,
98            Octave::P1 => 12,
99            Octave::P2 => 12 * 2,
100            Octave::P3 => 12 * 3,
101            Octave::P4 => 12 * 4,
102            Octave::P5 => 12 * 5,
103            Octave::P6 => 12 * 6,
104            Octave::P7 => 12 * 7,
105            Octave::P8 => 12 * 8,
106            Octave::P9 => 12 * 9,
107            Octave::P10 => 12 * 10,
108        };
109        Self::from(v)
110    }
111}
112
113impl From<Semitones> for Octave {
114    fn from(v: Semitones) -> Self {
115        if v.0 < 0 {
116            return Octave::N1;
117        }
118        match v.0 / 12 {
119            0 => Octave::P0,
120            1 => Octave::P1,
121            2 => Octave::P2,
122            3 => Octave::P3,
123            4 => Octave::P4,
124            5 => Octave::P5,
125            6 => Octave::P6,
126            7 => Octave::P7,
127            8 => Octave::P8,
128            9 => Octave::P9,
129            _ => Octave::P10,
130        }
131    }
132}