1use super::scaletype::{
6 DegreeComparison, HUMDRUM_SOLFEG_SYLLABLES, MAX_RANGE_OCTAVES, SOLFEG_SYLLABLES, ScaleType,
7 Simplification, 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 #[cfg_attr(feature = "serde", serde(default))]
49 custom_simplification: Option<Simplification>,
50 #[cfg_attr(feature = "serde", serde(default))]
55 cyclic: bool,
56}
57
58impl Scale {
59 pub fn roman_numeral(&self, degree: u8) -> Result<RomanNumeral> {
63 if !(1..=7).contains(°ree) {
64 return Err(Error::Scale(format!(
65 "a roman numeral stands on a degree from 1 to 7, not {degree}"
66 )));
67 }
68 let key = Key::from_tonic_mode(&self.tonic.name(), "major")?;
69 RomanNumeral::over_scale(
70 degree_to_roman(degree),
71 key,
72 Some(self.clone()),
73 Minor67Default::default(),
74 Minor67Default::default(),
75 false,
76 )
77 }
78
79 pub fn tune(&self, stream: &mut crate::Stream) -> Result<()> {
86 let scale_pitches = self.pitches()?;
87 let names: Vec<String> = scale_pitches.iter().map(Pitch::name).collect();
88 let tuned = |pitch: &Pitch| -> Result<Option<Pitch>> {
89 let mut candidates = pitch.all_common_enharmonics(2);
90 candidates.push(pitch.clone());
91 for candidate in candidates {
92 let Some(index) = names.iter().position(|name| *name == candidate.name()) else {
93 continue;
94 };
95 let mut target = scale_pitches[index].clone();
96 target.set_octave(candidate.octave());
97 let spelled = target
98 .all_common_enharmonics(2)
99 .into_iter()
100 .find(|spelling| spelling.name() == pitch.name());
101 return Ok(Some(spelled.unwrap_or(target)));
102 }
103 Ok(None)
104 };
105 for event in stream.events_mut() {
106 match event.element_mut() {
107 crate::stream::StreamElement::Note(note) => {
108 if let Some(pitch) = tuned(note.pitch())? {
109 note.set_pitch(pitch);
110 }
111 }
112 crate::stream::StreamElement::Chord(chord) => {
113 for note in chord.notes_mut() {
114 if let Some(pitch) = tuned(note.pitch())? {
115 note.set_pitch(pitch);
116 }
117 }
118 }
119 crate::stream::StreamElement::Stream(inner) => self.tune(inner)?,
120 _ => {}
121 }
122 }
123 Ok(())
124 }
125
126 pub fn scala_data(&self) -> Result<ScalaScale> {
130 let pitches = self.pitches()?;
131 let (Some(tonic), Some(closing)) = (pitches.first(), pitches.last()) else {
132 return Err(Error::Scale(
133 "a scale with no pitches has no degrees".to_string(),
134 ));
135 };
136 let cents = |pitch: &Pitch| ScalaDegree::Cents((pitch.ps() - tonic.ps()) * 100.0);
137 let mut degrees = vec![ScalaDegree::Ratio(crate::tuningsystem::Ratio::new(1, 1))];
138 degrees.extend(pitches[1..pitches.len() - 1].iter().map(cents));
139 Ok(ScalaScale::new(
140 format!(
141 "{} {}",
142 self.tonic.name(),
143 self.scale_type.music21_descriptive_name()
144 ),
145 degrees,
146 cents(closing),
147 ))
148 }
149
150 pub fn new(scale_type: ScaleType, tonic: Pitch) -> Self {
152 Self {
153 scale_type,
154 tonic,
155 custom_steps: None,
156 custom_simplification: None,
157 cyclic: scale_type.is_cycle(),
158 }
159 }
160
161 pub fn from_pitches(pitches: &[Pitch]) -> Result<Self> {
168 let pitches = fix_default_octave_for_pitch_list(pitches);
169 let pitches = pitches.as_slice();
170 let Some(tonic) = pitches.first() else {
171 return Err(crate::error::Error::Scale(
172 "a scale needs at least one pitch".to_string(),
173 ));
174 };
175 let mut steps = Vec::with_capacity(pitches.len());
176 for pair in pitches.windows(2) {
177 steps.push(Interval::between_pitches(&pair[0], &pair[1])?);
178 }
179 let last = pitches.last().unwrap_or(tonic);
186 if last.name() != tonic.name() {
187 let mut closing = tonic.clone();
188 let mut octave = tonic
189 .octave()
190 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType);
191 closing.octave_setter(Some(octave));
192 if last.ps() > tonic.ps() {
193 while closing.ps() < last.ps() {
194 octave += 1;
195 closing.octave_setter(Some(octave));
196 }
197 } else {
198 while closing.ps() > last.ps() {
199 octave -= 1;
200 closing.octave_setter(Some(octave));
201 }
202 }
203 steps.push(Interval::between_pitches(last, &closing)?);
204 }
205 Ok(Self {
206 scale_type: ScaleType::Major,
207 tonic: tonic.clone(),
208 custom_steps: Some(steps),
209 custom_simplification: None,
210 cyclic: false,
211 })
212 }
213
214 pub fn from_steps(tonic: Pitch, steps: Vec<Interval>) -> Result<Self> {
222 if steps.is_empty() {
223 return Err(crate::error::Error::Scale(
224 "a scale needs at least one step".to_string(),
225 ));
226 }
227 Ok(Self {
228 scale_type: ScaleType::Major,
229 tonic,
230 custom_steps: Some(steps),
231 custom_simplification: Some(Simplification::MaxAccidental),
235 cyclic: false,
236 })
237 }
238
239 pub fn from_cycle(tonic: Pitch, steps: Vec<Interval>) -> Result<Self> {
249 let mut scale = Self::from_steps(tonic, steps)?;
250 scale.cyclic = true;
251 Ok(scale)
252 }
253
254 pub fn with_simplification(mut self, simplification: Simplification) -> Self {
258 self.custom_simplification = Some(simplification);
259 self
260 }
261
262 pub fn from_scala(tonic: Pitch, scala: &ScalaScale) -> Result<Self> {
270 Ok(Self::from_cycle(tonic, scala.interval_sequence()?)?
271 .with_simplification(Simplification::MostCommon))
272 }
273
274 pub fn descending(&self) -> Scale {
276 if self.custom_steps.is_some() {
277 return self.clone();
278 }
279 if let Some(scale_type) = self.scale_type.descending_form() {
280 let descending = Scale::new(scale_type, self.tonic.clone());
283 return if scale_type.simplification() == self.simplification() {
284 descending
285 } else {
286 descending.with_simplification(self.simplification())
287 };
288 }
289 if let Some(steps) = self.scale_type.descending_steps() {
293 let walked: Result<Vec<Interval>> = steps.iter().copied().map(step_interval).collect();
294 if let Ok(walked) = walked {
295 return Self {
296 scale_type: self.scale_type,
297 tonic: self.tonic.clone(),
298 custom_steps: Some(walked),
299 custom_simplification: None,
300 cyclic: false,
301 };
302 }
303 }
304 self.clone()
305 }
306
307 pub fn pitches_descending(&self) -> Result<Vec<Pitch>> {
310 let ascending = self.pitches()?;
317 let bottom = ascending.iter().min_by(|a, b| a.ps().total_cmp(&b.ps()));
318 let top = ascending.iter().max_by(|a, b| a.ps().total_cmp(&b.ps()));
319 let (Some(bottom), Some(top)) = (bottom, top) else {
320 return Ok(ascending);
321 };
322 self.pitches_between_descending(bottom, top)
323 }
324
325 pub fn pitches_between_descending(
327 &self,
328 minimum: &Pitch,
329 maximum: &Pitch,
330 ) -> Result<Vec<Pitch>> {
331 self.descending().walked_down_between(minimum, maximum)
332 }
333
334 fn walked_down_between(&self, minimum: &Pitch, maximum: &Pitch) -> Result<Vec<Pitch>> {
343 let (lowest, highest) = (
344 minimum.ps().min(maximum.ps()),
345 minimum.ps().max(maximum.ps()),
346 );
347 if !self.repeats_at_the_octave() || self.cyclic {
348 let mut pitches: Vec<Pitch> = self
349 .cycle_between(lowest, highest)?
350 .into_iter()
351 .map(|(pitch, _)| pitch)
352 .collect();
353 pitches.reverse();
354 return Ok(pitches);
355 }
356 let simplification = self.simplification();
357 let steps = self.walk()?;
358 let period = self.period_in_octaves(&steps);
359 let mut current = self.realization_start()?;
360 shift_by_periods(&mut current, period, highest, false);
361 let mut pitches = Vec::new();
362 let limit = steps.len() * (MAX_RANGE_OCTAVES + 2) + 1;
363 for index in 0..limit {
364 let sounding = current.ps();
365 if within(sounding, lowest, highest) {
366 pitches.push(current.clone());
367 }
368 if sounding < lowest + SLACK {
369 break;
370 }
371 let step = &steps[steps.len() - 1 - index % steps.len()];
372 current = advance(¤t, &step.reversed()?, simplification)?;
373 }
374 Ok(pitches)
375 }
376
377 pub fn custom_steps(&self) -> Option<&[Interval]> {
381 self.custom_steps.as_deref()
382 }
383
384 pub fn is_cyclic(&self) -> bool {
387 self.cyclic
388 }
389
390 pub fn is_custom(&self) -> bool {
392 self.custom_steps.is_some()
393 }
394
395 pub fn set_tonic(&mut self, tonic: Pitch) {
397 self.tonic = tonic;
398 }
399
400 pub fn derive_ranked_by(
404 &self,
405 pitches: &[Pitch],
406 limit: Option<usize>,
407 comparison: DegreeComparison,
408 ) -> Result<Vec<(usize, Scale)>> {
409 if !self.is_custom() {
410 return self.scale_type.derive_ranked_by(pitches, limit, comparison);
411 }
412 super::scaletype::rank_on_every_start(pitches, limit, comparison, |tonic| {
413 let mut candidate = self.clone();
414 candidate.set_tonic(tonic);
415 candidate
416 })
417 }
418
419 pub fn simplification(&self) -> Simplification {
422 self.custom_simplification
423 .unwrap_or_else(|| self.scale_type.simplification())
424 }
425
426 fn walk(&self) -> Result<Vec<Interval>> {
428 match &self.custom_steps {
429 Some(steps) => Ok(steps.clone()),
430 None => self
431 .scale_type
432 .realization_steps()
433 .into_iter()
434 .map(step_interval)
435 .collect(),
436 }
437 }
438
439 pub fn degree_count(&self) -> usize {
442 match &self.custom_steps {
443 Some(steps) => steps.len(),
444 None => self.scale_type.degree_count(),
445 }
446 }
447
448 pub fn scale_type(&self) -> ScaleType {
450 self.scale_type
451 }
452
453 pub fn tonic(&self) -> &Pitch {
455 &self.tonic
456 }
457
458 pub fn pitches(&self) -> Result<Vec<Pitch>> {
468 let simplification = self.simplification();
469 let start = self.realization_start()?;
470 let mut pitches = Vec::with_capacity(self.scale_type.degree_count() + 1);
471 pitches.push(start.clone());
472
473 let mut current = start;
474 for step in self.walk()? {
475 current = advance(¤t, &step, simplification)?;
476 pitches.push(current.clone());
477 }
478 if self.custom_steps.is_none()
479 && let Some(beyond) = self.scale_type.beyond_terminus()
480 {
481 pitches.push(advance(¤t, &step_interval(beyond)?, simplification)?);
482 }
483 Ok(pitches)
484 }
485
486 fn realization_start(&self) -> Result<Pitch> {
494 let mut start = self.realized_tonic();
495 if self.custom_steps.is_some() {
496 return Ok(start);
497 }
498 let steps = self.scale_type.steps();
499 for step in steps
500 .iter()
501 .rev()
502 .take(self.scale_type.tonic_degree().saturating_sub(1))
503 {
504 start = start.transpose(&step_interval(step)?.reversed()?)?;
505 }
506 Ok(start)
507 }
508
509 pub fn realized_tonic(&self) -> Pitch {
512 let mut tonic = self.tonic.clone();
513 if tonic.octave().is_none() {
514 tonic.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
515 }
516 tonic
517 }
518
519 pub fn relative_major(&self) -> Result<Scale> {
526 self.relative(ScaleType::Major)
527 }
528
529 pub fn relative_minor(&self) -> Result<Scale> {
532 self.relative(ScaleType::Minor)
533 }
534
535 pub fn parallel_major(&self) -> Scale {
537 Scale::new(ScaleType::Major, self.tonic.clone())
538 }
539
540 pub fn parallel_minor(&self) -> Scale {
542 Scale::new(ScaleType::Minor, self.tonic.clone())
543 }
544
545 fn relative(&self, wanted: ScaleType) -> Result<Scale> {
551 let mode = self.scale_type.music21_descriptive_name();
552 let sharps = crate::key::pitch_to_sharps(&self.tonic, Some(mode))?;
553 let key = crate::key::KeySignature::new(sharps)
554 .try_as_key(Some(wanted.music21_descriptive_name()), None)?;
555 let here = self.realized_tonic();
556 let mut tonic = key.tonic();
557 tonic.set_octave(here.octave());
558 if tonic.ps() < here.ps() {
559 tonic.set_octave(tonic.octave().map(|octave| octave + 1));
560 }
561 Ok(Scale::new(wanted, tonic))
562 }
563
564 pub fn named_degrees(&self) -> Option<Vec<IntegerType>> {
571 if self.custom_steps.is_some() {
572 return None;
573 }
574 Some(
575 self.scale_type
576 .ascending_degrees()?
577 .iter()
578 .map(|°ree| IntegerType::from(degree))
579 .collect(),
580 )
581 }
582
583 fn degree_at_position(&self, position: usize) -> usize {
585 self.named_degrees()
586 .and_then(|degrees| degrees.get(position).copied())
587 .map_or(position + 1, |degree| degree as usize)
588 }
589
590 pub fn pitch_on_degree(&self, degree: IntegerType) -> Result<Option<Pitch>> {
604 let position = match self.named_degrees() {
605 Some(degrees) => match degrees.iter().position(|&named| named == degree) {
606 Some(position) => position,
607 None => return Ok(None),
608 },
609 None => {
610 let count = self.degree_count().max(1) as IntegerType;
611 (degree - 1).rem_euclid(count) as usize
612 }
613 };
614 let simplification = self.simplification();
615 let steps = self.walk()?;
616 let mut current = self.realization_start()?;
617 for index in 0..position {
618 current = advance(¤t, &steps[index % steps.len()], simplification)?;
619 }
620 Ok(Some(current))
621 }
622
623 pub fn pitch_at_degree(&self, degree: IntegerType) -> Result<Pitch> {
627 self.pitch_on_degree(degree)?.ok_or_else(|| {
628 Error::Scale(format!(
629 "{} has no degree {degree}",
630 self.scale_type.music21_descriptive_name()
631 ))
632 })
633 }
634 pub fn pitches_between(&self, minimum: &Pitch, maximum: &Pitch) -> Result<Vec<Pitch>> {
642 if maximum.ps() < minimum.ps() {
645 let mut descending = self.pitches_between(maximum, minimum)?;
646 descending.reverse();
647 return Ok(descending);
648 }
649 Ok(self
650 .placed_between(minimum.ps(), maximum.ps())?
651 .into_iter()
652 .map(|(pitch, _)| pitch)
653 .collect())
654 }
655
656 pub fn pitch_on_degree_between(
663 &self,
664 degree: IntegerType,
665 minimum: &Pitch,
666 maximum: &Pitch,
667 ) -> Result<Option<Pitch>> {
668 let (low, high) = if maximum.ps() < minimum.ps() {
669 (maximum, minimum)
670 } else {
671 (minimum, maximum)
672 };
673 let count = self.degree_count().max(1);
674 let wanted = |place: usize| match self.named_degrees() {
675 Some(_) => self.degree_at_position(place) as IntegerType == degree,
676 None => place % count == (degree - 1).rem_euclid(count as IntegerType) as usize,
677 };
678 Ok(self
679 .placed_between(low.ps(), high.ps())?
680 .into_iter()
681 .find(|(_, place)| wanted(*place))
682 .map(|(pitch, _)| pitch))
683 }
684
685 fn placed_between(&self, lowest: FloatType, highest: FloatType) -> Result<Vec<(Pitch, usize)>> {
688 if !self.repeats_at_the_octave() || self.cyclic {
689 return self.cycle_between(lowest, highest);
690 }
691 let simplification = self.simplification();
692 let steps = self.walk()?;
693 let period = self.period_in_octaves(&steps);
698 let mut current = self.realization_start()?;
699 shift_by_periods(&mut current, period, lowest, true);
700 let mut pitches = Vec::new();
701 let limit = steps.len() * (MAX_RANGE_OCTAVES + 2) + 1;
704 for index in 0..limit {
705 let sounding = current.ps();
706 if within(sounding, lowest, highest) {
707 pitches.push((current.clone(), index % steps.len()));
708 }
709 if sounding > highest - SLACK {
710 break;
711 }
712 current = advance(¤t, &steps[index % steps.len()], simplification)?;
713 }
714 Ok(pitches)
715 }
716
717 fn repeats_at_the_octave(&self) -> bool {
722 match &self.custom_steps {
723 Some(steps) => {
724 let risen: FloatType = steps.iter().map(Interval::semitones).sum();
725 (risen / 12.0 - (risen / 12.0).round()).abs() < 1e-9
726 }
727 None => true,
728 }
729 }
730
731 fn cycle_between(&self, lowest: FloatType, highest: FloatType) -> Result<Vec<(Pitch, usize)>> {
739 let steps = self.walk()?;
740 let period: FloatType = steps.iter().map(Interval::semitones).sum();
741 if period <= 0.0 {
742 return Err(Error::Scale(
743 "a cycle that does not rise cannot be walked over a range".to_string(),
744 ));
745 }
746 let simplification = self.simplification();
747 let start = self.realization_start()?;
748 let limit = steps.len() * 512;
749 let mut found = Vec::new();
750
751 let mut current = start.clone();
756 for index in 0..limit {
757 let sounding = current.ps();
758 if within(sounding, lowest, highest) {
759 found.push((current.clone(), index % steps.len()));
760 }
761 if sounding > highest - SLACK {
762 break;
763 }
764 current = advance(¤t, &steps[index % steps.len()], simplification)?;
765 }
766
767 let mut current = start;
768 for back in 1..=limit {
769 let place = (steps.len() - back % steps.len()) % steps.len();
770 current = advance(¤t, &steps[place].reversed()?, simplification)?;
771 let sounding = current.ps();
772 if within(sounding, lowest, highest) {
773 found.push((current.clone(), place));
774 }
775 if sounding < lowest + SLACK {
776 break;
777 }
778 }
779 found.sort_by(|left, right| left.0.ps().total_cmp(&right.0.ps()));
780 Ok(found)
781 }
782
783 fn cycle_places_of(&self, pitch: &Pitch, comparison: DegreeComparison) -> Result<Vec<usize>> {
793 let sounding = Self::sounding(pitch).ps();
794 let wanted = comparison.key(pitch);
795 let mut places = Vec::new();
796 for (candidate, place) in self.cycle_between(sounding - 12.0, sounding + 12.0)? {
797 if comparison.key(&candidate) == wanted && !places.contains(&place) {
798 places.push(place);
799 }
800 }
801 Ok(places)
802 }
803
804 fn cycle_place_sounding(
808 &self,
809 pitch: &Pitch,
810 comparison: DegreeComparison,
811 ) -> Result<Option<(Pitch, usize)>> {
812 let sounding = Self::sounding(pitch).ps();
813 let wanted = comparison.key(pitch);
814 Ok(self
815 .cycle_between(sounding - 1e-9, sounding + 1e-9)?
816 .into_iter()
817 .find(|(candidate, _)| comparison.key(candidate) == wanted))
818 }
819
820 fn sounding(pitch: &Pitch) -> Pitch {
822 let mut heard = pitch.clone();
823 if heard.octave().is_none() {
824 heard.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
825 }
826 heard
827 }
828
829 fn cycle_steps_from(
834 &self,
835 origin: &Pitch,
836 steps: IntegerType,
837 neighbour_below: Option<bool>,
838 ) -> Result<Pitch> {
839 let walk = self.walk()?;
840 let period: FloatType = walk.iter().map(Interval::semitones).sum();
841 let simplification = self.simplification();
842 let at = Self::sounding(origin).ps();
843 let ascending = steps > 0;
844 let (mut current, mut place, mut remaining) =
845 match self.cycle_place_sounding(origin, DegreeComparison::PitchClass)? {
848 Some((pitch, place)) => (pitch, place, steps),
849 None => {
850 let take_below = neighbour_below.unwrap_or(!ascending);
851 let beside = self.cycle_between(at - period, at + period)?;
852 let neighbour = if take_below {
853 beside.into_iter().rfind(|(pitch, _)| pitch.ps() < at)
854 } else {
855 beside.into_iter().find(|(pitch, _)| pitch.ps() > at)
856 };
857 let (pitch, place) = neighbour
858 .ok_or_else(|| Error::Scale(format!("no scale pitch beside {origin}")))?;
859 let remaining = if neighbour_below.is_some() {
860 steps
861 } else {
862 steps - steps.signum()
863 };
864 (pitch, place, remaining)
865 }
866 };
867 while remaining > 0 {
868 current = advance(¤t, &walk[place], simplification)?;
869 place = (place + 1) % walk.len();
870 remaining -= 1;
871 }
872 while remaining < 0 {
873 place = (place + walk.len() - 1) % walk.len();
874 current = advance(¤t, &walk[place].reversed()?, simplification)?;
875 remaining += 1;
876 }
877 if origin.octave().is_none() {
878 current.octave_setter(None);
879 }
880 Ok(current)
881 }
882
883 pub fn is_realizable(&self) -> bool {
890 match &self.custom_steps {
891 Some(steps) => steps.iter().map(Interval::semitones).sum::<FloatType>() > 0.0,
892 None => true,
893 }
894 }
895
896 pub fn octave_duplicating(&self) -> bool {
901 if self.cyclic {
902 return false;
903 }
904 match &self.custom_steps {
905 Some(steps) => self.period_in_octaves(steps) == 1,
906 None => true,
907 }
908 }
909
910 fn period_in_octaves(&self, steps: &[Interval]) -> IntegerType {
914 let semitones: FloatType = steps.iter().map(Interval::semitones).sum();
915 ((semitones / 12.0).round() as IntegerType).max(1)
916 }
917
918 pub fn final_pitch(&self) -> Result<Pitch> {
921 self.pitch_at_degree(self.scale_type.tonic_degree() as IntegerType)
922 }
923
924 pub fn dominant(&self) -> Result<Pitch> {
926 self.pitch_at_degree(self.scale_type.dominant_degree() as IntegerType)
927 }
928
929 pub fn leading_tone(&self) -> Result<Pitch> {
933 let seventh = self.pitch_at_degree(7)?;
934 let tonic = self.final_pitch()?;
935 let distance = seventh.midi() - tonic.midi();
936 if distance == 11 {
937 return Ok(seventh);
938 }
939 let alter = seventh.accidental().alter() + FloatType::from(11 - distance);
940 let mut raised = seventh.clone();
941 raised.set_accidental(crate::pitch::Accidental::new(alter)?);
942 Ok(raised)
943 }
944
945 pub fn derive_by_degree(&self, degree: usize, pitch: &Pitch) -> Result<Scale> {
950 let implicit_octave = Some(crate::defaults::PITCH_OCTAVE as IntegerType);
951 let mut tonic = self.tonic.clone();
952 if tonic.octave().is_none() {
953 tonic.octave_setter(implicit_octave);
954 }
955 let degree_pitch =
956 Scale::new(self.scale_type, tonic.clone()).pitch_at_degree(degree as IntegerType)?;
957 let up_to_degree = Interval::between_pitches(&tonic, °ree_pitch)?;
958 let mut reference = pitch.clone();
959 if reference.octave().is_none() {
960 reference.octave_setter(implicit_octave);
961 }
962 let new_tonic = reference.transpose(&up_to_degree.reversed()?)?;
963 Ok(Scale::new(self.scale_type, new_tonic))
964 }
965
966 pub fn transpose(&self, interval: &Interval) -> Result<Scale> {
968 Ok(Scale::new(self.scale_type, self.tonic.transpose(interval)?))
969 }
970
971 pub fn chord(&self) -> Result<Chord> {
974 Chord::new(self.pitches()?.as_slice())
975 }
976
977 pub fn pitches_from_scale_degrees(&self, degrees: &[usize]) -> Result<Vec<Pitch>> {
981 let octave = self.pitches()?;
982 let count = octave.len().saturating_sub(1).max(1);
987 Ok(octave
988 .into_iter()
989 .enumerate()
990 .filter(|(index, _)| degrees.contains(&(index % count + 1)))
991 .map(|(_, pitch)| pitch)
992 .collect())
993 }
994
995 pub fn pitches_from_scale_degrees_between(
999 &self,
1000 degrees: &[usize],
1001 minimum: &Pitch,
1002 maximum: &Pitch,
1003 ) -> Result<Vec<Pitch>> {
1004 let wanted: Vec<String> = self
1005 .pitches_from_scale_degrees(degrees)?
1006 .iter()
1007 .map(Pitch::name)
1008 .collect();
1009 Ok(self
1010 .pitches_between(minimum, maximum)?
1011 .into_iter()
1012 .filter(|pitch| wanted.contains(&pitch.name()))
1013 .collect())
1014 }
1015
1016 pub fn interval_between_degrees(&self, start: usize, end: usize) -> Result<Interval> {
1021 Interval::between_pitches(
1022 &self.pitch_at_degree(start as IntegerType)?,
1023 &self.pitch_at_degree(end as IntegerType)?,
1024 )
1025 }
1026
1027 pub fn is_next(&self, other: &Pitch, origin: &Pitch, steps: usize) -> Result<bool> {
1031 Ok(self.next_pitch_above(origin, steps)?.name() == other.name())
1032 }
1033
1034 pub fn match_pitches(&self, pitches: &[Pitch]) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
1039 self.match_pitches_by(pitches, DegreeComparison::Name)
1040 }
1041
1042 pub fn match_pitches_by(
1048 &self,
1049 pitches: &[Pitch],
1050 comparison: DegreeComparison,
1051 ) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
1052 let realized = self.realized_in_implicit_octave()?;
1053 let degrees: Vec<String> = realized.iter().map(|p| comparison.key(p)).collect();
1054 let mut matched = Vec::new();
1055 let mut unmatched = Vec::new();
1056 for pitch in pitches {
1057 let mut heard = pitch.clone();
1058 if heard.octave().is_none() {
1059 heard.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
1060 }
1061 if degrees.contains(&comparison.key(&heard)) {
1062 matched.push(heard);
1063 } else {
1064 unmatched.push(heard);
1065 }
1066 }
1067 Ok((matched, unmatched))
1068 }
1069
1070 pub fn find_missing(&self, pitches: &[Pitch]) -> Result<Vec<Pitch>> {
1074 let present: Vec<u8> = pitches.iter().map(root::pitch_class).collect();
1075 Ok(self
1076 .realized_in_implicit_octave()?
1077 .into_iter()
1078 .filter(|candidate| !present.contains(&root::pitch_class(candidate)))
1079 .collect())
1080 }
1081
1082 pub fn solfeg(&self, pitch: &Pitch, variant: SolfegVariant, chromatic: bool) -> Result<String> {
1088 let (degree, accidental) = self.degree_and_accidental_of(pitch)?;
1089 if degree > 7 {
1090 return Err(crate::error::Error::Scale(
1091 "Cannot call solfeg on non-7-degree scales".to_string(),
1092 ));
1093 }
1094 let table = match variant {
1095 SolfegVariant::Music21 => &SOLFEG_SYLLABLES,
1096 SolfegVariant::Humdrum => &HUMDRUM_SOLFEG_SYLLABLES,
1097 };
1098 let alter = if chromatic {
1099 accidental.map_or(0, |accidental| accidental.alter() as IntegerType)
1100 } else {
1101 0
1102 };
1103 let column = usize::try_from(alter + 2)
1104 .ok()
1105 .filter(|column| *column < 5)
1106 .ok_or_else(|| {
1107 crate::error::Error::Scale(format!(
1108 "no solfeg syllable for an alteration of {alter}"
1109 ))
1110 })?;
1111 Ok(table[degree - 1][column].to_string())
1112 }
1113
1114 fn realized_in_implicit_octave(&self) -> Result<Vec<Pitch>> {
1115 self.pitches()
1116 }
1117
1118 pub fn degree_of_by(
1121 &self,
1122 pitch: &Pitch,
1123 comparison: DegreeComparison,
1124 ) -> Result<Option<usize>> {
1125 if !self.repeats_at_the_octave() {
1126 return Ok(self
1127 .cycle_places_of(pitch, comparison)?
1128 .first()
1129 .map(|place| self.degree_at_position(*place)));
1130 }
1131 let wanted = comparison.key(pitch);
1132 Ok(self
1133 .scale_pitches()?
1134 .iter()
1135 .position(|candidate| comparison.key(candidate) == wanted)
1136 .map(|index| self.degree_at_position(index)))
1137 }
1138
1139 pub fn degrees_of_by(&self, pitch: &Pitch, comparison: DegreeComparison) -> Result<Vec<usize>> {
1146 if !self.repeats_at_the_octave() {
1147 return Ok(self
1148 .cycle_places_of(pitch, comparison)?
1149 .into_iter()
1150 .map(|place| self.degree_at_position(place))
1151 .collect());
1152 }
1153 let wanted = comparison.key(pitch);
1154 Ok(self
1155 .scale_pitches()?
1156 .iter()
1157 .enumerate()
1158 .filter(|(_, candidate)| comparison.key(candidate) == wanted)
1159 .map(|(index, _)| self.degree_at_position(index))
1160 .collect())
1161 }
1162
1163 pub fn degree_of(&self, pitch: &Pitch) -> Result<Option<usize>> {
1166 if !self.repeats_at_the_octave() {
1167 return self.degree_of_by(pitch, DegreeComparison::Name);
1168 }
1169 let name = pitch.name();
1170 Ok(self
1171 .scale_pitches()?
1172 .iter()
1173 .position(|candidate| candidate.name() == name)
1174 .map(|index| self.degree_at_position(index)))
1175 }
1176
1177 pub fn degree_of_pitch_class(&self, pitch: &Pitch) -> Result<Option<usize>> {
1180 if !self.repeats_at_the_octave() {
1181 return self.degree_of_by(pitch, DegreeComparison::PitchClass);
1182 }
1183 let pitch_class = pitch.pitch_class().number();
1184 Ok(self
1185 .scale_pitches()?
1186 .iter()
1187 .position(|candidate| candidate.pitch_class().number() == pitch_class)
1188 .map(|index| self.degree_at_position(index)))
1189 }
1190
1191 pub fn next_pitch_above(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
1194 self.pitch_steps_from(origin, steps as IntegerType, None)
1195 }
1196
1197 pub fn next_pitch_beside(
1202 &self,
1203 origin: &Pitch,
1204 steps: IntegerType,
1205 below: bool,
1206 ) -> Result<Pitch> {
1207 self.pitch_steps_from(origin, steps, Some(below))
1208 }
1209
1210 pub fn next_pitch_below(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
1213 self.pitch_steps_from(origin, -(steps as IntegerType), None)
1214 }
1215
1216 pub fn degree_and_accidental_of(
1221 &self,
1222 pitch: &Pitch,
1223 ) -> Result<(usize, Option<crate::pitch::Accidental>)> {
1224 if let Some(degree) = self.degree_of(pitch)? {
1225 return Ok((degree, None));
1226 }
1227 let pitches = self.scale_pitches()?;
1228 let index = pitches
1229 .iter()
1230 .position(|candidate| candidate.step() == pitch.step())
1231 .ok_or_else(|| {
1232 crate::error::Error::Scale(format!(
1233 "cannot get any scale degree for {pitch} in {self:?}"
1234 ))
1235 })?;
1236 let difference = pitch.accidental().alter() - pitches[index].accidental().alter();
1237 let accidental = if difference == 0.0 {
1238 None
1239 } else {
1240 Some(crate::pitch::Accidental::new(difference)?)
1241 };
1242 Ok((index + 1, accidental))
1243 }
1244
1245 fn scale_pitches(&self) -> Result<Vec<Pitch>> {
1246 let mut pitches = self.pitches()?;
1247 pitches.truncate(self.degree_count());
1248 Ok(pitches)
1249 }
1250
1251 fn pitch_steps_from(
1252 &self,
1253 origin: &Pitch,
1254 steps: IntegerType,
1255 neighbour_below: Option<bool>,
1256 ) -> Result<Pitch> {
1257 self.pitch_steps_from_place(origin, steps, neighbour_below, 0)
1258 }
1259
1260 pub fn places_of(&self, origin: &Pitch) -> Result<usize> {
1272 Ok(self.places_on(origin)?.len())
1273 }
1274
1275 fn places_on(&self, origin: &Pitch) -> Result<Vec<(usize, IntegerType)>> {
1280 if !self.repeats_at_the_octave() {
1281 return Ok(self
1282 .cycle_place_sounding(origin, DegreeComparison::PitchClass)?
1283 .map(|(_, place)| (place, 0))
1284 .into_iter()
1285 .collect());
1286 }
1287 let pitches = self.scale_pitches()?;
1288 let origin_ps = origin.ps();
1289 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
1290 let mut places = (base_shift - 1..=base_shift + 1)
1291 .flat_map(|shift| {
1292 pitches.iter().enumerate().map(move |(index, pitch)| {
1293 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
1294 })
1295 })
1296 .filter(|(ps, _, _)| (ps - origin_ps).abs() < 1e-9)
1297 .map(|(_, index, shift)| (index, shift))
1298 .collect::<Vec<_>>();
1299 places.sort_by_key(|&(index, shift)| (self.degree_at_position(index), index, shift));
1300 Ok(places)
1301 }
1302
1303 pub fn next_pitch_below_from(
1306 &self,
1307 origin: &Pitch,
1308 steps: usize,
1309 place: usize,
1310 ) -> Result<Pitch> {
1311 self.pitch_steps_from_place(origin, -(steps as IntegerType), None, place)
1312 }
1313
1314 pub fn next_pitch_above_from(
1316 &self,
1317 origin: &Pitch,
1318 steps: usize,
1319 place: usize,
1320 ) -> Result<Pitch> {
1321 self.pitch_steps_from_place(origin, steps as IntegerType, None, place)
1322 }
1323
1324 fn pitch_steps_from_place(
1325 &self,
1326 origin: &Pitch,
1327 steps: IntegerType,
1328 neighbour_below: Option<bool>,
1329 place: usize,
1330 ) -> Result<Pitch> {
1331 if steps == 0 {
1332 return Err(crate::error::Error::Scale(
1333 "step size must be at least 1".to_string(),
1334 ));
1335 }
1336 if !self.repeats_at_the_octave() {
1337 return self.cycle_steps_from(origin, steps, neighbour_below);
1338 }
1339 let pitches = self.scale_pitches()?;
1340 let count = pitches.len() as IntegerType;
1341 let origin_ps = origin.ps();
1342 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
1343 let candidates = (base_shift - 1..=base_shift + 1)
1344 .flat_map(|shift| {
1345 pitches.iter().enumerate().map(move |(index, pitch)| {
1346 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
1347 })
1348 })
1349 .collect::<Vec<_>>();
1350 let ascending = steps > 0;
1351 let standing = self.places_on(origin)?;
1352 let (index, shift, remaining) = match standing.get(place % standing.len().max(1)).copied() {
1353 Some((index, shift)) => (index, shift, steps),
1354 None => {
1355 let take_below = neighbour_below.unwrap_or(!ascending);
1359 let neighbour = if take_below {
1360 candidates
1361 .iter()
1362 .filter(|(ps, _, _)| *ps < origin_ps)
1363 .max_by(|left, right| left.0.total_cmp(&right.0))
1364 } else {
1365 candidates
1366 .iter()
1367 .filter(|(ps, _, _)| *ps > origin_ps)
1368 .min_by(|left, right| left.0.total_cmp(&right.0))
1369 };
1370 let &(_, index, shift) = neighbour.ok_or_else(|| {
1371 crate::error::Error::Scale(format!("no scale pitch beside {origin}"))
1372 })?;
1373 let remaining = if neighbour_below.is_some() {
1374 steps
1375 } else {
1376 steps - steps.signum()
1377 };
1378 (index, shift, remaining)
1379 }
1380 };
1381 let total = index as IntegerType + remaining;
1382 let mut pitch = pitches[total.rem_euclid(count) as usize].clone();
1383 let octave_shift = shift + total.div_euclid(count);
1384 let octave = origin.octave().map(|_| {
1385 pitch
1386 .octave()
1387 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1388 + octave_shift
1389 });
1390 pitch.octave_setter(octave);
1391 Ok(pitch)
1392 }
1393}
1394
1395fn within(sounding: FloatType, lowest: FloatType, highest: FloatType) -> bool {
1399 sounding > lowest - SLACK && sounding < highest + SLACK
1400}
1401
1402const SLACK: FloatType = 1e-5;
1404
1405fn shift_by_periods(pitch: &mut Pitch, period: IntegerType, target: FloatType, below: bool) {
1412 let semitones = 12.0 * FloatType::from(period);
1413 let shift = |pitch: &mut Pitch, by: IntegerType| {
1414 let octave = pitch.octave().unwrap_or(0);
1415 pitch.octave_setter(Some(octave + by));
1416 };
1417 for _ in 0..64 {
1420 let ps = pitch.ps();
1421 let too_high = if below {
1422 ps > target + SLACK
1423 } else {
1424 ps - semitones >= target - SLACK
1425 };
1426 let too_low = if below {
1427 ps + semitones <= target + SLACK
1428 } else {
1429 ps < target - SLACK
1430 };
1431 if too_high {
1432 shift(pitch, -period);
1433 } else if too_low {
1434 shift(pitch, period);
1435 } else {
1436 break;
1437 }
1438 }
1439}
1440
1441pub fn fix_default_octave_for_pitch_list(pitches: &[Pitch]) -> Vec<Pitch> {
1448 let mut risen: Vec<Pitch> = Vec::with_capacity(pitches.len());
1449 let mut last_ps = 0.0;
1450 let mut last_octave =
1451 pitches
1452 .first()
1453 .map_or(crate::defaults::PITCH_OCTAVE as IntegerType, |pitch| {
1454 pitch
1455 .octave()
1456 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1457 });
1458 for pitch in pitches {
1459 let mut pitch = pitch.clone();
1460 if pitch.octave().is_none() {
1461 if last_ps > pitch.ps() {
1462 pitch.octave_setter(Some(last_octave));
1463 }
1464 while last_ps > pitch.ps() {
1465 last_octave += 1;
1466 pitch.octave_setter(Some(last_octave));
1467 }
1468 }
1469 last_ps = pitch.ps();
1470 last_octave = pitch
1471 .octave()
1472 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType);
1473 risen.push(pitch);
1474 }
1475 risen
1476}
1477
1478#[cfg(test)]
1479mod tests {
1480 use super::*;
1481
1482 #[test]
1484 fn octaves_left_out_are_filled_in_as_music21_fills_them() {
1485 let cases = [
1486 ("C5 A F3 G B", "C5 A5 F3 G B"),
1487 ("C3 D6 E", "C3 D6 E6"),
1488 ("A B C D E F G A", "A B C5 D5 E5 F5 G5 A5"),
1489 ("C6 B A", "C6 B6 A7"),
1490 ("B#3 C D", "B#3 C D"),
1491 ];
1492 for (given, expected) in cases {
1493 let pitches: Vec<Pitch> = given
1494 .split(' ')
1495 .map(|name| Pitch::from_name(name).unwrap())
1496 .collect();
1497 let risen: Vec<String> = fix_default_octave_for_pitch_list(&pitches)
1498 .iter()
1499 .map(Pitch::name_with_octave)
1500 .collect();
1501 assert_eq!(risen.join(" "), expected, "{given}");
1502 }
1503 }
1504}