1use super::scaletype::{
6 DegreeComparison, HUMDRUM_SOLFEG_SYLLABLES, MAX_RANGE_OCTAVES, SCALE_STARTS, SOLFEG_SYLLABLES,
7 ScaleType, SolfegVariant, advance, step_interval,
8};
9use crate::chord::{Chord, root};
10use crate::defaults::{FloatType, IntegerType};
11use crate::error::{Error, Result};
12use crate::interval::Interval;
13use crate::key::Key;
14use crate::pitch::Pitch;
15use crate::roman::{Minor67Default, RomanNumeral, degree_to_roman};
16use crate::tuningsystem::scala::{ScalaDegree, ScalaScale};
17
18#[derive(Clone, Debug, PartialEq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[must_use]
32pub struct Scale {
33 scale_type: ScaleType,
34 tonic: Pitch,
35 #[cfg_attr(feature = "serde", serde(default))]
45 custom_steps: Option<Vec<Interval>>,
46}
47
48impl Scale {
49 pub fn roman_numeral(&self, degree: u8) -> Result<RomanNumeral> {
53 if !(1..=7).contains(°ree) {
54 return Err(Error::Scale(format!(
55 "a roman numeral stands on a degree from 1 to 7, not {degree}"
56 )));
57 }
58 let key = Key::from_tonic_mode(&self.tonic.name(), "major")?;
59 RomanNumeral::over_scale(
60 degree_to_roman(degree),
61 key,
62 Some(self.clone()),
63 Minor67Default::default(),
64 Minor67Default::default(),
65 false,
66 )
67 }
68
69 pub fn tune(&self, stream: &mut crate::Stream) -> Result<()> {
76 let scale_pitches = self.pitches()?;
77 let names: Vec<String> = scale_pitches.iter().map(Pitch::name).collect();
78 let tuned = |pitch: &Pitch| -> Result<Option<Pitch>> {
79 let mut candidates = pitch.all_common_enharmonics(2);
80 candidates.push(pitch.clone());
81 for candidate in candidates {
82 let Some(index) = names.iter().position(|name| *name == candidate.name()) else {
83 continue;
84 };
85 let mut target = scale_pitches[index].clone();
86 target.set_octave(candidate.octave());
87 let spelled = target
88 .all_common_enharmonics(2)
89 .into_iter()
90 .find(|spelling| spelling.name() == pitch.name());
91 return Ok(Some(spelled.unwrap_or(target)));
92 }
93 Ok(None)
94 };
95 for event in stream.events_mut() {
96 match event.element_mut() {
97 crate::stream::StreamElement::Note(note) => {
98 if let Some(pitch) = tuned(note.pitch())? {
99 note.set_pitch(pitch);
100 }
101 }
102 crate::stream::StreamElement::Chord(chord) => {
103 for note in chord.notes_mut() {
104 if let Some(pitch) = tuned(note.pitch())? {
105 note.set_pitch(pitch);
106 }
107 }
108 }
109 crate::stream::StreamElement::Stream(inner) => self.tune(inner)?,
110 _ => {}
111 }
112 }
113 Ok(())
114 }
115
116 pub fn scala_data(&self) -> Result<ScalaScale> {
120 let pitches = self.pitches()?;
121 let (Some(tonic), Some(closing)) = (pitches.first(), pitches.last()) else {
122 return Err(Error::Scale(
123 "a scale with no pitches has no degrees".to_string(),
124 ));
125 };
126 let cents = |pitch: &Pitch| ScalaDegree::Cents((pitch.ps() - tonic.ps()) * 100.0);
127 let mut degrees = vec![ScalaDegree::Ratio(crate::tuningsystem::Fraction::new(1, 1))];
128 degrees.extend(pitches[1..pitches.len() - 1].iter().map(cents));
129 Ok(ScalaScale::new(
130 format!(
131 "{} {}",
132 self.tonic.name(),
133 self.scale_type.music21_descriptive_name()
134 ),
135 degrees,
136 cents(closing),
137 ))
138 }
139
140 pub fn new(scale_type: ScaleType, tonic: Pitch) -> Self {
142 Self {
143 scale_type,
144 tonic,
145 custom_steps: None,
146 }
147 }
148
149 pub fn from_pitches(pitches: &[Pitch]) -> Result<Self> {
156 let pitches = rising_octaves(pitches);
157 let pitches = pitches.as_slice();
158 let Some(tonic) = pitches.first() else {
159 return Err(crate::error::Error::Scale(
160 "a scale needs at least one pitch".to_string(),
161 ));
162 };
163 let mut steps = Vec::with_capacity(pitches.len());
164 for pair in pitches.windows(2) {
165 steps.push(Interval::between_pitches(&pair[0], &pair[1])?);
166 }
167 let last = pitches.last().unwrap_or(tonic);
172 let span = last.ps() - tonic.ps();
173 if span.rem_euclid(12.0) != 0.0 {
174 let octaves = (span / 12.0).floor() + 1.0;
175 let closing = tonic.transpose(&Interval::from_semitones(
176 (octaves * 12.0) as crate::defaults::IntegerType,
177 )?)?;
178 steps.push(Interval::between_pitches(last, &closing)?);
179 }
180 Ok(Self {
181 scale_type: ScaleType::Major,
182 tonic: tonic.clone(),
183 custom_steps: Some(steps),
184 })
185 }
186
187 pub fn descending(&self) -> Scale {
189 if self.custom_steps.is_some() {
190 return self.clone();
191 }
192 if let Some(scale_type) = self.scale_type.descending_form() {
193 return Scale::new(scale_type, self.tonic.clone());
194 }
195 if let Some(steps) = self.scale_type.descending_steps() {
199 let walked: Result<Vec<Interval>> = steps.iter().copied().map(step_interval).collect();
200 if let Ok(walked) = walked {
201 return Self {
202 scale_type: self.scale_type,
203 tonic: self.tonic.clone(),
204 custom_steps: Some(walked),
205 };
206 }
207 }
208 self.clone()
209 }
210
211 pub fn pitches_descending(&self) -> Result<Vec<Pitch>> {
214 let mut pitches = self.descending().pitches()?;
215 pitches.reverse();
216 Ok(pitches)
217 }
218
219 pub fn pitches_between_descending(
221 &self,
222 minimum: &Pitch,
223 maximum: &Pitch,
224 ) -> Result<Vec<Pitch>> {
225 let mut pitches = self.descending().pitches_between(minimum, maximum)?;
226 pitches.reverse();
227 Ok(pitches)
228 }
229
230 pub fn is_custom(&self) -> bool {
232 self.custom_steps.is_some()
233 }
234
235 pub fn set_tonic(&mut self, tonic: Pitch) {
237 self.tonic = tonic;
238 }
239
240 pub fn derive_ranked_by(
244 &self,
245 pitches: &[Pitch],
246 limit: Option<usize>,
247 comparison: DegreeComparison,
248 ) -> Result<Vec<(usize, Scale)>> {
249 if !self.is_custom() {
250 return self.scale_type.derive_ranked_by(pitches, limit, comparison);
251 }
252 let targets: Vec<String> = pitches.iter().map(|p| comparison.key(p)).collect();
253 let mut ranked = Vec::with_capacity(SCALE_STARTS.len());
254 for start in SCALE_STARTS {
255 let mut candidate = self.clone();
256 candidate.set_tonic(Pitch::from_name(start)?);
257 let degrees: Vec<String> = candidate
258 .pitches()?
259 .iter()
260 .map(|p| comparison.key(p))
261 .collect();
262 let matched = targets
263 .iter()
264 .filter(|target| degrees.contains(target))
265 .count();
266 ranked.push((matched, candidate));
267 }
268 ranked.sort_by(|left, right| {
269 left.0
270 .cmp(&right.0)
271 .then_with(|| left.1.tonic().ps().total_cmp(&right.1.tonic().ps()))
272 });
273 ranked.reverse();
274 if let Some(limit) = limit {
275 ranked.truncate(limit);
276 }
277 Ok(ranked)
278 }
279
280 fn walk(&self) -> Result<Vec<Interval>> {
282 match &self.custom_steps {
283 Some(steps) => Ok(steps.clone()),
284 None => self
285 .scale_type
286 .realization_steps()
287 .into_iter()
288 .map(step_interval)
289 .collect(),
290 }
291 }
292
293 pub fn degree_count(&self) -> usize {
296 match &self.custom_steps {
297 Some(steps) => steps.len(),
298 None => self.scale_type.degree_count(),
299 }
300 }
301
302 pub fn scale_type(&self) -> ScaleType {
304 self.scale_type
305 }
306
307 pub fn tonic(&self) -> &Pitch {
309 &self.tonic
310 }
311
312 pub fn pitches(&self) -> Result<Vec<Pitch>> {
322 let simplification = self.scale_type.simplification();
323 let start = self.realization_start()?;
324 let mut pitches = Vec::with_capacity(self.scale_type.degree_count() + 1);
325 pitches.push(start.clone());
326
327 let mut current = start;
328 for step in self.walk()? {
329 current = advance(¤t, &step, simplification)?;
330 pitches.push(current.clone());
331 }
332 if self.custom_steps.is_none()
333 && let Some(beyond) = self.scale_type.beyond_terminus()
334 {
335 pitches.push(advance(¤t, &step_interval(beyond)?, simplification)?);
336 }
337 Ok(pitches)
338 }
339
340 fn realization_start(&self) -> Result<Pitch> {
348 let mut start = self.realized_tonic();
349 if self.custom_steps.is_some() {
350 return Ok(start);
351 }
352 let steps = self.scale_type.steps();
353 for step in steps
354 .iter()
355 .rev()
356 .take(self.scale_type.tonic_degree().saturating_sub(1))
357 {
358 start = start.transpose(&step_interval(step)?.reversed()?)?;
359 }
360 Ok(start)
361 }
362
363 pub fn realized_tonic(&self) -> Pitch {
366 let mut tonic = self.tonic.clone();
367 if tonic.octave().is_none() {
368 tonic.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
369 }
370 tonic
371 }
372
373 pub fn relative_major(&self) -> Result<Scale> {
380 self.relative(ScaleType::Major)
381 }
382
383 pub fn relative_minor(&self) -> Result<Scale> {
386 self.relative(ScaleType::Minor)
387 }
388
389 pub fn parallel_major(&self) -> Scale {
391 Scale::new(ScaleType::Major, self.tonic.clone())
392 }
393
394 pub fn parallel_minor(&self) -> Scale {
396 Scale::new(ScaleType::Minor, self.tonic.clone())
397 }
398
399 fn relative(&self, wanted: ScaleType) -> Result<Scale> {
405 let mode = self.scale_type.music21_descriptive_name();
406 let sharps = crate::key::pitch_to_sharps(&self.tonic, Some(mode))?;
407 let key = crate::key::KeySignature::new(sharps)
408 .try_as_key(Some(wanted.music21_descriptive_name()), None)?;
409 let here = self.realized_tonic();
410 let mut tonic = key.tonic();
411 tonic.set_octave(here.octave());
412 if tonic.ps() < here.ps() {
413 tonic.set_octave(tonic.octave().map(|octave| octave + 1));
414 }
415 Ok(Scale::new(wanted, tonic))
416 }
417
418 pub fn named_degrees(&self) -> Option<Vec<IntegerType>> {
425 if self.custom_steps.is_some() {
426 return None;
427 }
428 Some(
429 self.scale_type
430 .ascending_degrees()?
431 .iter()
432 .map(|°ree| IntegerType::from(degree))
433 .collect(),
434 )
435 }
436
437 fn degree_at_position(&self, position: usize) -> usize {
439 self.named_degrees()
440 .and_then(|degrees| degrees.get(position).copied())
441 .map_or(position + 1, |degree| degree as usize)
442 }
443
444 pub fn pitch_on_degree(&self, degree: IntegerType) -> Result<Option<Pitch>> {
458 let position = match self.named_degrees() {
459 Some(degrees) => match degrees.iter().position(|&named| named == degree) {
460 Some(position) => position,
461 None => return Ok(None),
462 },
463 None => {
464 let count = self.degree_count().max(1) as IntegerType;
465 (degree - 1).rem_euclid(count) as usize
466 }
467 };
468 let simplification = self.scale_type.simplification();
469 let steps = self.walk()?;
470 let mut current = self.realization_start()?;
471 for index in 0..position {
472 current = advance(¤t, &steps[index % steps.len()], simplification)?;
473 }
474 Ok(Some(current))
475 }
476
477 pub fn pitch_at_degree(&self, degree: IntegerType) -> Result<Pitch> {
481 self.pitch_on_degree(degree)?.ok_or_else(|| {
482 Error::Scale(format!(
483 "{} has no degree {degree}",
484 self.scale_type.music21_descriptive_name()
485 ))
486 })
487 }
488 pub fn pitches_between(&self, minimum: &Pitch, maximum: &Pitch) -> Result<Vec<Pitch>> {
496 if maximum.ps() < minimum.ps() {
499 let mut descending = self.pitches_between(maximum, minimum)?;
500 descending.reverse();
501 return Ok(descending);
502 }
503 let lowest = minimum.ps();
504 let highest = maximum.ps();
505 let simplification = self.scale_type.simplification();
506 let steps = self.walk()?;
507 let period = self.period_in_octaves(&steps);
512 let mut current = self.realization_start()?;
513 while current.ps() > lowest {
514 let octave = current.octave().unwrap_or(0);
515 current.octave_setter(Some(octave - period));
516 }
517 let mut pitches = Vec::new();
518 let limit = steps.len() * (MAX_RANGE_OCTAVES + 2) + 1;
521 let clear_of = highest + 12.0 * FloatType::from(period);
526 for index in 0..limit {
527 let sounding = current.ps();
528 if sounding > clear_of {
529 break;
530 }
531 if (lowest..=highest).contains(&sounding) {
532 pitches.push(current.clone());
533 }
534 current = advance(¤t, &steps[index % steps.len()], simplification)?;
535 }
536 Ok(pitches)
537 }
538
539 pub fn is_realizable(&self) -> bool {
546 match &self.custom_steps {
547 Some(steps) => steps.iter().map(Interval::semitones).sum::<FloatType>() > 0.0,
548 None => true,
549 }
550 }
551
552 pub fn octave_duplicating(&self) -> bool {
557 match &self.custom_steps {
558 Some(steps) => self.period_in_octaves(steps) == 1,
559 None => true,
560 }
561 }
562
563 fn period_in_octaves(&self, steps: &[Interval]) -> IntegerType {
567 let semitones: FloatType = steps.iter().map(Interval::semitones).sum();
568 ((semitones / 12.0).round() as IntegerType).max(1)
569 }
570
571 pub fn final_pitch(&self) -> Result<Pitch> {
574 self.pitch_at_degree(self.scale_type.tonic_degree() as IntegerType)
575 }
576
577 pub fn dominant(&self) -> Result<Pitch> {
579 self.pitch_at_degree(self.scale_type.dominant_degree() as IntegerType)
580 }
581
582 pub fn leading_tone(&self) -> Result<Pitch> {
586 let seventh = self.pitch_at_degree(7)?;
587 let tonic = self.final_pitch()?;
588 let distance = seventh.midi() - tonic.midi();
589 if distance == 11 {
590 return Ok(seventh);
591 }
592 let alter = seventh.accidental().alter() + FloatType::from(11 - distance);
593 let mut raised = seventh.clone();
594 raised.set_accidental(Some(crate::pitch::Accidental::new(alter)?));
595 Ok(raised)
596 }
597
598 pub fn derive_by_degree(&self, degree: usize, pitch: &Pitch) -> Result<Scale> {
603 let implicit_octave = Some(crate::defaults::PITCH_OCTAVE as IntegerType);
604 let mut tonic = self.tonic.clone();
605 if tonic.octave().is_none() {
606 tonic.octave_setter(implicit_octave);
607 }
608 let degree_pitch =
609 Scale::new(self.scale_type, tonic.clone()).pitch_at_degree(degree as IntegerType)?;
610 let up_to_degree = Interval::between_pitches(&tonic, °ree_pitch)?;
611 let mut reference = pitch.clone();
612 if reference.octave().is_none() {
613 reference.octave_setter(implicit_octave);
614 }
615 let new_tonic = reference.transpose(&up_to_degree.reversed()?)?;
616 Ok(Scale::new(self.scale_type, new_tonic))
617 }
618
619 pub fn transpose(&self, interval: &Interval) -> Result<Scale> {
621 Ok(Scale::new(self.scale_type, self.tonic.transpose(interval)?))
622 }
623
624 pub fn chord(&self) -> Result<Chord> {
627 Chord::new(self.pitches()?.as_slice())
628 }
629
630 pub fn pitches_from_scale_degrees(&self, degrees: &[usize]) -> Result<Vec<Pitch>> {
634 let octave = self.pitches()?;
635 let count = octave.len().saturating_sub(1).max(1);
640 Ok(octave
641 .into_iter()
642 .enumerate()
643 .filter(|(index, _)| degrees.contains(&(index % count + 1)))
644 .map(|(_, pitch)| pitch)
645 .collect())
646 }
647
648 pub fn pitches_from_scale_degrees_between(
652 &self,
653 degrees: &[usize],
654 minimum: &Pitch,
655 maximum: &Pitch,
656 ) -> Result<Vec<Pitch>> {
657 let wanted: Vec<String> = self
658 .pitches_from_scale_degrees(degrees)?
659 .iter()
660 .map(Pitch::name)
661 .collect();
662 Ok(self
663 .pitches_between(minimum, maximum)?
664 .into_iter()
665 .filter(|pitch| wanted.contains(&pitch.name()))
666 .collect())
667 }
668
669 pub fn interval_between_degrees(&self, start: usize, end: usize) -> Result<Interval> {
674 Interval::between_pitches(
675 &self.pitch_at_degree(start as IntegerType)?,
676 &self.pitch_at_degree(end as IntegerType)?,
677 )
678 }
679
680 pub fn is_next(&self, other: &Pitch, origin: &Pitch, steps: usize) -> Result<bool> {
684 Ok(self.next_pitch_above(origin, steps)?.name() == other.name())
685 }
686
687 pub fn match_pitches(&self, pitches: &[Pitch]) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
692 self.match_pitches_by(pitches, DegreeComparison::Name)
693 }
694
695 pub fn match_pitches_by(
701 &self,
702 pitches: &[Pitch],
703 comparison: DegreeComparison,
704 ) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
705 let realized = self.realized_in_implicit_octave()?;
706 let degrees: Vec<String> = realized.iter().map(|p| comparison.key(p)).collect();
707 let mut matched = Vec::new();
708 let mut unmatched = Vec::new();
709 for pitch in pitches {
710 let mut heard = pitch.clone();
711 if heard.octave().is_none() {
712 heard.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
713 }
714 if degrees.contains(&comparison.key(&heard)) {
715 matched.push(heard);
716 } else {
717 unmatched.push(heard);
718 }
719 }
720 Ok((matched, unmatched))
721 }
722
723 pub fn find_missing(&self, pitches: &[Pitch]) -> Result<Vec<Pitch>> {
727 let present: Vec<u8> = pitches.iter().map(root::pitch_class).collect();
728 Ok(self
729 .realized_in_implicit_octave()?
730 .into_iter()
731 .filter(|candidate| !present.contains(&root::pitch_class(candidate)))
732 .collect())
733 }
734
735 pub fn solfeg(&self, pitch: &Pitch, variant: SolfegVariant, chromatic: bool) -> Result<String> {
741 let (degree, accidental) = self.degree_and_accidental_of(pitch)?;
742 if degree > 7 {
743 return Err(crate::error::Error::Scale(
744 "Cannot call solfeg on non-7-degree scales".to_string(),
745 ));
746 }
747 let table = match variant {
748 SolfegVariant::Music21 => &SOLFEG_SYLLABLES,
749 SolfegVariant::Humdrum => &HUMDRUM_SOLFEG_SYLLABLES,
750 };
751 let alter = if chromatic {
752 accidental.map_or(0, |accidental| accidental.alter() as IntegerType)
753 } else {
754 0
755 };
756 let column = usize::try_from(alter + 2)
757 .ok()
758 .filter(|column| *column < 5)
759 .ok_or_else(|| {
760 crate::error::Error::Scale(format!(
761 "no solfeg syllable for an alteration of {alter}"
762 ))
763 })?;
764 Ok(table[degree - 1][column].to_string())
765 }
766
767 fn realized_in_implicit_octave(&self) -> Result<Vec<Pitch>> {
768 self.pitches()
769 }
770
771 pub fn degree_of_by(
774 &self,
775 pitch: &Pitch,
776 comparison: DegreeComparison,
777 ) -> Result<Option<usize>> {
778 let wanted = comparison.key(pitch);
779 Ok(self
780 .scale_pitches()?
781 .iter()
782 .position(|candidate| comparison.key(candidate) == wanted)
783 .map(|index| self.degree_at_position(index)))
784 }
785
786 pub fn degrees_of_by(&self, pitch: &Pitch, comparison: DegreeComparison) -> Result<Vec<usize>> {
793 let wanted = comparison.key(pitch);
794 Ok(self
795 .scale_pitches()?
796 .iter()
797 .enumerate()
798 .filter(|(_, candidate)| comparison.key(candidate) == wanted)
799 .map(|(index, _)| self.degree_at_position(index))
800 .collect())
801 }
802
803 pub fn degree_of(&self, pitch: &Pitch) -> Result<Option<usize>> {
806 let name = pitch.name();
807 Ok(self
808 .scale_pitches()?
809 .iter()
810 .position(|candidate| candidate.name() == name)
811 .map(|index| self.degree_at_position(index)))
812 }
813
814 pub fn degree_of_pitch_class(&self, pitch: &Pitch) -> Result<Option<usize>> {
817 let pitch_class = pitch.pitch_class().number();
818 Ok(self
819 .scale_pitches()?
820 .iter()
821 .position(|candidate| candidate.pitch_class().number() == pitch_class)
822 .map(|index| self.degree_at_position(index)))
823 }
824
825 pub fn next_pitch_above(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
828 self.pitch_steps_from(origin, steps as IntegerType, None)
829 }
830
831 pub fn next_pitch_beside(
836 &self,
837 origin: &Pitch,
838 steps: IntegerType,
839 below: bool,
840 ) -> Result<Pitch> {
841 self.pitch_steps_from(origin, steps, Some(below))
842 }
843
844 pub fn next_pitch_below(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
847 self.pitch_steps_from(origin, -(steps as IntegerType), None)
848 }
849
850 pub fn degree_and_accidental_of(
855 &self,
856 pitch: &Pitch,
857 ) -> Result<(usize, Option<crate::pitch::Accidental>)> {
858 if let Some(degree) = self.degree_of(pitch)? {
859 return Ok((degree, None));
860 }
861 let pitches = self.scale_pitches()?;
862 let index = pitches
863 .iter()
864 .position(|candidate| candidate.step() == pitch.step())
865 .ok_or_else(|| {
866 crate::error::Error::Scale(format!(
867 "cannot get any scale degree for {pitch} in {self:?}"
868 ))
869 })?;
870 let difference = pitch.accidental().alter() - pitches[index].accidental().alter();
871 let accidental = if difference == 0.0 {
872 None
873 } else {
874 Some(crate::pitch::Accidental::new(difference)?)
875 };
876 Ok((index + 1, accidental))
877 }
878
879 fn scale_pitches(&self) -> Result<Vec<Pitch>> {
880 let mut pitches = self.pitches()?;
881 pitches.truncate(self.scale_type.degree_count());
882 Ok(pitches)
883 }
884
885 fn pitch_steps_from(
886 &self,
887 origin: &Pitch,
888 steps: IntegerType,
889 neighbour_below: Option<bool>,
890 ) -> Result<Pitch> {
891 self.pitch_steps_from_place(origin, steps, neighbour_below, 0)
892 }
893
894 pub fn places_of(&self, origin: &Pitch) -> Result<usize> {
902 Ok(self.places_on(origin)?.len())
903 }
904
905 fn places_on(&self, origin: &Pitch) -> Result<Vec<(usize, IntegerType)>> {
908 let pitches = self.scale_pitches()?;
909 let origin_ps = origin.ps();
910 let name = origin.name();
911 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
912 Ok((base_shift - 1..=base_shift + 1)
913 .flat_map(|shift| {
914 pitches.iter().enumerate().map(move |(index, pitch)| {
915 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
916 })
917 })
918 .filter(|(ps, index, _)| {
919 pitches[*index].name() == name && (ps - origin_ps).abs() < 1e-9
920 })
921 .map(|(_, index, shift)| (index, shift))
922 .collect())
923 }
924
925 pub fn next_pitch_below_from(
928 &self,
929 origin: &Pitch,
930 steps: usize,
931 place: usize,
932 ) -> Result<Pitch> {
933 self.pitch_steps_from_place(origin, -(steps as IntegerType), None, place)
934 }
935
936 pub fn next_pitch_above_from(
938 &self,
939 origin: &Pitch,
940 steps: usize,
941 place: usize,
942 ) -> Result<Pitch> {
943 self.pitch_steps_from_place(origin, steps as IntegerType, None, place)
944 }
945
946 fn pitch_steps_from_place(
947 &self,
948 origin: &Pitch,
949 steps: IntegerType,
950 neighbour_below: Option<bool>,
951 place: usize,
952 ) -> Result<Pitch> {
953 if steps == 0 {
954 return Err(crate::error::Error::Scale(
955 "step size must be at least 1".to_string(),
956 ));
957 }
958 let pitches = self.scale_pitches()?;
959 let count = pitches.len() as IntegerType;
960 let origin_ps = origin.ps();
961 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
962 let candidates = (base_shift - 1..=base_shift + 1)
963 .flat_map(|shift| {
964 pitches.iter().enumerate().map(move |(index, pitch)| {
965 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
966 })
967 })
968 .collect::<Vec<_>>();
969 let ascending = steps > 0;
970 let standing = self.places_on(origin)?;
971 let (index, shift, remaining) = match standing.get(place % standing.len().max(1)).copied() {
972 Some((index, shift)) => (index, shift, steps),
973 None => {
974 let take_below = neighbour_below.unwrap_or(!ascending);
978 let neighbour = if take_below {
979 candidates
980 .iter()
981 .filter(|(ps, _, _)| *ps < origin_ps)
982 .max_by(|left, right| left.0.total_cmp(&right.0))
983 } else {
984 candidates
985 .iter()
986 .filter(|(ps, _, _)| *ps > origin_ps)
987 .min_by(|left, right| left.0.total_cmp(&right.0))
988 };
989 let &(_, index, shift) = neighbour.ok_or_else(|| {
990 crate::error::Error::Scale(format!("no scale pitch beside {origin}"))
991 })?;
992 let remaining = if neighbour_below.is_some() {
993 steps
994 } else {
995 steps - steps.signum()
996 };
997 (index, shift, remaining)
998 }
999 };
1000 let total = index as IntegerType + remaining;
1001 let mut pitch = pitches[total.rem_euclid(count) as usize].clone();
1002 let octave_shift = shift + total.div_euclid(count);
1003 let octave = origin.octave().map(|_| {
1004 pitch
1005 .octave()
1006 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1007 + octave_shift
1008 });
1009 pitch.octave_setter(octave);
1010 Ok(pitch)
1011 }
1012}
1013
1014pub(super) fn rising_octaves(pitches: &[Pitch]) -> Vec<Pitch> {
1021 let mut risen: Vec<Pitch> = Vec::with_capacity(pitches.len());
1022 let mut last_ps = 0.0;
1023 let mut last_octave =
1024 pitches
1025 .first()
1026 .map_or(crate::defaults::PITCH_OCTAVE as IntegerType, |pitch| {
1027 pitch
1028 .octave()
1029 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1030 });
1031 for pitch in pitches {
1032 let mut pitch = pitch.clone();
1033 if pitch.octave().is_none() {
1034 if last_ps > pitch.ps() {
1035 pitch.octave_setter(Some(last_octave));
1036 }
1037 while last_ps > pitch.ps() {
1038 last_octave += 1;
1039 pitch.octave_setter(Some(last_octave));
1040 }
1041 }
1042 last_ps = pitch.ps();
1043 risen.push(pitch);
1044 }
1045 risen
1046}