#![no_std]
mod extended;
mod seventh;
mod suspended;
mod triad;
use core::ops::{Add, Sub};
use micromath::F32Ext;
pub mod chord_type {
pub mod triad {
pub use crate::triad::Mode;
}
pub mod seventh {
pub use crate::seventh::Mode;
}
pub mod extended {
pub use crate::extended::Mode;
}
pub mod suspended {
pub use crate::suspended::Mode;
}
}
trait Mode {
fn make_chord(&self, tonality: MusicalNote) -> Chord;
}
#[derive(Debug, Copy, Clone)]
pub enum Chord {
Triad([MusicalNote; 3]),
Seventh([MusicalNote; 4]),
FiveNotesChord([MusicalNote; 5]),
SixNotesChord([MusicalNote; 6]),
SevenNotesChord([MusicalNote; 7]),
}
impl From<[MusicalNote; 3]> for Chord {
fn from(c: [MusicalNote; 3]) -> Self {
Self::Triad(c)
}
}
impl From<[MusicalNote; 4]> for Chord {
fn from(c: [MusicalNote; 4]) -> Self {
Self::Seventh(c)
}
}
impl From<[MusicalNote; 5]> for Chord {
fn from(c: [MusicalNote; 5]) -> Self {
Self::FiveNotesChord(c)
}
}
impl From<[MusicalNote; 6]> for Chord {
fn from(c: [MusicalNote; 6]) -> Self {
Self::SixNotesChord(c)
}
}
impl From<[MusicalNote; 7]> for Chord {
fn from(c: [MusicalNote; 7]) -> Self {
Self::SevenNotesChord(c)
}
}
#[derive(Debug, Copy, Clone)]
pub enum Note {
A,
B,
C,
D,
E,
F,
G,
}
#[derive(Debug, Copy, Clone)]
pub enum Accidental {
Sharp,
Flat,
Natural,
DoubleFlat,
DoubleSharp,
}
#[derive(Debug, Copy, Clone)]
pub struct Interval(pub i8);
impl From<i8> for Interval {
fn from(i: i8) -> Self {
Self(i)
}
}
#[derive(Debug, Copy, Clone)]
pub struct MusicalNote(pub Note, pub Accidental);
impl Sub for MusicalNote {
type Output = Interval;
fn sub(self, rhs: Self) -> Self::Output {
Interval(SemitonesFromC::from(self) - SemitonesFromC::from(rhs))
}
}
impl<T> Add<T> for MusicalNote
where
T: Into<Interval>,
{
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
Self::from(SemitonesFromC(
(SemitonesFromC::from(self).0 + rhs.into().0) % 12,
))
}
}
impl From<(Note, Accidental)> for MusicalNote {
fn from(n: (Note, Accidental)) -> Self {
Self(n.0, n.1)
}
}
impl MusicalNote {
pub fn get_frequency(&self, octave: i8) -> f32 {
let a: f32 = (2f32).powf(1.0 / 12.0);
let a4 = SemitonesFromC::from(MusicalNote(Note::A, Accidental::Natural)).0 + 4 * 12;
440.0 * a.powf(((octave * 12 + SemitonesFromC::from(*self).0) - a4).into())
}
}
struct SemitonesFromC(i8);
impl Sub for SemitonesFromC {
type Output = i8;
fn sub(self, rhs: Self) -> Self::Output {
self.0 - rhs.0
}
}
impl From<MusicalNote> for SemitonesFromC {
fn from(n: MusicalNote) -> Self {
let (note, accidental) = (n.0, n.1);
let number = match note {
Note::A => 9,
Note::B => 11,
Note::C => 0,
Note::D => 2,
Note::E => 4,
Note::F => 5,
Note::G => 7,
};
let delta = match accidental {
Accidental::Sharp => 1,
Accidental::Flat => -1,
Accidental::Natural => 0,
Accidental::DoubleFlat => -2,
Accidental::DoubleSharp => 2,
};
SemitonesFromC(number + delta)
}
}
impl From<SemitonesFromC> for MusicalNote {
fn from(n: SemitonesFromC) -> Self {
match n.0 % 12 {
0 => (Note::C, Accidental::Natural),
1 => (Note::C, Accidental::Sharp),
2 => (Note::D, Accidental::Natural),
3 => (Note::D, Accidental::Sharp),
4 => (Note::E, Accidental::Natural),
5 => (Note::F, Accidental::Natural),
6 => (Note::F, Accidental::Sharp),
7 => (Note::G, Accidental::Natural),
8 => (Note::G, Accidental::Sharp),
9 => (Note::A, Accidental::Natural),
10 => (Note::A, Accidental::Sharp),
11 => (Note::B, Accidental::Natural),
_ => panic!("Unreachable code reached"),
}
.into()
}
}
impl<M: Mode> From<(MusicalNote, M)> for Chord {
fn from(n: (MusicalNote, M)) -> Self {
let (tonality, mode) = n;
mode.make_chord(tonality)
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(4, 4);
}
}