1use super::{Stream, StreamElement, StreamKind};
5use crate::defaults::FloatType;
6use crate::error::Result;
7use crate::interval::Interval;
8use crate::spanner::SpannerKind;
9
10const NEAR: FloatType = 1e-9;
13
14impl Stream {
15 pub fn at_sounding_pitch(&self) -> Option<bool> {
24 self.labels.at_sounding_pitch
25 }
26
27 pub fn set_at_sounding_pitch(&mut self, at_sounding_pitch: Option<bool>) {
29 self.labels.at_sounding_pitch = at_sounding_pitch;
30 }
31
32 pub fn to_written_pitch(&self) -> Result<Stream> {
60 self.to_written_pitch_by(false)
61 }
62
63 pub fn to_written_pitch_by(&self, ottavas_to_sounding: bool) -> Result<Stream> {
68 let mut written = self.clone();
69 written.make_written_pitch(ottavas_to_sounding)?;
70 Ok(written)
71 }
72
73 pub fn to_sounding_pitch(&self) -> Result<Stream> {
79 let mut sounding = self.clone();
80 sounding.make_sounding(None)?;
81 sounding.move_under_ottavas(true)?;
82 Ok(sounding)
83 }
84
85 pub(crate) fn make_written_pitch(&mut self, ottavas_to_sounding: bool) -> Result<()> {
87 self.make_written(None)?;
88 self.move_under_ottavas(ottavas_to_sounding)
89 }
90
91 fn move_under_ottavas(&mut self, to_sounding: bool) -> Result<()> {
97 let mut moves: Vec<(Vec<usize>, Interval)> = Vec::new();
98 for spanner in &mut self.labels.spanners {
99 if spanner.kind() != SpannerKind::Ottava {
100 continue;
101 }
102 let Some(mut shift) = spanner.shift() else {
103 continue;
104 };
105 if shift.transposing() != to_sounding {
107 continue;
108 }
109 shift.set_transposing(!to_sounding);
110 spanner.set_shift(Some(shift));
111 let interval = if to_sounding {
112 shift.interval()
113 } else {
114 shift.interval().reversed()?
115 };
116 let places = spanner.spanned().iter().flatten().copied().collect();
117 moves.push((places, interval));
118 }
119 if !moves.is_empty() {
120 let mut position = 0;
121 let mut failure = None;
122 self.for_each_mut(&mut |_, element| {
123 for (places, interval) in &moves {
124 if failure.is_none()
125 && places.contains(&position)
126 && let Err(error) = transpose_pitches(element, interval)
127 {
128 failure = Some(error);
129 }
130 }
131 position += 1;
132 });
133 if let Some(error) = failure {
134 return Err(error);
135 }
136 }
137 for event in &mut self.events {
138 if let StreamElement::Stream(inner) = &mut event.element {
139 inner.move_under_ottavas(to_sounding)?;
140 }
141 }
142 Ok(())
143 }
144
145 #[cfg(feature = "musicxml")]
148 pub(crate) fn holds_transposing_ottava(&self) -> bool {
149 self.spanners().iter().any(|spanner| {
150 spanner.kind() == SpannerKind::Ottava
151 && spanner.shift().is_some_and(|shift| shift.transposing())
152 }) || self.events.iter().any(|event| match &event.element {
153 StreamElement::Stream(inner) => inner.holds_transposing_ottava(),
154 _ => false,
155 })
156 }
157
158 fn make_written(&mut self, held_in: Option<bool>) -> Result<()> {
161 if self.has_part_like_streams() || self.kind == StreamKind::Opus {
162 let here = self.at_sounding_pitch().or(held_in);
163 for event in &mut self.events {
164 if let StreamElement::Stream(inner) = &mut event.element {
165 inner.make_written(here)?;
166 }
167 }
168 self.set_at_sounding_pitch(Some(false));
169 } else if self.treat_at_sounding_pitch(held_in)? == Some(true) {
170 self.transpose_by_instrument(true)?;
171 self.say_everywhere(false);
172 }
173 Ok(())
174 }
175
176 fn make_sounding(&mut self, held_in: Option<bool>) -> Result<()> {
178 if self.has_part_like_streams() || self.kind == StreamKind::Opus {
179 let here = self.at_sounding_pitch().or(held_in);
180 for event in &mut self.events {
181 if let StreamElement::Stream(inner) = &mut event.element {
182 inner.make_sounding(here)?;
183 }
184 }
185 self.set_at_sounding_pitch(Some(true));
186 } else if self.treat_at_sounding_pitch(held_in)? == Some(false) {
187 self.transpose_by_instrument(false)?;
188 self.say_everywhere(true);
189 }
190 Ok(())
191 }
192
193 fn treat_at_sounding_pitch(&mut self, held_in: Option<bool>) -> Result<Option<bool>> {
197 let Some(at) = self.at_sounding_pitch().or(held_in) else {
198 return Ok(None);
199 };
200 self.bring_round(at)?;
201 Ok(Some(at))
202 }
203
204 fn bring_round(&mut self, at: bool) -> Result<()> {
207 for event in &mut self.events {
208 if let StreamElement::Stream(inner) = &mut event.element {
209 match inner.at_sounding_pitch() {
210 Some(said) if said != at && at => inner.make_sounding(None)?,
211 Some(said) if said != at => inner.make_written(None)?,
212 _ => {}
213 }
214 inner.bring_round(at)?;
215 }
216 }
217 Ok(())
218 }
219
220 fn say_everywhere(&mut self, at: bool) {
222 self.set_at_sounding_pitch(Some(at));
223 for event in &mut self.events {
224 if let StreamElement::Stream(inner) = &mut event.element {
225 inner.say_everywhere(at);
226 }
227 }
228 }
229
230 fn transpose_by_instrument(&mut self, reverse: bool) -> Result<()> {
235 let leaves = self.leaves();
236 let end_of_stream = leaves
237 .iter()
238 .map(|(offset, element)| offset + element.quarter_length())
239 .fold(0.0, FloatType::max);
240 let mut instruments: Vec<(FloatType, Option<Interval>)> = leaves
241 .iter()
242 .filter_map(|(offset, element)| match element {
243 StreamElement::Instrument(instrument) => {
244 Some((*offset, instrument.transposition().cloned()))
245 }
246 _ => None,
247 })
248 .collect();
249 instruments.sort_by(|left, right| left.0.total_cmp(&right.0));
251 let mut spans: Vec<(FloatType, FloatType, Interval)> = Vec::new();
252 for (index, (start, transposition)) in instruments.iter().enumerate() {
253 let Some(transposition) = transposition else {
254 continue;
255 };
256 let end = instruments
257 .get(index + 1)
258 .map_or(end_of_stream, |next| next.0);
259 let interval = if reverse {
260 transposition.reversed()?
261 } else {
262 transposition.clone()
263 };
264 spans.push((*start, end, interval));
265 }
266 if spans.is_empty() {
267 return Ok(());
268 }
269 let mut failure = None;
270 self.for_each_mut(&mut |offset, element| {
271 if failure.is_some() {
272 return;
273 }
274 let Some((_, _, interval)) = spans
275 .iter()
276 .find(|(start, end, _)| offset > start - NEAR && offset < end - NEAR)
277 else {
278 return;
279 };
280 if let Err(error) = transpose_element(element, interval) {
281 failure = Some(error);
282 }
283 });
284 failure.map_or(Ok(()), Err)
285 }
286}
287
288fn transpose_element(element: &mut StreamElement, interval: &Interval) -> Result<()> {
292 match element {
293 StreamElement::Note(note) => *note = note.transpose(interval)?,
294 StreamElement::Chord(chord) => *chord = chord.transpose(interval)?,
295 StreamElement::ChordSymbol(symbol) => *symbol = symbol.transpose(interval)?,
296 StreamElement::Key(key) => *key = key.transpose(interval)?,
297 StreamElement::KeySignature(signature) => *signature = signature.transpose(interval)?,
298 _ => {}
299 }
300 Ok(())
301}
302
303fn transpose_pitches(element: &mut StreamElement, interval: &Interval) -> Result<()> {
306 match element {
307 StreamElement::Note(note) => *note = note.transpose(interval)?,
308 StreamElement::Chord(chord) => *chord = chord.transpose(interval)?,
309 StreamElement::ChordSymbol(symbol) => *symbol = symbol.transpose(interval)?,
310 _ => {}
311 }
312 Ok(())
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::instrument::Instrument;
318 use crate::note::Note;
319 use crate::spanner::{OctaveShift, Spanner};
320 use crate::stream::{Stream, StreamElement, StreamKind};
321
322 fn names(stream: &Stream) -> Vec<String> {
323 stream
324 .leaves()
325 .iter()
326 .filter_map(|(_, element)| match element {
327 StreamElement::Note(note) => Some(note.pitch().name_with_octave()),
328 _ => None,
329 })
330 .collect()
331 }
332
333 fn part_with(kind: &str, notes: &[&str]) -> Stream {
334 let mut part = Stream::with_kind(StreamKind::Part);
335 part.push(StreamElement::Instrument(Box::new(
336 Instrument::of_kind(kind).unwrap(),
337 )));
338 for name in notes {
339 part.push(StreamElement::Note(Note::from_name(name).unwrap()));
340 }
341 part
342 }
343
344 #[test]
345 fn a_part_at_sounding_pitch_is_written_up_for_a_clarinet_and_back() {
346 let mut part = part_with("Clarinet", &["Bb4", "D5"]);
347 part.set_at_sounding_pitch(Some(true));
348 let written = part.to_written_pitch().unwrap();
349 assert_eq!(names(&written), ["C5", "E5"]);
350 assert_eq!(written.at_sounding_pitch(), Some(false));
351 let sounding = written.to_sounding_pitch().unwrap();
352 assert_eq!(names(&sounding), ["Bb4", "D5"]);
353 assert_eq!(sounding.at_sounding_pitch(), Some(true));
354 }
355
356 #[test]
357 fn a_part_that_says_nothing_is_left_as_it_is() {
358 let part = part_with("Clarinet", &["Bb4"]);
359 assert_eq!(names(&part.to_written_pitch().unwrap()), ["Bb4"]);
360 assert_eq!(part.to_written_pitch().unwrap().at_sounding_pitch(), None);
361 }
362
363 #[test]
364 fn a_part_takes_what_the_score_holding_it_says() {
365 let mut score = Stream::with_kind(StreamKind::Score);
366 score.push(StreamElement::Stream(Box::new(part_with("Horn", &["C4"]))));
367 score.set_at_sounding_pitch(Some(true));
368 let written = score.to_written_pitch().unwrap();
369 assert_eq!(names(&written), ["G4"]);
371 assert_eq!(written.at_sounding_pitch(), Some(false));
372 }
373
374 #[test]
375 fn each_instrument_moves_the_notes_from_where_it_stands() {
376 let mut part = part_with("Clarinet", &["Bb4"]);
377 part.push(StreamElement::Instrument(Box::new(
378 Instrument::of_kind("Flute").unwrap(),
379 )));
380 part.push(StreamElement::Note(Note::from_name("Bb4").unwrap()));
381 part.set_at_sounding_pitch(Some(true));
382 assert_eq!(names(&part.to_written_pitch().unwrap()), ["C5", "Bb4"]);
383 }
384
385 fn under_an_ottava(name: &str, transposing: bool) -> Stream {
386 let mut part = Stream::with_kind(StreamKind::Part);
387 for note in ["C5", "D5", "E5"] {
388 part.push(StreamElement::Note(Note::from_name(note).unwrap()));
389 }
390 let shift = OctaveShift::from_name(name, transposing).unwrap();
391 part.add_spanner(Spanner::ottava(shift, vec![0, 1]));
392 part
393 }
394
395 fn transposing(stream: &Stream) -> bool {
396 stream.spanners()[0].shift().unwrap().transposing()
397 }
398
399 #[test]
400 fn the_notes_under_an_ottava_sound_an_octave_from_where_they_are_written() {
401 let part = under_an_ottava("8va", true);
402 let sounding = part.to_sounding_pitch().unwrap();
403 assert_eq!(names(&sounding), ["C6", "D6", "E5"]);
404 assert!(!transposing(&sounding));
405 assert_eq!(
407 names(&sounding.to_sounding_pitch().unwrap()),
408 ["C6", "D6", "E5"]
409 );
410 let written = sounding.to_written_pitch().unwrap();
411 assert_eq!(names(&written), ["C5", "D5", "E5"]);
412 assert!(transposing(&written));
413 assert_eq!(
415 names(&written.to_written_pitch_by(true).unwrap()),
416 ["C6", "D6", "E5"]
417 );
418 }
419
420 #[test]
421 fn an_ottava_already_at_sounding_pitch_is_written_where_its_line_says() {
422 let part = under_an_ottava("15mb", false);
423 assert_eq!(
424 names(&part.to_sounding_pitch().unwrap()),
425 ["C5", "D5", "E5"]
426 );
427 let written = part.to_written_pitch().unwrap();
428 assert_eq!(names(&written), ["C7", "D7", "E5"]);
429 assert!(transposing(&written));
430 }
431}