rstmt_core/pitch/types/
pitch_class.rs

1/*
2    appellation: pitch_class <module>
3    authors: @FL03
4*/
5/// The [`PitchClass`] trait establishes an interface to defining pitch classes.
6pub trait PitchClass:
7    'static + Send + Sized + Sync + core::fmt::Debug + core::fmt::Display
8{
9    private!();
10
11    fn new() -> Self;
12
13    fn index(&self) -> usize;
14}
15/*
16 ************* Implementations *************
17*/
18macro_rules! class_enum {
19    {
20        $(#[doc $($doc:tt)*])?
21        $vis:vis enum $name:ident {$($rest:tt)*}
22    } => {
23        $(#[doc $($doc)*])?
24        #[derive(
25            Clone,
26            Copy,
27            Debug,
28            Default,
29            Eq,
30            Hash,
31            Ord,
32            PartialEq,
33            PartialOrd,
34            scsys::VariantConstructors,
35            strum::AsRefStr,
36            strum::Display,
37            strum::EnumCount,
38            strum::EnumIs,
39            strum::EnumIter,
40            strum::EnumString,
41            strum::VariantArray,
42            strum::VariantNames,
43        )]
44        #[cfg_attr(
45            feature = "serde",
46            derive(serde::Deserialize, serde::Serialize),
47            serde(rename_all = "UPPERCASE")
48        )]
49        #[strum(serialize_all = "UPPERCASE")]
50        $vis enum $name {$($rest)*}
51    };
52}
53
54macro_rules! pitch_class {
55    {
56        $($(#[doc $($doc:tt)*])?
57        $vis:vis $i:ident $name:ident = $c:literal);* $(;)?
58    } => {
59        $(
60            pitch_class! {
61                @impl
62                $(#[doc $($doc)*])?
63                $vis $i $name
64            }
65            pitch_class!(@ext $name = $c);
66        )*
67    };
68    {
69        @impl
70        $(#[doc $($doc:tt)*])?
71        $vis:vis struct $name:ident
72    } => {
73        $(#[doc $($doc)*])?
74        #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
75        #[cfg_attr(
76            feature = "serde",
77            derive(serde::Deserialize, serde::Serialize),
78        )]
79        $vis struct $name;
80    };
81    (@ext $name:ident = $c:literal) => {
82        impl $name {
83            pub const C_MAJOR_ID: usize = $c;
84            /// returns a new instance of the pitch class
85            pub const fn new() -> Self {
86                Self
87            }
88            /// returns a copy of the assigned index on the c-major scale
89            pub const fn value(&self) -> usize {
90                Self::C_MAJOR_ID
91            }
92        }
93
94        impl ::core::fmt::Display for $name {
95            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
96                write!(f, "{}", core::any::type_name::<Self>())
97            }
98        }
99
100        impl $crate::pitch::PitchClass for $name {
101            seal!();
102
103            fn new() -> Self {
104                Self::new()
105            }
106
107            fn index(&self) -> usize {
108                self.value()
109            }
110        }
111
112        impl PartialEq<usize> for $name {
113            fn eq(&self, other: &usize) -> bool {
114                self.value() == *other
115            }
116        }
117
118        impl PartialEq<$name> for usize {
119            fn eq(&self, other: &$name) -> bool {
120                *self == other.value()
121            }
122        }
123
124        impl PartialOrd<usize> for $name {
125            fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
126                Some(self.value().cmp(other))
127            }
128        }
129
130        impl PartialOrd<$name> for usize {
131            fn partial_cmp(&self, other: &$name) -> Option<core::cmp::Ordering> {
132                Some(self.cmp(&other.value()))
133            }
134        }
135    };
136}
137
138class_enum! {
139    #[doc = "A representation of the natural pitch class"]
140    pub enum Natural {
141        #[default]
142        C = 0,
143        D = 2,
144        E = 4,
145        F = 5,
146        G = 7,
147        A = 9,
148        B = 11,
149    }
150}
151
152class_enum! {
153    #[doc = "A representation of the sharp pitch class"]
154    pub enum Sharp {
155        #[default]
156        C = 1,
157        D = 3,
158        F = 6,
159        G = 8,
160        A = 10,
161    }
162}
163
164class_enum! {
165    #[doc = "A representation of the flat pitch class"]
166    pub enum Flat {
167        #[default]
168        D = 1,
169        E = 3,
170        G = 6,
171        A = 8,
172        B = 10,
173    }
174}
175
176pitch_class! {
177    #[doc = "A representation of the C pitch class"]
178    pub struct C = 0;
179    #[doc = "A representation of the D pitch class"]
180    pub struct D = 2;
181    #[doc = "A representation of the E pitch class"]
182    pub struct E = 4;
183    #[doc = "A representation of the F pitch class"]
184    pub struct F = 5;
185    #[doc = "A representation of the G pitch class"]
186    pub struct G = 7;
187    #[doc = "A representation of the A pitch class"]
188    pub struct A = 9;
189    #[doc = "A representation of the B pitch class"]
190    pub struct B = 11;
191
192    pub struct CSharp = 1;
193    pub struct DSharp = 3;
194    pub struct FSharp = 6;
195    pub struct GSharp = 8;
196    pub struct ASharp = 10;
197    pub struct DFlat = 1;
198    pub struct EFlat = 3;
199    pub struct GFlat = 6;
200    pub struct AFlat = 8;
201    pub struct BFlat = 10;
202
203}