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