Skip to main content

music21_rs/instrument/
streams.rs

1//! What an instrument does to a stream: music21's `unbundleInstruments`,
2//! `bundleInstruments`, `deduplicate` and `partitionByInstrument`.
3//!
4//! A note may keep the instrument that plays it as its own (music21's
5//! `storedInstrument`), or a stream may hold the instrument as an element
6//! of its own, in force from where it stands. These move instruments
7//! between the two, fold together the instruments a part says twice, and
8//! split a score into a part for each instrument playing in it.
9
10use super::Instrument;
11use crate::defaults::FloatType;
12use crate::makenotation::sorted_events;
13use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
14
15/// The instrument a note, chord or unpitched stroke keeps as its own.
16fn stored(element: &StreamElement) -> Option<&Instrument> {
17    match element {
18        StreamElement::Note(note) => note.stored_instrument(),
19        StreamElement::Chord(chord) => chord.stored_instrument(),
20        StreamElement::Unpitched(stroke) => stroke.written().stored_instrument(),
21        StreamElement::PercussionChord(chord) => chord.written().stored_instrument(),
22        _ => None,
23    }
24}
25
26/// Gives a note, chord or unpitched stroke an instrument of its own, or
27/// takes it away.
28fn set_stored(element: &mut StreamElement, instrument: Option<Instrument>) {
29    match element {
30        StreamElement::Note(note) => note.set_stored_instrument(instrument),
31        StreamElement::Chord(chord) => chord.set_stored_instrument(instrument),
32        StreamElement::Unpitched(stroke) => stroke.written_mut().set_stored_instrument(instrument),
33        StreamElement::PercussionChord(chord) => {
34            chord.written_mut().set_stored_instrument(instrument);
35        }
36        _ => {}
37    }
38}
39
40/// For each of a stream's leaves, whether the stream holds it itself rather
41/// than through a stream nested in it.
42fn held_directly(stream: &Stream) -> Vec<bool> {
43    let mut direct = Vec::new();
44    for event in stream.events() {
45        match event.element() {
46            StreamElement::Stream(inner) => {
47                direct.extend(std::iter::repeat_n(false, inner.leaves().len()));
48            }
49            _ => direct.push(true),
50        }
51    }
52    direct
53}
54
55/// Puts the instrument each note, chord or stroke of a stream keeps as its
56/// own into the stream beside it, at its offset: music21's
57/// `unbundleInstruments`.
58///
59/// Only what the stream holds itself is looked at, not what a measure or a
60/// voice in it holds. The notes keep their instruments too.
61///
62/// ```
63/// use music21_rs::{instrument::unbundle_instruments, Instrument, Stream, StreamElement, Unpitched};
64///
65/// let mut stream = Stream::new();
66/// for (offset, kind) in [(0.0, "BassDrum"), (1.0, "Cowbell")] {
67///     let mut stroke = Unpitched::new();
68///     stroke.written_mut().set_stored_instrument(Some(Instrument::of_kind(kind)?));
69///     stream.insert(offset, stroke);
70/// }
71/// unbundle_instruments(&mut stream);
72/// let said: Vec<String> = stream
73///     .events()
74///     .iter()
75///     .map(|event| match event.element() {
76///         StreamElement::Instrument(instrument) => format!("{} {}", event.offset(), instrument.kind()),
77///         _ => format!("{} stroke", event.offset()),
78///     })
79///     .collect();
80/// assert_eq!(said, ["0 BassDrum", "0 stroke", "1 Cowbell", "1 stroke"]);
81/// # Ok::<(), music21_rs::Error>(())
82/// ```
83pub fn unbundle_instruments(stream: &mut Stream) {
84    let added: Vec<StreamEvent> = stream
85        .events()
86        .iter()
87        .filter_map(|event| {
88            stored(event.element())
89                .map(|instrument| StreamEvent::new(event.offset(), instrument.clone()))
90        })
91        .collect();
92    stream.insert_sorted(added);
93}
94
95/// Gives each note, chord or stroke of a stream the instrument standing
96/// last before it as its own, and takes the instruments out of the stream:
97/// music21's `bundleInstruments`, which undoes
98/// [`unbundle_instruments`].
99///
100/// Only what the stream holds itself is looked at. A note with no
101/// instrument before it is left with none of its own, whatever it had.
102pub fn bundle_instruments(stream: &mut Stream) {
103    stream.insert_sorted(Vec::new());
104    let direct = held_directly(stream);
105    let mut last: Option<Instrument> = None;
106    stream.retain_leaves(&mut |position, element| {
107        if !direct[position] {
108            return true;
109        }
110        if let StreamElement::Instrument(instrument) = element {
111            last = Some(instrument.as_ref().clone());
112            return false;
113        }
114        if matches!(
115            element,
116            StreamElement::Note(_)
117                | StreamElement::Chord(_)
118                | StreamElement::Unpitched(_)
119                | StreamElement::PercussionChord(_)
120        ) {
121            set_stored(element, last.clone());
122        }
123        true
124    });
125}
126
127/// What [`deduplicate`] makes of the instruments one part holds, each
128/// beside its offset in the part, in order: for each, nothing where it is
129/// left as it is, else what it is to stand as, or nothing where it goes.
130/// Instruments are settled with the others at their offset only.
131pub(crate) fn settle_by_offset(
132    held: &[(FloatType, &Instrument)],
133) -> Vec<Option<Option<Instrument>>> {
134    let mut plan: Vec<Option<Option<Instrument>>> = vec![None; held.len()];
135    let mut offsets: Vec<FloatType> = Vec::new();
136    for (offset, _) in held {
137        if !offsets.contains(offset) {
138            offsets.push(*offset);
139        }
140    }
141    for offset in offsets {
142        let places: Vec<usize> = (0..held.len())
143            .filter(|&index| held[index].0 == offset)
144            .collect();
145        let together: Vec<&Instrument> = places.iter().map(|&index| held[index].1).collect();
146        if let Some(settled) = settle(&together) {
147            for (index, becomes) in places.into_iter().zip(settled) {
148                plan[index] = Some(becomes);
149            }
150        }
151    }
152    plan
153}
154
155/// What [`deduplicate`] makes of the instruments standing together, in
156/// order: each instrument as it is to stand, or nothing where it goes.
157/// Nothing at all where they are left as they are.
158///
159/// Where the instruments disagree on the part's name or on their own, they
160/// are left alone. Otherwise every instrument kept takes the one name each
161/// of them gives, and of instruments all of one kind only the first is
162/// kept, while of instruments of several kinds the bare `Instrument`s go.
163pub(crate) fn settle(instruments: &[&Instrument]) -> Option<Vec<Option<Instrument>>> {
164    if instruments.len() <= 1 {
165        return None;
166    }
167    let mut part_names: Vec<&str> = instruments
168        .iter()
169        .filter_map(|instrument| instrument.part_name())
170        .collect();
171    part_names.sort_unstable();
172    part_names.dedup();
173    let mut names: Vec<&str> = instruments
174        .iter()
175        .filter_map(|instrument| instrument.name())
176        .collect();
177    names.sort_unstable();
178    names.dedup();
179    if part_names.len() > 1 || names.len() > 1 {
180        return None;
181    }
182    let part_name = part_names.first().map(|name| name.to_string());
183    let name = names.first().map(|name| name.to_string());
184    let mut kinds: Vec<&str> = instruments
185        .iter()
186        .map(|instrument| instrument.kind())
187        .collect();
188    kinds.sort_unstable();
189    kinds.dedup();
190    let one_kind = kinds.len() == 1;
191    Some(
192        instruments
193            .iter()
194            .enumerate()
195            .map(|(index, instrument)| {
196                let goes = if one_kind {
197                    index > 0
198                } else {
199                    instrument.kind() == "Instrument"
200                };
201                (!goes).then(|| {
202                    let mut named = (*instrument).clone();
203                    named.set_part_name(part_name.clone());
204                    named.set_name(name.clone());
205                    named
206                })
207            })
208            .collect(),
209    )
210}
211
212/// Folds together the instruments a part says more than once: music21's
213/// `instrument.deduplicate`.
214///
215/// Each part of a score is done on its own, or the stream itself where it
216/// holds no parts, and the instruments standing at one offset of it, in a
217/// measure or the part itself, are settled together. Where they agree on
218/// the part's name and on their own, or say nothing, all of them take those
219/// names; then of instruments all of one kind only the first is kept, and of
220/// instruments of several kinds the bare `Instrument`s go.
221///
222/// ```
223/// use music21_rs::{instrument::deduplicate, Instrument, Stream, StreamElement};
224///
225/// let mut named = Instrument::new();
226/// named.set_name(Some("Semi-Hollow Body".to_string()));
227/// let mut guitar = Instrument::new();
228/// guitar.set_part_name(Some("Electric Guitar".to_string()));
229/// let mut stream = Stream::new();
230/// stream.insert(4.0, named);
231/// stream.insert(4.0, guitar);
232/// deduplicate(&mut stream);
233/// let [event] = stream.events() else { panic!("one instrument is left") };
234/// let StreamElement::Instrument(left) = event.element() else { panic!() };
235/// assert_eq!(left.part_name(), Some("Electric Guitar"));
236/// assert_eq!(left.name(), Some("Semi-Hollow Body"));
237/// # Ok::<(), music21_rs::Error>(())
238/// ```
239pub fn deduplicate(stream: &mut Stream) {
240    // Each leaf's part, for the leaves in one.
241    let mut part_of: Vec<Option<usize>> = Vec::new();
242    if stream.has_part_like_streams() {
243        for (index, event) in stream.events().iter().enumerate() {
244            match event.element() {
245                StreamElement::Stream(inner) => {
246                    part_of.extend(std::iter::repeat_n(Some(index), inner.leaves().len()));
247                }
248                _ => part_of.push(None),
249            }
250        }
251    } else {
252        part_of = vec![Some(0); stream.leaves().len()];
253    }
254
255    let leaves = stream.leaves();
256    let mut plan: Vec<Option<Option<Instrument>>> = vec![None; leaves.len()];
257    let mut parts: Vec<usize> = part_of.iter().flatten().copied().collect();
258    parts.dedup();
259    for part in parts {
260        let places: Vec<usize> = (0..leaves.len())
261            .filter(|&position| {
262                part_of[position] == Some(part)
263                    && matches!(leaves[position].1, StreamElement::Instrument(_))
264            })
265            .collect();
266        let held: Vec<(FloatType, &Instrument)> = places
267            .iter()
268            .filter_map(|&position| match leaves[position].1 {
269                StreamElement::Instrument(instrument) => {
270                    Some((leaves[position].0, instrument.as_ref()))
271                }
272                _ => None,
273            })
274            .collect();
275        for (position, becomes) in places.into_iter().zip(settle_by_offset(&held)) {
276            plan[position] = becomes;
277        }
278    }
279    stream.retain_leaves(&mut |position, element| match plan[position].take() {
280        None => true,
281        Some(None) => false,
282        Some(Some(becomes)) => {
283            *element = becomes.into();
284            true
285        }
286    });
287}
288
289/// Splits a score into a part for each instrument playing in it, each part
290/// gathering what is played on that instrument from every part of the
291/// score: music21's `partitionByInstrument`.
292///
293/// Each part of the score is flattened, or the stream itself where it holds
294/// none. An instrument is in force from where it stands until the next one
295/// in its part, the last until the part ends, and whatever starts while it
296/// is in force goes to the part for its name; instruments with one name
297/// share a part, the first of them standing at its start, and a part is
298/// named by its instrument's name. What a part plays under two instruments
299/// of one name is gathered once. A stream with no instrument in it comes
300/// back as a score of its parts flattened.
301///
302/// The parts come out as music21's do, with no measures; spanners are not
303/// carried across, as the places they name move.
304///
305/// ```
306/// use music21_rs::{instrument::partition_by_instrument, Instrument, Note, Stream};
307///
308/// let mut part = Stream::new();
309/// part.insert(0.0, Instrument::of_kind("Piccolo")?);
310/// part.insert(0.0, Note::from_name("C4")?);
311/// part.insert(1.0, Instrument::of_kind("Trombone")?);
312/// part.insert(1.0, Note::from_name("D4")?);
313/// part.insert(2.0, Note::from_name("E4")?);
314/// let score = partition_by_instrument(&part);
315/// let names: Vec<Option<&str>> = score.parts().iter().map(|part| part.id()).collect();
316/// assert_eq!(names, [Some("Piccolo"), Some("Trombone")]);
317/// assert_eq!(score.parts()[1].notes().len(), 2);
318/// # Ok::<(), music21_rs::Error>(())
319/// ```
320pub fn partition_by_instrument(stream: &Stream) -> Stream {
321    let flattened: Vec<Stream> = if stream.has_part_like_streams() {
322        stream
323            .events()
324            .iter()
325            .filter_map(|event| event.element().as_stream())
326            .map(Stream::flatten)
327            .collect()
328    } else {
329        vec![stream.flatten()]
330    };
331    let flattened: Vec<Stream> = flattened
332        .into_iter()
333        .map(|part| {
334            let events = sorted_events(part.events().to_vec());
335            part.with_events(events)
336        })
337        .collect();
338
339    // One part for each name, in the order the names are met, holding the
340    // first instrument of that name -- except that an instrument with no
341    // name at all takes the place of the one before it, as music21's
342    // `names[None or '']` does.
343    let mut names: Vec<(String, Instrument)> = Vec::new();
344    for part in &flattened {
345        for event in part.events() {
346            let StreamElement::Instrument(instrument) = event.element() else {
347                continue;
348            };
349            let key = instrument.name().unwrap_or("").to_string();
350            match names.iter_mut().find(|(name, _)| *name == key) {
351                Some((_, held)) if instrument.name().is_none() => {
352                    *held = instrument.as_ref().clone();
353                }
354                Some(_) => {}
355                None => names.push((key, instrument.as_ref().clone())),
356            }
357        }
358    }
359    if names.is_empty() {
360        let mut score = Stream::with_kind(StreamKind::Score);
361        for part in flattened {
362            score.insert(0.0, part);
363        }
364        return score;
365    }
366
367    let mut gathered: Vec<Vec<StreamEvent>> = names
368        .iter()
369        .map(|(_, instrument)| vec![StreamEvent::new(0.0, instrument.clone())])
370        .collect();
371    // What each new part already holds, as the part and event it came from.
372    let mut taken: Vec<Vec<(usize, usize)>> = vec![Vec::new(); names.len()];
373    for (source, part) in flattened.iter().enumerate() {
374        let events = part.events();
375        let end = part.end_offset();
376        let starts: Vec<(usize, FloatType)> = events
377            .iter()
378            .enumerate()
379            .filter(|(_, event)| matches!(event.element(), StreamElement::Instrument(_)))
380            .map(|(index, event)| (index, event.offset()))
381            .collect();
382        for (which, &(index, start)) in starts.iter().enumerate() {
383            let StreamElement::Instrument(instrument) = events[index].element() else {
384                continue;
385            };
386            let stop = starts.get(which + 1).map_or(end, |next| next.1);
387            let key = instrument.name().unwrap_or("");
388            let Some(target) = names.iter().position(|(name, _)| name == key) else {
389                continue;
390            };
391            for (place, event) in events.iter().enumerate() {
392                if matches!(event.element(), StreamElement::Instrument(_))
393                    || !in_span(event, start, stop)
394                    || taken[target].contains(&(source, place))
395                {
396                    continue;
397                }
398                taken[target].push((source, place));
399                gathered[target].push(event.clone());
400            }
401        }
402    }
403
404    let mut score = Stream::with_kind(StreamKind::Score);
405    for ((name, _), events) in names.into_iter().zip(gathered) {
406        let mut part = Stream::with_kind(StreamKind::Part).with_events(sorted_events(events));
407        part.set_id(Some(name));
408        score.insert(0.0, part);
409    }
410    score
411}
412
413/// Whether an event starts inside an instrument's span: music21's
414/// `getElementsByOffset` with `mustBeginInSpan`, not counting what starts at
415/// the end, except that a span of no length takes in what lasts no time at
416/// its start.
417fn in_span(event: &StreamEvent, start: FloatType, stop: FloatType) -> bool {
418    let offset = event.offset();
419    if stop <= start {
420        return offset == start && event.element().quarter_length() == 0.0;
421    }
422    offset >= start && offset < stop
423}
424
425#[cfg(test)]
426mod tests {
427    use super::*;
428    use crate::Note;
429
430    #[test]
431    fn bundling_puts_back_what_unbundling_took_out() {
432        let mut stream = Stream::new();
433        let mut first = Note::from_name("C4").unwrap();
434        first.set_stored_instrument(Some(Instrument::of_kind("BassDrum").unwrap()));
435        stream.insert(0.0, first);
436        stream.insert(1.0, Note::from_name("D4").unwrap());
437        let mut last = Note::from_name("E4").unwrap();
438        last.set_stored_instrument(Some(Instrument::of_kind("Cowbell").unwrap()));
439        stream.insert(2.0, last);
440
441        unbundle_instruments(&mut stream);
442        assert_eq!(stream.len(), 5);
443        bundle_instruments(&mut stream);
444        let kinds: Vec<Option<String>> = stream
445            .events()
446            .iter()
447            .map(|event| stored(event.element()).map(|held| held.kind().to_string()))
448            .collect();
449        // music21's own example: the note between takes the bass drum.
450        assert_eq!(
451            kinds,
452            [
453                Some("BassDrum".to_string()),
454                Some("BassDrum".to_string()),
455                Some("Cowbell".to_string())
456            ]
457        );
458    }
459
460    #[test]
461    fn a_spanner_follows_its_notes_when_instruments_come_and_go() {
462        let mut stream = Stream::new();
463        let mut note = Note::from_name("C4").unwrap();
464        note.set_stored_instrument(Some(Instrument::of_kind("Violin").unwrap()));
465        stream.insert(0.0, note);
466        stream.insert(1.0, Note::from_name("D4").unwrap());
467        stream.add_spanner(crate::Spanner::slur(vec![0, 1]));
468        unbundle_instruments(&mut stream);
469        assert_eq!(stream.spanners()[0].spanned(), [Some(1), Some(2)]);
470        bundle_instruments(&mut stream);
471        assert_eq!(stream.spanners()[0].spanned(), [Some(0), Some(1)]);
472    }
473
474    #[test]
475    fn a_stream_with_no_instrument_is_its_parts_flattened() {
476        let mut part = Stream::with_kind(StreamKind::Part);
477        part.insert(0.0, Note::from_name("C4").unwrap());
478        let mut score = Stream::with_kind(StreamKind::Score);
479        score.insert(0.0, part);
480        let partitioned = partition_by_instrument(&score);
481        assert_eq!(partitioned.parts().len(), 1);
482        assert_eq!(partitioned.parts()[0].notes().len(), 1);
483    }
484}