libdaw/notation/
step.rs

1mod parse;
2
3use crate::{parse::IResult, pitch::Pitch};
4use nom::{combinator::all_consuming, error::convert_error, Finish as _};
5use std::str::FromStr;
6
7use super::resolve_state::ResolveState;
8
9/// A notation-specific scale step specification
10#[derive(Debug, Clone)]
11pub struct Step {
12    pub step: usize,
13    pub octave_shift: i8,
14    pub adjustment: f64,
15}
16
17impl Step {
18    pub fn parse(input: &str) -> IResult<&str, Self> {
19        parse::step(input)
20    }
21
22    /// Resolve to an absolute pitch
23    pub(super) fn absolute(&self, state: &ResolveState) -> Pitch {
24        let scale_octave = self.scale_octave(state);
25        let index = (self.step + state.inversion) % state.scale.len();
26        let scale_pitch = &state.scale[index];
27        let pitch_class = scale_pitch.pitch_class.clone();
28        pitch_class.lock().expect("poisoned").adjustment += self.adjustment;
29        Pitch {
30            pitch_class,
31            octave: scale_pitch.octave.saturating_add(scale_octave),
32        }
33    }
34
35    pub(super) fn scale_octave(&self, state: &ResolveState) -> i8 {
36        let half_scale = state.scale.len() / 2;
37        let step = (self.step + state.inversion) % state.scale.len();
38        let state_step = state.scale_step % state.scale.len();
39        let relative_shift = if state_step + half_scale < step {
40            -1
41        } else if step + half_scale < state_step {
42            1
43        } else {
44            0
45        };
46        relative_shift + self.octave_shift + state.scale_octave
47    }
48    pub(super) fn update_state(&self, state: &mut ResolveState) {
49        let scale_step = (self.step + state.inversion) % state.scale.len();
50        let scale_octave = self.scale_octave(state);
51        state.scale_step = scale_step;
52        state.scale_octave = scale_octave;
53    }
54}
55impl FromStr for Step {
56    type Err = String;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        let step = all_consuming(Self::parse)(s)
60            .finish()
61            .map_err(move |e| convert_error(s, e))?
62            .1;
63        Ok(step)
64    }
65}