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;
#[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)
}
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)
}
}