notation_core 0.6.0

Fun notation - definition for core concepts
Documentation
use std::fmt::Display;

use serde::{Deserialize, Serialize};

use crate::prelude::Semitones;

// https://en.wikipedia.org/wiki/Scientific_pitch_notation
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
pub enum Octave {
    N1,
    P0,
    P1,
    P2,
    P3,
    P4,
    P5,
    P6,
    P7,
    P8,
    P9,
    P10,
}

impl Octave {
    pub const CENTER: Self = Self::P4;

    pub fn is_even(&self) -> bool {
        match self {
            Octave::P0 |
            Octave::P2 |
            Octave::P4 |
            Octave::P6 |
            Octave::P8 |
            Octave::P10 => true,
            _ => false,
        }
    }
}

impl Default for Octave {
    fn default() -> Self {
        Self::CENTER
    }
}

impl Display for Octave {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", match self {
            Octave::N1 => "N1",
            Octave::P0 => "P0",
            Octave::P1 => "P1",
            Octave::P2 => "P2",
            Octave::P3 => "P3",
            Octave::P4 => "P4",
            Octave::P5 => "P5",
            Octave::P6 => "P6",
            Octave::P7 => "P7",
            Octave::P8 => "P8",
            Octave::P9 => "P9",
            Octave::P10 => "P10",
        })
    }
}

impl Octave {
    pub fn to_ident(&self) -> String {
        format!("{}", self)
    }
    pub fn from_ident(ident: &str) -> Self {
        match ident {
            "P0" => Self::P0,
            "P1" => Self::P1,
            "P2" => Self::P2,
            "P3" => Self::P3,
            "P4" => Self::P4,
            "P5" => Self::P5,
            "P6" => Self::P6,
            "P7" => Self::P7,
            "P8" => Self::P8,
            "P9" => Self::P9,
            "P10" => Self::P10,
            _ => Self::N1,
        }
    }
    pub fn get_higher(&self) -> Self {
        (Semitones::from(*self) + Semitones(12)).into()
    }
    pub fn get_lower(&self) -> Self {
        (Semitones::from(*self) - Semitones(12)).into()
    }
}

impl From<Octave> for Semitones {
    fn from(v: Octave) -> Self {
        let v = match v {
            Octave::N1 => -12,
            Octave::P0 => 0,
            Octave::P1 => 12,
            Octave::P2 => 12 * 2,
            Octave::P3 => 12 * 3,
            Octave::P4 => 12 * 4,
            Octave::P5 => 12 * 5,
            Octave::P6 => 12 * 6,
            Octave::P7 => 12 * 7,
            Octave::P8 => 12 * 8,
            Octave::P9 => 12 * 9,
            Octave::P10 => 12 * 10,
        };
        Self::from(v)
    }
}

impl From<Semitones> for Octave {
    fn from(v: Semitones) -> Self {
        if v.0 < 0 {
            return Octave::N1;
        }
        match v.0 / 12 {
            0 => Octave::P0,
            1 => Octave::P1,
            2 => Octave::P2,
            3 => Octave::P3,
            4 => Octave::P4,
            5 => Octave::P5,
            6 => Octave::P6,
            7 => Octave::P7,
            8 => Octave::P8,
            9 => Octave::P9,
            _ => Octave::P10,
        }
    }
}