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))]
56 cyclic: bool,
57}
58
59impl Scale {
60 pub fn roman_numeral(&self, degree: u8) -> Result<RomanNumeral> {
64 if !(1..=7).contains(°ree) {
65 return Err(Error::Scale(format!(
66 "a roman numeral stands on a degree from 1 to 7, not {degree}"
67 )));
68 }
69 let key = Key::from_tonic_mode(&self.tonic.name(), "major")?;
70 RomanNumeral::over_scale(
71 degree_to_roman(degree),
72 key,
73 Some(self.clone()),
74 Minor67Default::default(),
75 Minor67Default::default(),
76 false,
77 )
78 }
79
80 pub fn tune(&self, stream: &mut crate::Stream) -> Result<()> {
87 let scale_pitches = self.pitches()?;
88 let names: Vec<String> = scale_pitches.iter().map(Pitch::name).collect();
89 let tuned = |pitch: &Pitch| -> Result<Option<Pitch>> {
90 let mut candidates = pitch.all_common_enharmonics(2);
91 candidates.push(pitch.clone());
92 for candidate in candidates {
93 let Some(index) = names.iter().position(|name| *name == candidate.name()) else {
94 continue;
95 };
96 let mut target = scale_pitches[index].clone();
97 target.set_octave(candidate.octave());
98 let spelled = target
99 .all_common_enharmonics(2)
100 .into_iter()
101 .find(|spelling| spelling.name() == pitch.name());
102 return Ok(Some(spelled.unwrap_or(target)));
103 }
104 Ok(None)
105 };
106 for event in stream.events_mut() {
107 match event.element_mut() {
108 crate::stream::StreamElement::Note(note) => {
109 if let Some(pitch) = tuned(note.pitch())? {
110 note.set_pitch(pitch);
111 }
112 }
113 crate::stream::StreamElement::Chord(chord) => {
114 for note in chord.notes_mut() {
115 if let Some(pitch) = tuned(note.pitch())? {
116 note.set_pitch(pitch);
117 }
118 }
119 }
120 crate::stream::StreamElement::Stream(inner) => self.tune(inner)?,
121 _ => {}
122 }
123 }
124 Ok(())
125 }
126
127 pub fn scala_data(&self) -> Result<ScalaScale> {
131 let pitches = self.pitches()?;
132 let (Some(tonic), Some(closing)) = (pitches.first(), pitches.last()) else {
133 return Err(Error::Scale(
134 "a scale with no pitches has no degrees".to_string(),
135 ));
136 };
137 let cents = |pitch: &Pitch| ScalaDegree::Cents((pitch.ps() - tonic.ps()) * 100.0);
138 let mut degrees = vec![ScalaDegree::Ratio(crate::tuningsystem::Ratio::new(1, 1))];
139 degrees.extend(pitches[1..pitches.len() - 1].iter().map(cents));
140 Ok(ScalaScale::new(
141 format!(
142 "{} {}",
143 self.tonic.name(),
144 self.scale_type.music21_descriptive_name()
145 ),
146 degrees,
147 cents(closing),
148 ))
149 }
150
151 pub fn new(scale_type: ScaleType, tonic: Pitch) -> Self {
153 Self {
154 scale_type,
155 tonic,
156 custom_steps: None,
157 custom_simplification: None,
158 cyclic: false,
159 }
160 }
161
162 pub fn from_pitches(pitches: &[Pitch]) -> Result<Self> {
169 let pitches = fix_default_octave_for_pitch_list(pitches);
170 let pitches = pitches.as_slice();
171 let Some(tonic) = pitches.first() else {
172 return Err(crate::error::Error::Scale(
173 "a scale needs at least one pitch".to_string(),
174 ));
175 };
176 let mut steps = Vec::with_capacity(pitches.len());
177 for pair in pitches.windows(2) {
178 steps.push(Interval::between_pitches(&pair[0], &pair[1])?);
179 }
180 let last = pitches.last().unwrap_or(tonic);
187 if last.name() != tonic.name() {
188 let mut closing = tonic.clone();
189 let mut octave = tonic
190 .octave()
191 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType);
192 closing.octave_setter(Some(octave));
193 if last.ps() > tonic.ps() {
194 while closing.ps() < last.ps() {
195 octave += 1;
196 closing.octave_setter(Some(octave));
197 }
198 } else {
199 while closing.ps() > last.ps() {
200 octave -= 1;
201 closing.octave_setter(Some(octave));
202 }
203 }
204 steps.push(Interval::between_pitches(last, &closing)?);
205 }
206 Ok(Self {
207 scale_type: ScaleType::Major,
208 tonic: tonic.clone(),
209 custom_steps: Some(steps),
210 custom_simplification: None,
211 cyclic: false,
212 })
213 }
214
215 pub fn from_steps(tonic: Pitch, steps: Vec<Interval>) -> Result<Self> {
223 if steps.is_empty() {
224 return Err(crate::error::Error::Scale(
225 "a scale needs at least one step".to_string(),
226 ));
227 }
228 Ok(Self {
229 scale_type: ScaleType::Major,
230 tonic,
231 custom_steps: Some(steps),
232 custom_simplification: Some(Simplification::MaxAccidental),
236 cyclic: false,
237 })
238 }
239
240 pub fn from_cycle(tonic: Pitch, steps: Vec<Interval>) -> Result<Self> {
255 if let Some(filled) = super::scaletype::cycle_within_octave(&steps)? {
256 return Self::from_steps(tonic, filled);
257 }
258 let mut scale = Self::from_steps(tonic, steps)?;
259 scale.cyclic = true;
260 Ok(scale)
261 }
262
263 pub fn with_simplification(mut self, simplification: Simplification) -> Self {
267 self.custom_simplification = Some(simplification);
268 self
269 }
270
271 pub fn from_scala(tonic: Pitch, scala: &ScalaScale) -> Result<Self> {
279 Ok(Self::from_cycle(tonic, scala.interval_sequence()?)?
280 .with_simplification(Simplification::MostCommon))
281 }
282
283 pub fn descending(&self) -> Scale {
285 if self.custom_steps.is_some() {
286 return self.clone();
287 }
288 if let Some(scale_type) = self.scale_type.descending_form() {
289 let descending = Scale::new(scale_type, self.tonic.clone());
292 return if scale_type.simplification() == self.simplification() {
293 descending
294 } else {
295 descending.with_simplification(self.simplification())
296 };
297 }
298 if let Some(steps) = self.scale_type.descending_steps() {
302 let walked: Result<Vec<Interval>> = steps.iter().copied().map(step_interval).collect();
303 if let Ok(walked) = walked {
304 return Self {
305 scale_type: self.scale_type,
306 tonic: self.tonic.clone(),
307 custom_steps: Some(walked),
308 custom_simplification: None,
309 cyclic: false,
310 };
311 }
312 }
313 self.clone()
314 }
315
316 pub fn pitches_descending(&self) -> Result<Vec<Pitch>> {
319 let ascending = self.pitches()?;
326 let bottom = ascending.iter().min_by(|a, b| a.ps().total_cmp(&b.ps()));
327 let top = ascending.iter().max_by(|a, b| a.ps().total_cmp(&b.ps()));
328 let (Some(bottom), Some(top)) = (bottom, top) else {
329 return Ok(ascending);
330 };
331 self.pitches_between_descending(bottom, top)
332 }
333
334 pub fn pitches_between_descending(
336 &self,
337 minimum: &Pitch,
338 maximum: &Pitch,
339 ) -> Result<Vec<Pitch>> {
340 self.descending().walked_down_between(minimum, maximum)
341 }
342
343 fn walked_down_between(&self, minimum: &Pitch, maximum: &Pitch) -> Result<Vec<Pitch>> {
352 let (lowest, highest) = (
353 minimum.ps().min(maximum.ps()),
354 minimum.ps().max(maximum.ps()),
355 );
356 if !self.repeats_at_the_octave() || self.cyclic {
357 let mut pitches: Vec<Pitch> = self
358 .cycle_between(lowest, highest)?
359 .into_iter()
360 .map(|(pitch, _)| pitch)
361 .collect();
362 pitches.reverse();
363 return Ok(pitches);
364 }
365 let simplification = self.simplification();
366 let steps = self.walk()?;
367 let period = self.period_in_octaves(&steps);
368 let terminus = self.terminus_name();
369 let mut current = self.realization_start()?;
370 shift_by_periods(&mut current, period, highest, false);
371 let mut pitches = Vec::new();
372 let limit = steps.len() * (MAX_RANGE_OCTAVES + 2) + 1;
373 for index in 0..limit {
374 let sounding = current.ps();
375 if within(sounding, lowest, highest) {
376 pitches.push(current.clone());
377 }
378 if sounding < lowest + SLACK {
379 break;
380 }
381 let place = steps.len() - 1 - index % steps.len();
382 current = advance(¤t, &steps[place].reversed()?, simplification)?;
383 if place == 0 {
384 current = spell_terminus(current, terminus.as_deref())?;
385 }
386 }
387 Ok(pitches)
388 }
389
390 fn terminus_name(&self) -> Option<String> {
401 let from_tonic = self.custom_steps.is_some() || self.scale_type.tonic_degree() == 1;
402 (from_tonic && !self.cyclic && self.repeats_at_the_octave() && self.octave_duplicating())
403 .then(|| self.realized_tonic().name())
404 }
405
406 pub fn custom_steps(&self) -> Option<&[Interval]> {
410 self.custom_steps.as_deref()
411 }
412
413 pub fn is_cyclic(&self) -> bool {
416 self.cyclic
417 }
418
419 pub fn is_custom(&self) -> bool {
421 self.custom_steps.is_some()
422 }
423
424 pub fn set_tonic(&mut self, tonic: Pitch) {
426 self.tonic = tonic;
427 }
428
429 pub fn derive_ranked_by(
433 &self,
434 pitches: &[Pitch],
435 limit: Option<usize>,
436 comparison: DegreeComparison,
437 ) -> Result<Vec<(usize, Scale)>> {
438 if !self.is_custom() {
439 return self.scale_type.derive_ranked_by(pitches, limit, comparison);
440 }
441 super::scaletype::rank_on_every_start(pitches, limit, comparison, |tonic| {
442 let mut candidate = self.clone();
443 candidate.set_tonic(tonic);
444 candidate
445 })
446 }
447
448 pub fn simplification(&self) -> Simplification {
451 self.custom_simplification
452 .unwrap_or_else(|| self.scale_type.simplification())
453 }
454
455 fn walk(&self) -> Result<Vec<Interval>> {
457 match &self.custom_steps {
458 Some(steps) => Ok(steps.clone()),
459 None => self
460 .scale_type
461 .realization_steps()
462 .into_iter()
463 .map(step_interval)
464 .collect(),
465 }
466 }
467
468 pub fn degree_count(&self) -> usize {
471 match &self.custom_steps {
472 Some(steps) => steps.len(),
473 None => self.scale_type.degree_count(),
474 }
475 }
476
477 pub fn scale_type(&self) -> ScaleType {
479 self.scale_type
480 }
481
482 pub fn tonic(&self) -> &Pitch {
484 &self.tonic
485 }
486
487 pub fn pitches(&self) -> Result<Vec<Pitch>> {
497 let simplification = self.simplification();
498 let start = self.realization_start()?;
499 let mut pitches = Vec::with_capacity(self.scale_type.degree_count() + 1);
500 pitches.push(start.clone());
501
502 let terminus = self.terminus_name();
503 let steps = self.walk()?;
504 let mut current = start;
505 for (index, step) in steps.iter().enumerate() {
506 current = advance(¤t, step, simplification)?;
507 if (index + 1) % steps.len() == 0 {
508 current = spell_terminus(current, terminus.as_deref())?;
509 }
510 pitches.push(current.clone());
511 }
512 if self.custom_steps.is_none()
513 && let Some(beyond) = self.scale_type.beyond_terminus()
514 {
515 pitches.push(advance(¤t, &step_interval(beyond)?, simplification)?);
516 }
517 Ok(pitches)
518 }
519
520 fn realization_start(&self) -> Result<Pitch> {
528 let mut start = self.realized_tonic();
529 if self.custom_steps.is_some() {
530 return Ok(start);
531 }
532 let steps = self.scale_type.steps();
533 for step in steps
534 .iter()
535 .rev()
536 .take(self.scale_type.tonic_degree().saturating_sub(1))
537 {
538 start = start.transpose(&step_interval(step)?.reversed()?)?;
539 }
540 Ok(start)
541 }
542
543 pub fn realized_tonic(&self) -> Pitch {
546 let mut tonic = self.tonic.clone();
547 if tonic.octave().is_none() {
548 tonic.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
549 }
550 tonic
551 }
552
553 pub fn relative_major(&self) -> Result<Scale> {
560 self.relative(ScaleType::Major)
561 }
562
563 pub fn relative_minor(&self) -> Result<Scale> {
566 self.relative(ScaleType::Minor)
567 }
568
569 pub fn parallel_major(&self) -> Scale {
571 Scale::new(ScaleType::Major, self.tonic.clone())
572 }
573
574 pub fn parallel_minor(&self) -> Scale {
576 Scale::new(ScaleType::Minor, self.tonic.clone())
577 }
578
579 fn relative(&self, wanted: ScaleType) -> Result<Scale> {
585 let mode = self.scale_type.music21_descriptive_name();
586 let sharps = crate::key::pitch_to_sharps(&self.tonic, Some(mode))?;
587 let key = crate::key::KeySignature::new(sharps)
588 .try_as_key(Some(wanted.music21_descriptive_name()), None)?;
589 let here = self.realized_tonic();
590 let mut tonic = key.tonic();
591 tonic.set_octave(here.octave());
592 if tonic.ps() < here.ps() {
593 tonic.set_octave(tonic.octave().map(|octave| octave + 1));
594 }
595 Ok(Scale::new(wanted, tonic))
596 }
597
598 pub fn named_degrees(&self) -> Option<Vec<IntegerType>> {
605 if self.custom_steps.is_some() {
606 return None;
607 }
608 Some(
609 self.scale_type
610 .ascending_degrees()?
611 .iter()
612 .map(|°ree| IntegerType::from(degree))
613 .collect(),
614 )
615 }
616
617 fn degree_at_position(&self, position: usize) -> usize {
619 self.named_degrees()
620 .and_then(|degrees| degrees.get(position).copied())
621 .map_or(position + 1, |degree| degree as usize)
622 }
623
624 pub fn pitch_on_degree(&self, degree: IntegerType) -> Result<Option<Pitch>> {
638 let position = match self.named_degrees() {
639 Some(degrees) => match degrees.iter().position(|&named| named == degree) {
640 Some(position) => position,
641 None => return Ok(None),
642 },
643 None => {
644 let count = self.degree_count().max(1) as IntegerType;
645 (degree - 1).rem_euclid(count) as usize
646 }
647 };
648 let simplification = self.simplification();
649 let steps = self.walk()?;
650 let mut current = self.realization_start()?;
651 for index in 0..position {
652 current = advance(¤t, &steps[index % steps.len()], simplification)?;
653 }
654 Ok(Some(current))
655 }
656
657 pub fn pitch_at_degree(&self, degree: IntegerType) -> Result<Pitch> {
661 self.pitch_on_degree(degree)?.ok_or_else(|| {
662 Error::Scale(format!(
663 "{} has no degree {degree}",
664 self.scale_type.music21_descriptive_name()
665 ))
666 })
667 }
668 pub fn pitches_between(&self, minimum: &Pitch, maximum: &Pitch) -> Result<Vec<Pitch>> {
676 if maximum.ps() < minimum.ps() {
679 let mut descending = self.pitches_between(maximum, minimum)?;
680 descending.reverse();
681 return Ok(descending);
682 }
683 Ok(self
684 .placed_between(minimum.ps(), maximum.ps())?
685 .into_iter()
686 .map(|(pitch, _)| pitch)
687 .collect())
688 }
689
690 pub fn pitch_on_degree_between(
697 &self,
698 degree: IntegerType,
699 minimum: &Pitch,
700 maximum: &Pitch,
701 ) -> Result<Option<Pitch>> {
702 let (low, high) = if maximum.ps() < minimum.ps() {
703 (maximum, minimum)
704 } else {
705 (minimum, maximum)
706 };
707 let count = self.degree_count().max(1);
708 let wanted = |place: usize| match self.named_degrees() {
709 Some(_) => self.degree_at_position(place) as IntegerType == degree,
710 None => place % count == (degree - 1).rem_euclid(count as IntegerType) as usize,
711 };
712 Ok(self
713 .placed_between(low.ps(), high.ps())?
714 .into_iter()
715 .find(|(_, place)| wanted(*place))
716 .map(|(pitch, _)| pitch))
717 }
718
719 fn placed_between(&self, lowest: FloatType, highest: FloatType) -> Result<Vec<(Pitch, usize)>> {
722 if !self.repeats_at_the_octave() || self.cyclic {
723 return self.cycle_between(lowest, highest);
724 }
725 let simplification = self.simplification();
726 let steps = self.walk()?;
727 let period = self.period_in_octaves(&steps);
732 let terminus = self.terminus_name();
733 let mut current = self.realization_start()?;
734 shift_by_periods(&mut current, period, lowest, true);
735 let mut pitches = Vec::new();
736 let limit = steps.len() * (MAX_RANGE_OCTAVES + 2) + 1;
739 for index in 0..limit {
740 let sounding = current.ps();
741 if within(sounding, lowest, highest) {
742 pitches.push((current.clone(), index % steps.len()));
743 }
744 if sounding > highest - SLACK {
745 break;
746 }
747 current = advance(¤t, &steps[index % steps.len()], simplification)?;
748 if (index + 1) % steps.len() == 0 {
749 current = spell_terminus(current, terminus.as_deref())?;
750 }
751 }
752 Ok(pitches)
753 }
754
755 fn repeats_at_the_octave(&self) -> bool {
760 match &self.custom_steps {
761 Some(steps) => {
762 let risen: FloatType = steps.iter().map(Interval::semitones).sum();
763 (risen / 12.0 - (risen / 12.0).round()).abs() < 1e-9
764 }
765 None => true,
766 }
767 }
768
769 fn cycle_between(&self, lowest: FloatType, highest: FloatType) -> Result<Vec<(Pitch, usize)>> {
777 let steps = self.walk()?;
778 let period: FloatType = steps.iter().map(Interval::semitones).sum();
779 if period <= 0.0 {
780 return Err(Error::Scale(
781 "a cycle that does not rise cannot be walked over a range".to_string(),
782 ));
783 }
784 let simplification = self.simplification();
785 let start = self.realization_start()?;
786 let limit = steps.len() * 512;
787 let mut found = Vec::new();
788
789 let mut current = start.clone();
794 for index in 0..limit {
795 let sounding = current.ps();
796 if within(sounding, lowest, highest) {
797 found.push((current.clone(), index % steps.len()));
798 }
799 if sounding > highest - SLACK {
800 break;
801 }
802 current = advance(¤t, &steps[index % steps.len()], simplification)?;
803 }
804
805 let mut current = start;
806 for back in 1..=limit {
807 let place = (steps.len() - back % steps.len()) % steps.len();
808 current = advance(¤t, &steps[place].reversed()?, simplification)?;
809 let sounding = current.ps();
810 if within(sounding, lowest, highest) {
811 found.push((current.clone(), place));
812 }
813 if sounding < lowest + SLACK {
814 break;
815 }
816 }
817 found.sort_by(|left, right| left.0.ps().total_cmp(&right.0.ps()));
818 Ok(found)
819 }
820
821 fn cycle_places_of(&self, pitch: &Pitch, comparison: DegreeComparison) -> Result<Vec<usize>> {
831 let sounding = Self::sounding(pitch).ps();
832 let wanted = comparison.key(pitch);
833 let mut places = Vec::new();
834 for (candidate, place) in self.cycle_between(sounding - 12.0, sounding + 12.0)? {
835 if comparison.key(&candidate) == wanted && !places.contains(&place) {
836 places.push(place);
837 }
838 }
839 Ok(places)
840 }
841
842 fn cycle_place_sounding(
846 &self,
847 pitch: &Pitch,
848 comparison: DegreeComparison,
849 ) -> Result<Option<(Pitch, usize)>> {
850 let sounding = Self::sounding(pitch).ps();
851 let wanted = comparison.key(pitch);
852 Ok(self
853 .cycle_between(sounding - 1e-9, sounding + 1e-9)?
854 .into_iter()
855 .find(|(candidate, _)| comparison.key(candidate) == wanted))
856 }
857
858 fn sounding(pitch: &Pitch) -> Pitch {
860 let mut heard = pitch.clone();
861 if heard.octave().is_none() {
862 heard.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
863 }
864 heard
865 }
866
867 fn cycle_steps_from(
872 &self,
873 origin: &Pitch,
874 steps: IntegerType,
875 neighbour_below: Option<bool>,
876 ) -> Result<Pitch> {
877 let walk = self.walk()?;
878 let period: FloatType = walk.iter().map(Interval::semitones).sum();
879 let simplification = self.simplification();
880 let at = Self::sounding(origin).ps();
881 let ascending = steps > 0;
882 let (mut current, mut place, mut remaining) =
883 match self.cycle_place_sounding(origin, DegreeComparison::PitchClass)? {
886 Some((pitch, place)) => (pitch, place, steps),
887 None => {
888 let take_below = neighbour_below.unwrap_or(!ascending);
889 let beside = self.cycle_between(at - period, at + period)?;
890 let neighbour = if take_below {
891 beside.into_iter().rfind(|(pitch, _)| pitch.ps() < at)
892 } else {
893 beside.into_iter().find(|(pitch, _)| pitch.ps() > at)
894 };
895 let (pitch, place) = neighbour
896 .ok_or_else(|| Error::Scale(format!("no scale pitch beside {origin}")))?;
897 let remaining = if neighbour_below.is_some() {
898 steps
899 } else {
900 steps - steps.signum()
901 };
902 (pitch, place, remaining)
903 }
904 };
905 while remaining > 0 {
906 current = advance(¤t, &walk[place], simplification)?;
907 place = (place + 1) % walk.len();
908 remaining -= 1;
909 }
910 while remaining < 0 {
911 place = (place + walk.len() - 1) % walk.len();
912 current = advance(¤t, &walk[place].reversed()?, simplification)?;
913 remaining += 1;
914 }
915 if origin.octave().is_none() {
916 current.octave_setter(None);
917 }
918 Ok(current)
919 }
920
921 pub fn is_realizable(&self) -> bool {
928 match &self.custom_steps {
929 Some(steps) => steps.iter().map(Interval::semitones).sum::<FloatType>() > 0.0,
930 None => true,
931 }
932 }
933
934 pub fn octave_duplicating(&self) -> bool {
939 if self.cyclic {
940 return false;
941 }
942 match &self.custom_steps {
943 Some(steps) => self.period_in_octaves(steps) == 1,
944 None => true,
945 }
946 }
947
948 fn period_in_octaves(&self, steps: &[Interval]) -> IntegerType {
952 let semitones: FloatType = steps.iter().map(Interval::semitones).sum();
953 ((semitones / 12.0).round() as IntegerType).max(1)
954 }
955
956 pub fn final_pitch(&self) -> Result<Pitch> {
959 self.pitch_at_degree(self.scale_type.tonic_degree() as IntegerType)
960 }
961
962 pub fn dominant(&self) -> Result<Pitch> {
964 self.pitch_at_degree(self.scale_type.dominant_degree() as IntegerType)
965 }
966
967 pub fn leading_tone(&self) -> Result<Pitch> {
971 let seventh = self.pitch_at_degree(7)?;
972 let tonic = self.final_pitch()?;
973 let distance = seventh.midi() - tonic.midi();
974 if distance == 11 {
975 return Ok(seventh);
976 }
977 let alter = seventh.accidental().alter() + FloatType::from(11 - distance);
978 let mut raised = seventh.clone();
979 raised.set_accidental(crate::pitch::Accidental::new(alter)?);
980 Ok(raised)
981 }
982
983 pub fn derive_by_degree(&self, degree: usize, pitch: &Pitch) -> Result<Scale> {
988 let implicit_octave = Some(crate::defaults::PITCH_OCTAVE as IntegerType);
989 let mut tonic = self.tonic.clone();
990 if tonic.octave().is_none() {
991 tonic.octave_setter(implicit_octave);
992 }
993 let degree_pitch =
994 Scale::new(self.scale_type, tonic.clone()).pitch_at_degree(degree as IntegerType)?;
995 let up_to_degree = Interval::between_pitches(&tonic, °ree_pitch)?;
996 let mut reference = pitch.clone();
997 if reference.octave().is_none() {
998 reference.octave_setter(implicit_octave);
999 }
1000 let new_tonic = reference.transpose(&up_to_degree.reversed()?)?;
1001 Ok(Scale::new(self.scale_type, new_tonic))
1002 }
1003
1004 pub fn transpose(&self, interval: &Interval) -> Result<Scale> {
1006 Ok(Scale::new(self.scale_type, self.tonic.transpose(interval)?))
1007 }
1008
1009 pub fn chord(&self) -> Result<Chord> {
1012 Chord::new(self.pitches()?.as_slice())
1013 }
1014
1015 pub fn pitches_from_scale_degrees(&self, degrees: &[usize]) -> Result<Vec<Pitch>> {
1019 let octave = self.pitches()?;
1020 let count = octave.len().saturating_sub(1).max(1);
1025 Ok(octave
1026 .into_iter()
1027 .enumerate()
1028 .filter(|(index, _)| degrees.contains(&(index % count + 1)))
1029 .map(|(_, pitch)| pitch)
1030 .collect())
1031 }
1032
1033 pub fn pitches_from_scale_degrees_between(
1037 &self,
1038 degrees: &[usize],
1039 minimum: &Pitch,
1040 maximum: &Pitch,
1041 ) -> Result<Vec<Pitch>> {
1042 let wanted: Vec<String> = self
1043 .pitches_from_scale_degrees(degrees)?
1044 .iter()
1045 .map(Pitch::name)
1046 .collect();
1047 Ok(self
1048 .pitches_between(minimum, maximum)?
1049 .into_iter()
1050 .filter(|pitch| wanted.contains(&pitch.name()))
1051 .collect())
1052 }
1053
1054 pub fn interval_between_degrees(&self, start: usize, end: usize) -> Result<Interval> {
1059 Interval::between_pitches(
1060 &self.pitch_at_degree(start as IntegerType)?,
1061 &self.pitch_at_degree(end as IntegerType)?,
1062 )
1063 }
1064
1065 pub fn is_next(&self, other: &Pitch, origin: &Pitch, steps: usize) -> Result<bool> {
1069 Ok(self.next_pitch_above(origin, steps)?.name() == other.name())
1070 }
1071
1072 pub fn match_pitches(&self, pitches: &[Pitch]) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
1077 self.match_pitches_by(pitches, DegreeComparison::Name)
1078 }
1079
1080 pub fn match_pitches_by(
1086 &self,
1087 pitches: &[Pitch],
1088 comparison: DegreeComparison,
1089 ) -> Result<(Vec<Pitch>, Vec<Pitch>)> {
1090 let realized = self.realized_in_implicit_octave()?;
1091 let degrees: Vec<String> = realized.iter().map(|p| comparison.key(p)).collect();
1092 let mut matched = Vec::new();
1093 let mut unmatched = Vec::new();
1094 for pitch in pitches {
1095 let mut heard = pitch.clone();
1096 if heard.octave().is_none() {
1097 heard.octave_setter(Some(crate::defaults::PITCH_OCTAVE as IntegerType));
1098 }
1099 if degrees.contains(&comparison.key(&heard)) {
1100 matched.push(heard);
1101 } else {
1102 unmatched.push(heard);
1103 }
1104 }
1105 Ok((matched, unmatched))
1106 }
1107
1108 pub fn find_missing(&self, pitches: &[Pitch]) -> Result<Vec<Pitch>> {
1112 let present: Vec<u8> = pitches.iter().map(root::pitch_class).collect();
1113 Ok(self
1114 .realized_in_implicit_octave()?
1115 .into_iter()
1116 .filter(|candidate| !present.contains(&root::pitch_class(candidate)))
1117 .collect())
1118 }
1119
1120 pub fn solfeg(&self, pitch: &Pitch, variant: SolfegVariant, chromatic: bool) -> Result<String> {
1126 let (degree, accidental) = self.degree_and_accidental_of(pitch)?;
1127 if degree > 7 {
1128 return Err(crate::error::Error::Scale(
1129 "Cannot call solfeg on non-7-degree scales".to_string(),
1130 ));
1131 }
1132 let table = match variant {
1133 SolfegVariant::Music21 => &SOLFEG_SYLLABLES,
1134 SolfegVariant::Humdrum => &HUMDRUM_SOLFEG_SYLLABLES,
1135 };
1136 let alter = if chromatic {
1137 accidental.map_or(0, |accidental| accidental.alter() as IntegerType)
1138 } else {
1139 0
1140 };
1141 let column = usize::try_from(alter + 2)
1142 .ok()
1143 .filter(|column| *column < 5)
1144 .ok_or_else(|| {
1145 crate::error::Error::Scale(format!(
1146 "no solfeg syllable for an alteration of {alter}"
1147 ))
1148 })?;
1149 Ok(table[degree - 1][column].to_string())
1150 }
1151
1152 fn realized_in_implicit_octave(&self) -> Result<Vec<Pitch>> {
1153 self.pitches()
1154 }
1155
1156 pub fn degree_of_by(
1159 &self,
1160 pitch: &Pitch,
1161 comparison: DegreeComparison,
1162 ) -> Result<Option<usize>> {
1163 if !self.repeats_at_the_octave() {
1164 return Ok(self
1165 .cycle_places_of(pitch, comparison)?
1166 .first()
1167 .map(|place| self.degree_at_position(*place)));
1168 }
1169 let wanted = comparison.key(pitch);
1170 Ok(self
1171 .scale_pitches()?
1172 .iter()
1173 .position(|candidate| comparison.key(candidate) == wanted)
1174 .map(|index| self.degree_at_position(index)))
1175 }
1176
1177 pub fn degrees_of_by(&self, pitch: &Pitch, comparison: DegreeComparison) -> Result<Vec<usize>> {
1184 if !self.repeats_at_the_octave() {
1185 return Ok(self
1186 .cycle_places_of(pitch, comparison)?
1187 .into_iter()
1188 .map(|place| self.degree_at_position(place))
1189 .collect());
1190 }
1191 let wanted = comparison.key(pitch);
1192 Ok(self
1193 .scale_pitches()?
1194 .iter()
1195 .enumerate()
1196 .filter(|(_, candidate)| comparison.key(candidate) == wanted)
1197 .map(|(index, _)| self.degree_at_position(index))
1198 .collect())
1199 }
1200
1201 pub fn degree_of(&self, pitch: &Pitch) -> Result<Option<usize>> {
1204 if !self.repeats_at_the_octave() {
1205 return self.degree_of_by(pitch, DegreeComparison::Name);
1206 }
1207 let name = pitch.name();
1208 Ok(self
1209 .scale_pitches()?
1210 .iter()
1211 .position(|candidate| candidate.name() == name)
1212 .map(|index| self.degree_at_position(index)))
1213 }
1214
1215 pub fn degree_of_pitch_class(&self, pitch: &Pitch) -> Result<Option<usize>> {
1218 if !self.repeats_at_the_octave() {
1219 return self.degree_of_by(pitch, DegreeComparison::PitchClass);
1220 }
1221 let pitch_class = pitch.pitch_class().number();
1222 Ok(self
1223 .scale_pitches()?
1224 .iter()
1225 .position(|candidate| candidate.pitch_class().number() == pitch_class)
1226 .map(|index| self.degree_at_position(index)))
1227 }
1228
1229 pub fn next_pitch_above(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
1232 self.pitch_steps_from(origin, steps as IntegerType, None)
1233 }
1234
1235 pub fn next_pitch_beside(
1240 &self,
1241 origin: &Pitch,
1242 steps: IntegerType,
1243 below: bool,
1244 ) -> Result<Pitch> {
1245 self.pitch_steps_from(origin, steps, Some(below))
1246 }
1247
1248 pub fn next_pitch_below(&self, origin: &Pitch, steps: usize) -> Result<Pitch> {
1251 self.pitch_steps_from(origin, -(steps as IntegerType), None)
1252 }
1253
1254 pub fn degree_and_accidental_of(
1259 &self,
1260 pitch: &Pitch,
1261 ) -> Result<(usize, Option<crate::pitch::Accidental>)> {
1262 if let Some(degree) = self.degree_of(pitch)? {
1263 return Ok((degree, None));
1264 }
1265 let pitches = self.scale_pitches()?;
1266 let index = pitches
1267 .iter()
1268 .position(|candidate| candidate.step() == pitch.step())
1269 .ok_or_else(|| {
1270 crate::error::Error::Scale(format!(
1271 "cannot get any scale degree for {pitch} in {self:?}"
1272 ))
1273 })?;
1274 let difference = pitch.accidental().alter() - pitches[index].accidental().alter();
1275 let accidental = if difference == 0.0 {
1276 None
1277 } else {
1278 Some(crate::pitch::Accidental::new(difference)?)
1279 };
1280 Ok((index + 1, accidental))
1281 }
1282
1283 fn scale_pitches(&self) -> Result<Vec<Pitch>> {
1284 let mut pitches = self.pitches()?;
1285 pitches.truncate(self.degree_count());
1286 Ok(pitches)
1287 }
1288
1289 fn pitch_steps_from(
1290 &self,
1291 origin: &Pitch,
1292 steps: IntegerType,
1293 neighbour_below: Option<bool>,
1294 ) -> Result<Pitch> {
1295 self.pitch_steps_from_place(origin, steps, neighbour_below, 0)
1296 }
1297
1298 pub fn places_of(&self, origin: &Pitch) -> Result<usize> {
1310 Ok(self.places_on(origin)?.len())
1311 }
1312
1313 fn places_on(&self, origin: &Pitch) -> Result<Vec<(usize, IntegerType)>> {
1318 if !self.repeats_at_the_octave() {
1319 return Ok(self
1320 .cycle_place_sounding(origin, DegreeComparison::PitchClass)?
1321 .map(|(_, place)| (place, 0))
1322 .into_iter()
1323 .collect());
1324 }
1325 let pitches = self.scale_pitches()?;
1326 let origin_ps = origin.ps();
1327 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
1328 let mut places = (base_shift - 1..=base_shift + 1)
1329 .flat_map(|shift| {
1330 pitches.iter().enumerate().map(move |(index, pitch)| {
1331 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
1332 })
1333 })
1334 .filter(|(ps, _, _)| (ps - origin_ps).abs() < 1e-9)
1335 .map(|(_, index, shift)| (index, shift))
1336 .collect::<Vec<_>>();
1337 places.sort_by_key(|&(index, shift)| (self.degree_at_position(index), index, shift));
1338 Ok(places)
1339 }
1340
1341 pub fn next_pitch_below_from(
1344 &self,
1345 origin: &Pitch,
1346 steps: usize,
1347 place: usize,
1348 ) -> Result<Pitch> {
1349 self.pitch_steps_from_place(origin, -(steps as IntegerType), None, place)
1350 }
1351
1352 pub fn next_pitch_above_from(
1354 &self,
1355 origin: &Pitch,
1356 steps: usize,
1357 place: usize,
1358 ) -> Result<Pitch> {
1359 self.pitch_steps_from_place(origin, steps as IntegerType, None, place)
1360 }
1361
1362 fn pitch_steps_from_place(
1363 &self,
1364 origin: &Pitch,
1365 steps: IntegerType,
1366 neighbour_below: Option<bool>,
1367 place: usize,
1368 ) -> Result<Pitch> {
1369 if steps == 0 {
1370 return Err(crate::error::Error::Scale(
1371 "step size must be at least 1".to_string(),
1372 ));
1373 }
1374 if !self.repeats_at_the_octave() {
1375 return self.cycle_steps_from(origin, steps, neighbour_below);
1376 }
1377 let pitches = self.scale_pitches()?;
1378 let count = pitches.len() as IntegerType;
1379 let origin_ps = origin.ps();
1380 let base_shift = ((origin_ps - self.tonic.ps()) / 12.0).floor() as IntegerType;
1381 let candidates = (base_shift - 1..=base_shift + 1)
1382 .flat_map(|shift| {
1383 pitches.iter().enumerate().map(move |(index, pitch)| {
1384 (pitch.ps() + 12.0 * shift as FloatType, index, shift)
1385 })
1386 })
1387 .collect::<Vec<_>>();
1388 let ascending = steps > 0;
1389 let standing = self.places_on(origin)?;
1390 let (index, shift, remaining) = match standing.get(place % standing.len().max(1)).copied() {
1391 Some((index, shift)) => (index, shift, steps),
1392 None => {
1393 let take_below = neighbour_below.unwrap_or(!ascending);
1397 let neighbour = if take_below {
1398 candidates
1399 .iter()
1400 .filter(|(ps, _, _)| *ps < origin_ps)
1401 .max_by(|left, right| left.0.total_cmp(&right.0))
1402 } else {
1403 candidates
1404 .iter()
1405 .filter(|(ps, _, _)| *ps > origin_ps)
1406 .min_by(|left, right| left.0.total_cmp(&right.0))
1407 };
1408 let &(_, index, shift) = neighbour.ok_or_else(|| {
1409 crate::error::Error::Scale(format!("no scale pitch beside {origin}"))
1410 })?;
1411 let remaining = if neighbour_below.is_some() {
1412 steps
1413 } else {
1414 steps - steps.signum()
1415 };
1416 (index, shift, remaining)
1417 }
1418 };
1419 let total = index as IntegerType + remaining;
1420 let mut pitch = pitches[total.rem_euclid(count) as usize].clone();
1421 let octave_shift = shift + total.div_euclid(count);
1422 let octave = origin.octave().map(|_| {
1423 pitch
1424 .octave()
1425 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1426 + octave_shift
1427 });
1428 pitch.octave_setter(octave);
1429 Ok(pitch)
1430 }
1431}
1432
1433fn within(sounding: FloatType, lowest: FloatType, highest: FloatType) -> bool {
1437 sounding > lowest - SLACK && sounding < highest + SLACK
1438}
1439
1440fn spell_terminus(pitch: Pitch, name: Option<&str>) -> Result<Pitch> {
1444 let Some(name) = name else {
1445 return Ok(pitch);
1446 };
1447 if pitch.name() == name {
1448 return Ok(pitch);
1449 }
1450 let octave = pitch
1451 .octave()
1452 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType);
1453 let mut respelled = Pitch::from_name(name)?;
1454 respelled.octave_setter(Some(octave));
1455 let octaves = (pitch.ps() - respelled.ps()) / 12.0;
1456 if octaves != octaves.round() {
1457 return Ok(pitch);
1458 }
1459 respelled.octave_setter(Some(octave + octaves.round() as IntegerType));
1460 Ok(respelled)
1461}
1462
1463const SLACK: FloatType = 1e-5;
1465
1466fn shift_by_periods(pitch: &mut Pitch, period: IntegerType, target: FloatType, below: bool) {
1473 let semitones = 12.0 * FloatType::from(period);
1474 let shift = |pitch: &mut Pitch, by: IntegerType| {
1475 let octave = pitch.octave().unwrap_or(0);
1476 pitch.octave_setter(Some(octave + by));
1477 };
1478 for _ in 0..64 {
1481 let ps = pitch.ps();
1482 let too_high = if below {
1483 ps > target + SLACK
1484 } else {
1485 ps - semitones >= target - SLACK
1486 };
1487 let too_low = if below {
1488 ps + semitones <= target + SLACK
1489 } else {
1490 ps < target - SLACK
1491 };
1492 if too_high {
1493 shift(pitch, -period);
1494 } else if too_low {
1495 shift(pitch, period);
1496 } else {
1497 break;
1498 }
1499 }
1500}
1501
1502pub fn fix_default_octave_for_pitch_list(pitches: &[Pitch]) -> Vec<Pitch> {
1509 let mut risen: Vec<Pitch> = Vec::with_capacity(pitches.len());
1510 let mut last_ps = 0.0;
1511 let mut last_octave =
1512 pitches
1513 .first()
1514 .map_or(crate::defaults::PITCH_OCTAVE as IntegerType, |pitch| {
1515 pitch
1516 .octave()
1517 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType)
1518 });
1519 for pitch in pitches {
1520 let mut pitch = pitch.clone();
1521 if pitch.octave().is_none() {
1522 if last_ps > pitch.ps() {
1523 pitch.octave_setter(Some(last_octave));
1524 }
1525 while last_ps > pitch.ps() {
1526 last_octave += 1;
1527 pitch.octave_setter(Some(last_octave));
1528 }
1529 }
1530 last_ps = pitch.ps();
1531 last_octave = pitch
1532 .octave()
1533 .unwrap_or(crate::defaults::PITCH_OCTAVE as IntegerType);
1534 risen.push(pitch);
1535 }
1536 risen
1537}
1538
1539#[cfg(test)]
1540mod tests {
1541 use super::*;
1542
1543 #[test]
1545 fn octaves_left_out_are_filled_in_as_music21_fills_them() {
1546 let cases = [
1547 ("C5 A F3 G B", "C5 A5 F3 G B"),
1548 ("C3 D6 E", "C3 D6 E6"),
1549 ("A B C D E F G A", "A B C5 D5 E5 F5 G5 A5"),
1550 ("C6 B A", "C6 B6 A7"),
1551 ("B#3 C D", "B#3 C D"),
1552 ];
1553 for (given, expected) in cases {
1554 let pitches: Vec<Pitch> = given
1555 .split(' ')
1556 .map(|name| Pitch::from_name(name).unwrap())
1557 .collect();
1558 let risen: Vec<String> = fix_default_octave_for_pitch_list(&pitches)
1559 .iter()
1560 .map(Pitch::name_with_octave)
1561 .collect();
1562 assert_eq!(risen.join(" "), expected, "{given}");
1563 }
1564 }
1565}