libdaw 0.2.0

A library for Rust for making programmable DAWs
Documentation
mod parse;

use crate::{parse::IResult, pitch::Pitch};
use nom::{combinator::all_consuming, error::convert_error, Finish as _};
use std::str::FromStr;

use super::resolve_state::ResolveState;

/// A notation-specific scale step specification
#[derive(Debug, Clone)]
pub struct Step {
    pub step: usize,
    pub octave_shift: i8,
    pub adjustment: f64,
}

impl Step {
    pub fn parse(input: &str) -> IResult<&str, Self> {
        parse::step(input)
    }

    /// Resolve to an absolute pitch
    pub(super) fn absolute(&self, state: &ResolveState) -> Pitch {
        let scale_octave = self.scale_octave(state);
        let index = (self.step + state.inversion) % state.scale.len();
        let scale_pitch = &state.scale[index];
        let pitch_class = scale_pitch.pitch_class.clone();
        pitch_class.lock().expect("poisoned").adjustment += self.adjustment;
        Pitch {
            pitch_class,
            octave: scale_pitch.octave.saturating_add(scale_octave),
        }
    }

    pub(super) fn scale_octave(&self, state: &ResolveState) -> i8 {
        let half_scale = state.scale.len() / 2;
        let step = (self.step + state.inversion) % state.scale.len();
        let state_step = state.scale_step % state.scale.len();
        let relative_shift = if state_step + half_scale < step {
            -1
        } else if step + half_scale < state_step {
            1
        } else {
            0
        };
        relative_shift + self.octave_shift + state.scale_octave
    }
    pub(super) fn update_state(&self, state: &mut ResolveState) {
        let scale_step = (self.step + state.inversion) % state.scale.len();
        let scale_octave = self.scale_octave(state);
        state.scale_step = scale_step;
        state.scale_octave = scale_octave;
    }
}
impl FromStr for Step {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let step = all_consuming(Self::parse)(s)
            .finish()
            .map_err(move |e| convert_error(s, e))?
            .1;
        Ok(step)
    }
}