1use std::cmp::Ordering;
12use std::fmt;
13
14use crate::{
15 defaults::{FloatType, IntegerType},
16 dynamics::{Dynamic, dynamic_str_from_decimal as dynamic_name},
17 error::{Error, Result},
18 stream::{Stream, StreamElement, StreamKind},
19};
20
21const UNSET_VELOCITY_SHIFT: FloatType = 0.20866;
25
26const BASE_LEVEL: FloatType = 0.5;
28
29#[derive(Clone, Debug, PartialEq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[must_use]
33pub struct Volume {
34 velocity_scalar: Option<FloatType>,
35 velocity_is_relative: bool,
36}
37
38impl Default for Volume {
39 fn default() -> Self {
42 Self {
43 velocity_scalar: None,
44 velocity_is_relative: true,
45 }
46 }
47}
48
49impl Volume {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn merge_attributes(&mut self, other: &Volume) {
58 self.velocity_scalar = other.velocity_scalar;
59 self.velocity_is_relative = other.velocity_is_relative;
60 }
61
62 pub fn from_velocity(velocity: IntegerType) -> Self {
64 let mut volume = Self::new();
65 volume.set_velocity(Some(velocity));
66 volume
67 }
68
69 pub fn from_velocity_scalar(scalar: FloatType) -> Result<Self> {
71 let mut volume = Self::new();
72 volume.set_velocity_scalar(Some(scalar))?;
73 Ok(volume)
74 }
75
76 pub fn velocity(&self) -> Option<IntegerType> {
78 let scalar = self.velocity_scalar?;
79 let velocity = (scalar * 127.0).clamp(0.0, 127.0);
80 Some(velocity.round_ties_even() as IntegerType)
81 }
82
83 pub fn set_velocity(&mut self, velocity: Option<IntegerType>) {
85 self.velocity_scalar = velocity.map(|velocity| {
86 if velocity <= 0 {
87 0.0
88 } else if velocity >= 127 {
89 1.0
90 } else {
91 FloatType::from(velocity) / 127.0
92 }
93 });
94 }
95
96 pub fn velocity_scalar(&self) -> Option<FloatType> {
99 self.velocity_scalar
100 }
101
102 pub fn set_velocity_scalar(&mut self, scalar: Option<FloatType>) -> Result<()> {
105 match scalar {
106 None => self.velocity_scalar = None,
107 Some(scalar) if scalar.is_nan() => {
108 return Err(Error::Volume(
109 "value provided for velocityScalar must be a number, not NaN".to_string(),
110 ));
111 }
112 Some(scalar) => self.velocity_scalar = Some(scalar.clamp(0.0, 1.0)),
113 }
114 Ok(())
115 }
116
117 pub fn velocity_is_relative(&self) -> bool {
121 self.velocity_is_relative
122 }
123
124 pub fn set_velocity_is_relative(&mut self, relative: bool) {
126 self.velocity_is_relative = relative;
127 }
128
129 pub fn has_velocity_information(&self) -> bool {
132 self.velocity_scalar.is_some()
133 }
134
135 pub fn realized(&self) -> FloatType {
138 self.realized_with(None, 0.0, BASE_LEVEL, true)
139 }
140
141 pub fn realized_with(
151 &self,
152 dynamic_scalar: Option<FloatType>,
153 articulation_shift: FloatType,
154 base_level: FloatType,
155 clip: bool,
156 ) -> FloatType {
157 self.realize(true, dynamic_scalar, articulation_shift, base_level, clip)
158 }
159
160 pub fn realized_without_velocity(
165 &self,
166 dynamic_scalar: Option<FloatType>,
167 articulation_shift: FloatType,
168 base_level: FloatType,
169 clip: bool,
170 ) -> FloatType {
171 self.realize(false, dynamic_scalar, articulation_shift, base_level, clip)
172 }
173
174 fn realize(
175 &self,
176 use_velocity: bool,
177 dynamic_scalar: Option<FloatType>,
178 articulation_shift: FloatType,
179 base_level: FloatType,
180 clip: bool,
181 ) -> FloatType {
182 let mut value = base_level;
183 if use_velocity {
184 match self.velocity_scalar {
185 Some(scalar) if !self.velocity_is_relative => value = scalar,
186 Some(scalar) => value *= scalar * 2.0,
187 None => value += UNSET_VELOCITY_SHIFT,
188 }
189 }
190 if self.velocity_is_relative {
191 if let Some(dynamic_scalar) = dynamic_scalar {
192 value *= dynamic_scalar * 2.0;
193 }
194 value += articulation_shift;
195 }
196 if clip {
197 value = value.clamp(0.0, 1.0);
198 }
199 value
200 }
201
202 pub fn realized_dynamic(&self) -> &'static str {
205 dynamic_name(self.realized())
206 }
207
208 pub fn realized_str(&self) -> String {
211 rounded_str(self.realized())
212 }
213}
214
215pub fn rounded_str(value: FloatType) -> String {
218 let rounded = (value * 100.0).round() / 100.0;
219 let written = format!("{rounded}");
220 if written.contains('.') {
221 written
222 } else {
223 format!("{written}.0")
224 }
225}
226
227#[derive(Clone, Debug, Default, PartialEq)]
230pub enum DynamicContext {
231 #[default]
234 FromStream,
235 Ignored,
237 Fixed(Dynamic),
239}
240
241pub fn realize_volume(stream: &mut Stream, context: &DynamicContext, use_velocity: bool) {
277 let in_force: Vec<Option<FloatType>> = match context {
278 DynamicContext::Ignored => vec![None; stream.leaves().len()],
279 DynamicContext::Fixed(dynamic) => {
280 vec![Some(dynamic.volume_scalar()); stream.leaves().len()]
281 }
282 DynamicContext::FromStream => {
283 let leaves = stream.leaves();
284 dynamics_in_force(stream)
285 .into_iter()
286 .map(|found| match found.map(|position| leaves[position].1) {
287 Some(StreamElement::Dynamic(dynamic)) => Some(dynamic.volume_scalar()),
288 _ => None,
289 })
290 .collect()
291 }
292 };
293
294 let realized = |volume: &Volume, dynamic: Option<FloatType>| -> Volume {
295 let value = if use_velocity {
296 volume.realized_with(dynamic, 0.0, BASE_LEVEL, true)
297 } else {
298 volume.realized_without_velocity(dynamic, 0.0, BASE_LEVEL, true)
299 };
300 let mut absolute = volume.clone();
301 absolute.set_velocity_is_relative(false);
302 absolute
303 .set_velocity_scalar(Some(value))
304 .expect("a clipped loudness is within the range a velocity takes");
305 absolute
306 };
307 let mut position = 0;
308 stream.for_each_mut(&mut |_, element| {
309 let dynamic = in_force[position];
310 position += 1;
311 match element {
312 StreamElement::Note(note) => {
313 let volume = realized(¬e.volume(), dynamic);
314 note.set_volume(Some(volume));
315 }
316 StreamElement::Chord(chord) => {
317 let volume = realized(&chord.volume(), dynamic);
318 chord.set_volume(Some(volume));
319 }
320 _ => {}
321 }
322 });
323}
324
325pub fn dynamics_in_force(stream: &Stream) -> Vec<Option<usize>> {
347 let leaves = stream.leaves();
348 let order = Stream::flatten_order(&leaves);
350 let dynamics: Vec<(Held, usize)> = order
351 .iter()
352 .filter(|position| matches!(leaves[**position].1, StreamElement::Dynamic(_)))
353 .map(|position| (Held::of(leaves[*position].0), *position))
354 .collect();
355 let total = Held::of(stream.end_offset());
360 let mut spans: Vec<((Held, Held), usize)> = Vec::new();
361 for (index, &(start, position)) in dynamics.iter().enumerate() {
362 let next = dynamics.get(index + 1).map_or(total, |(next, _)| *next);
363 let key = (start, start.add(next.span_from(start)));
364 match spans
366 .iter_mut()
367 .find(|(held, _)| held.0.same(key.0) && held.1.same(key.1))
368 {
369 Some(held) => held.1 = position,
370 None => spans.push((key, position)),
371 }
372 }
373 spans.sort_by(|left, right| {
374 left.0
375 .0
376 .compare(right.0.0)
377 .then(left.0.1.compare(right.0.1))
378 });
379
380 let mut answers: Vec<Option<usize>> = vec![None; leaves.len()];
381 let mut last = 0;
382 for &position in &order {
383 let at = Held::of(leaves[position].0);
384 let found = spans[last.min(spans.len())..]
385 .iter()
386 .position(|((start, end), _)| {
387 end.compare(at) == Ordering::Greater && at.compare(*start) != Ordering::Less
388 })
389 .map(|index| index + last);
390 if crate::makenotation::is_not_rest(leaves[position].1)
391 && let Some(index) = found
392 {
393 last = index;
394 }
395 answers[position] = found.map(|index| spans[index].1);
396 }
397 answers
398}
399
400#[derive(Clone, Copy, Debug)]
403enum Held {
404 Float(FloatType),
405 Exact(i128, i128),
406}
407
408impl Held {
409 fn of(value: FloatType) -> Self {
410 let snapped = crate::makenotation::op_frac(value);
411 match crate::duration::limited_fraction(snapped, 65535) {
412 Some((numerator, denominator)) if !is_power_of_two(denominator) => {
413 Self::Exact(numerator, denominator)
414 }
415 _ => Self::Float(snapped),
416 }
417 }
418
419 fn float(self) -> FloatType {
420 match self {
421 Self::Float(value) => value,
422 Self::Exact(numerator, denominator) => {
423 numerator as FloatType / denominator as FloatType
424 }
425 }
426 }
427
428 fn add(self, other: Self) -> Self {
430 match (self, other) {
431 (Self::Exact(a, b), Self::Exact(c, d)) => reduced(a * d + c * b, b * d),
432 _ => Self::Float(self.float() + other.float()),
433 }
434 }
435
436 fn span_from(self, start: Self) -> Self {
439 match (self, start) {
440 (Self::Exact(a, b), Self::Exact(c, d)) => match reduced(a * d - c * b, b * d) {
441 Self::Exact(numerator, denominator)
442 if is_power_of_two(denominator) || denominator > 65535 =>
443 {
444 Self::of(numerator as FloatType / denominator as FloatType)
445 }
446 exact => exact,
447 },
448 _ => Self::of(self.float() - start.float()),
449 }
450 }
451
452 fn compare(self, other: Self) -> Ordering {
454 let (a, b) = self.ratio();
455 let (c, d) = other.ratio();
456 match (a.checked_mul(d), c.checked_mul(b)) {
457 (Some(left), Some(right)) => left.cmp(&right),
458 _ => self.float().total_cmp(&other.float()),
459 }
460 }
461
462 fn same(self, other: Self) -> bool {
463 self.compare(other) == Ordering::Equal
464 }
465
466 fn ratio(self) -> (i128, i128) {
468 match self {
469 Self::Exact(numerator, denominator) => (numerator, denominator),
470 Self::Float(value) => {
471 let mut exact = value;
472 let mut denominator: i128 = 1;
473 while exact.fract() != 0.0 && denominator < (1 << 70) {
474 exact *= 2.0;
475 denominator *= 2;
476 }
477 (exact as i128, denominator)
478 }
479 }
480 }
481}
482
483fn reduced(numerator: i128, denominator: i128) -> Held {
484 let (mut a, mut b) = (numerator.abs(), denominator.abs());
485 while b != 0 {
486 (a, b) = (b, a % b);
487 }
488 let divisor = a.max(1);
489 Held::Exact(numerator / divisor, denominator / divisor)
490}
491
492fn is_power_of_two(value: i128) -> bool {
493 value > 0 && value & (value - 1) == 0
494}
495
496pub fn dynamic_context(stream: &Stream, position: usize) -> Option<usize> {
522 let mut chain: Vec<(&Stream, FloatType, usize)> = Vec::new();
525 let at = holders_of(stream, 0.0, 0, position, &mut chain)?;
526 for &(site, start, first) in chain.iter().rev() {
527 let at = at - start;
528 let own_first = !matches!(site.kind(), StreamKind::Part | StreamKind::PartStaff);
529 let then_inside = !matches!(site.kind(), StreamKind::Score | StreamKind::Opus);
530 if own_first && let Some(found) = last_dynamic(site, first, at, false) {
531 return Some(found);
532 }
533 if then_inside && let Some(found) = last_dynamic(site, first, at, true) {
534 return Some(found);
535 }
536 }
537 None
538}
539
540fn holders_of<'a>(
543 stream: &'a Stream,
544 start: FloatType,
545 first: usize,
546 position: usize,
547 chain: &mut Vec<(&'a Stream, FloatType, usize)>,
548) -> Option<FloatType> {
549 chain.push((stream, start, first));
550 let mut next = first;
551 for event in stream.events() {
552 let offset = start + event.offset();
553 match event.element() {
554 StreamElement::Stream(inner) => {
555 let count = inner.leaves().len();
556 if position < next + count {
557 return holders_of(inner, offset, next, position, chain);
558 }
559 next += count;
560 }
561 _ => {
562 if next == position {
563 return Some(offset);
564 }
565 next += 1;
566 }
567 }
568 }
569 None
570}
571
572fn last_dynamic(stream: &Stream, first: usize, at: FloatType, inside: bool) -> Option<usize> {
575 let mut found: Option<(FloatType, usize)> = None;
576 let mut consider = |offset: FloatType, position: usize| {
577 if offset <= at + 1e-9 && found.is_none_or(|(best, _)| offset >= best) {
578 found = Some((offset, position));
579 }
580 };
581 if inside {
582 for (position, (offset, element)) in stream.leaves().into_iter().enumerate() {
583 if matches!(element, StreamElement::Dynamic(_)) {
584 consider(offset, first + position);
585 }
586 }
587 } else {
588 let mut next = first;
589 for event in stream.events() {
590 match event.element() {
591 StreamElement::Stream(inner) => next += inner.leaves().len(),
592 StreamElement::Dynamic(_) => {
593 consider(event.offset(), next);
594 next += 1;
595 }
596 _ => next += 1,
597 }
598 }
599 }
600 found.map(|(_, position)| position)
601}
602
603impl fmt::Display for Volume {
604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 let mut rounded = format!("{:.2}", self.realized());
611 while rounded.ends_with('0') && !rounded.ends_with(".0") {
612 rounded.pop();
613 }
614 write!(f, "realized={rounded}")
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use super::*;
621
622 #[test]
623 fn merging_takes_the_other_volumes_velocity() {
624 let mut loud = Volume::from_velocity(111);
625 loud.set_velocity_is_relative(false);
626 let mut volume = Volume::new();
627 volume.merge_attributes(&loud);
628 assert_eq!(volume.velocity(), Some(111));
629 assert!(!volume.velocity_is_relative());
630 }
631
632 #[test]
633 fn velocity_and_scalar_are_the_same_number() {
634 let mut volume = Volume::new();
635 assert_eq!(volume.velocity(), None);
636 assert_eq!(volume.velocity_scalar(), None);
637 assert!(!volume.has_velocity_information());
638
639 volume.set_velocity(Some(64));
640 assert_eq!(volume.velocity(), Some(64));
641 assert!(volume.has_velocity_information());
642 assert!((volume.velocity_scalar().unwrap() - 64.0 / 127.0).abs() < 1e-12);
643
644 volume.set_velocity(Some(200));
645 assert_eq!(volume.velocity(), Some(127));
646 volume.set_velocity(Some(-5));
647 assert_eq!(volume.velocity(), Some(0));
648 volume.set_velocity(None);
649 assert_eq!(volume.velocity(), None);
650 }
651
652 #[test]
653 fn scalars_clamp_and_round_trip() {
654 let volume = Volume::from_velocity_scalar(0.5).unwrap();
655 assert_eq!(volume.velocity(), Some(64));
656 assert_eq!(
657 Volume::from_velocity_scalar(2.0).unwrap().velocity_scalar(),
658 Some(1.0)
659 );
660 assert_eq!(
661 Volume::from_velocity_scalar(-1.0)
662 .unwrap()
663 .velocity_scalar(),
664 Some(0.0)
665 );
666 assert!(Volume::from_velocity_scalar(FloatType::NAN).is_err());
667 assert_eq!(Volume::from_velocity(127).velocity_scalar(), Some(1.0));
668 }
669
670 #[test]
671 fn realized_matches_music21() {
672 let unset = Volume::new();
675 assert!((unset.realized() - 0.70866).abs() < 1e-9);
676 assert_eq!(unset.realized_dynamic(), "f");
677 assert_eq!(unset.realized_str(), "0.71");
678
679 let half = Volume::from_velocity_scalar(0.5).unwrap();
682 assert!((half.realized() - 0.5).abs() < 1e-9);
683 assert_eq!(half.realized_dynamic(), "mf");
684 assert_eq!(half.realized_str(), "0.5");
685
686 let mut absolute = Volume::from_velocity_scalar(0.5).unwrap();
688 absolute.set_velocity_is_relative(false);
689 assert!((absolute.realized() - 0.5).abs() < 1e-9);
690 let mut loud = Volume::from_velocity_scalar(1.0).unwrap();
691 loud.set_velocity_is_relative(false);
692 assert!((loud.realized() - 1.0).abs() < 1e-9);
693
694 assert!((half.realized_with(Some(0.5), 0.0, 0.5, true) - 0.5).abs() < 1e-9);
696 assert!((half.realized_with(Some(1.0), 0.0, 0.5, true) - 1.0).abs() < 1e-9);
697 assert!((half.realized_with(Some(1.0), 0.0, 0.5, false) - 1.0).abs() < 1e-9);
698 assert!(loud.realized_with(Some(1.0), 0.0, 0.5, false) > 0.99);
699
700 let unmarked = Volume::new();
703 assert!((unmarked.realized_with(None, 0.1, 0.5, true) - 0.80866).abs() < 1e-9);
704 assert!(
707 (unmarked.realized_without_velocity(Some(0.25), 0.0, 0.5, true) - 0.25).abs() < 1e-9
708 );
709 assert!(
710 (unmarked.realized_without_velocity(Some(0.85), 0.0, 0.5, true) - 0.85).abs() < 1e-9
711 );
712 }
713
714 #[test]
715 fn realized_names_cover_the_dynamic_range() {
716 assert_eq!(dynamic_name(0.0), "n");
717 assert_eq!(dynamic_name(0.05), "pppp");
718 assert_eq!(dynamic_name(0.12), "ppp");
719 assert_eq!(dynamic_name(0.2), "pp");
720 assert_eq!(dynamic_name(0.3), "p");
721 assert_eq!(dynamic_name(0.4), "mp");
722 assert_eq!(dynamic_name(0.6), "mf");
723 assert_eq!(dynamic_name(0.7), "f");
724 assert_eq!(dynamic_name(0.85), "ff");
725 assert_eq!(dynamic_name(1.0), "fff");
726 assert_eq!(Volume::from_velocity(64).to_string(), "realized=0.5");
727 assert_eq!(Volume::new().to_string(), "realized=0.71");
728 let mut silent = Volume::new();
729 silent.set_velocity(Some(0));
730 silent.set_velocity_is_relative(false);
731 assert_eq!(silent.to_string(), "realized=0.0");
732 }
733
734 #[test]
737 fn a_stream_is_realized_against_the_dynamics_in_force() {
738 use crate::{Note, Stream, StreamElement};
739
740 let velocities = |stream: &Stream| -> Vec<Option<IntegerType>> {
741 stream
742 .notes()
743 .iter()
744 .map(|(_, element)| match element {
745 StreamElement::Note(note) => note.volume().velocity(),
746 _ => None,
747 })
748 .collect()
749 };
750 let notes = |count: usize| {
751 let mut stream = Stream::new();
752 for _ in 0..count {
753 stream.push(Note::from_name("G3").unwrap());
754 }
755 stream
756 };
757
758 let mut stream = notes(16);
759 for (index, mark) in ["pp", "p", "mp", "f", "mf", "ff", "ppp", "mf"]
760 .into_iter()
761 .enumerate()
762 {
763 stream.insert(index as FloatType * 2.0, Dynamic::new(mark));
764 }
765 realize_volume(&mut stream, &DynamicContext::FromStream, true);
766 assert_eq!(
767 velocities(&stream),
768 [
769 45, 45, 63, 63, 81, 81, 126, 126, 99, 99, 127, 127, 27, 27, 99, 99
770 ]
771 .map(Some)
772 );
773
774 let mut stream = notes(4);
777 stream.insert(1.0, Dynamic::new("p"));
778 stream.insert(3.0, Dynamic::new("ff"));
779 let mut position = 0;
780 stream.for_each_mut(&mut |_, element| {
781 if let StreamElement::Note(note) = element {
782 let mut volume = note.volume();
783 match position {
784 2 => volume.set_velocity(Some(100)),
785 3 => {
786 volume.set_velocity(Some(64));
787 volume.set_velocity_is_relative(false);
788 }
789 _ => {}
790 }
791 note.set_volume(Some(volume));
792 position += 1;
793 }
794 });
795 realize_volume(&mut stream, &DynamicContext::FromStream, true);
796 assert_eq!(velocities(&stream), [90, 63, 70, 64].map(Some));
797
798 let mut stream = notes(2);
799 stream.insert(0.0, Dynamic::new("p"));
800 stream.for_each_mut(&mut |offset, element| {
801 if let (true, StreamElement::Note(note)) = (offset == 1.0, element) {
802 note.set_volume(Some(Volume::from_velocity(100)));
803 }
804 });
805 realize_volume(&mut stream, &DynamicContext::FromStream, false);
806 assert_eq!(velocities(&stream), [44, 44].map(Some));
807
808 let mut stream = notes(2);
809 stream.insert(0.0, Dynamic::new("p"));
810 realize_volume(
811 &mut stream,
812 &DynamicContext::Fixed(Dynamic::new("sfz")),
813 true,
814 );
815 assert_eq!(velocities(&stream), [126, 126].map(Some));
816
817 let mut stream = notes(2);
818 stream.insert(0.0, Dynamic::new("p"));
819 realize_volume(&mut stream, &DynamicContext::Ignored, true);
820 assert_eq!(velocities(&stream), [90, 90].map(Some));
821 }
822}