1use super::{Stream, StreamElement, StreamKind};
4use crate::defaults::FloatType;
5use crate::error::{Error, Result};
6use crate::notation::{Tie, TieType};
7use crate::pitch::Pitch;
8
9impl Stream {
10 pub fn strip_ties(&mut self, match_by_pitch: bool) -> Result<()> {
49 if self.has_part_like_streams() {
50 for event in &mut self.events {
51 if let StreamElement::Stream(inner) = &mut event.element {
52 inner.strip_ties(match_by_pitch)?;
53 }
54 }
55 return Ok(());
56 }
57 let has_voices = self.events.iter().any(|event| {
58 matches!(&event.element, StreamElement::Stream(inner) if inner.kind == StreamKind::Voice)
59 });
60 if has_voices {
61 for event in &mut self.events {
62 if let StreamElement::Stream(inner) = &mut event.element
63 && inner.kind == StreamKind::Voice
64 {
65 inner.strip_ties(match_by_pitch)?;
66 }
67 }
68 return Ok(());
69 }
70
71 let leaves = self.leaves();
72 let mut order: Vec<usize> = (0..leaves.len()).collect();
74 order.sort_by(|left, right| {
75 let (left_offset, left_element) = leaves[*left];
76 let (right_offset, right_element) = leaves[*right];
77 let grace = |element: &StreamElement| {
78 element
79 .duration()
80 .is_some_and(crate::duration::Duration::is_grace)
81 };
82 left_offset
83 .total_cmp(&right_offset)
84 .then(
85 left_element
86 .class_sort_order()
87 .cmp(&right_element.class_sort_order()),
88 )
89 .then(grace(right_element).cmp(&grace(left_element)))
90 });
91 let line: Vec<(usize, Tied)> = order
92 .into_iter()
93 .filter_map(|position| {
94 let element = leaves[position].1;
95 Tied::of(element)
96 .filter(|_| element.quarter_length() > 0.0)
97 .map(|tied| (position, tied))
98 })
99 .collect();
100
101 let mut merged: Vec<(usize, FloatType)> = Vec::new();
102 let mut taken: Vec<(usize, usize)> = Vec::new();
103 let mut connected: Vec<usize> = Vec::new();
104 for index in 0..line.len() {
105 let tied = &line[index].1;
106 let last = index.checked_sub(1);
107 let ends = |connected: &[usize]| {
108 ends_run(
109 tied,
110 last.map(|last| &line[last].1),
111 last,
112 connected,
113 match_by_pitch,
114 )
115 };
116 let mut end_match = None;
117 match tied.tie {
118 Some(TieType::Start) => {
119 if last.is_none_or(|last| !connected.contains(&last)) {
120 connected = vec![index];
121 } else {
122 connected.push(index);
123 }
124 end_match = Some(false);
125 }
126 Some(TieType::Continue) => {
127 if connected.is_empty() {
128 connected.push(index);
129 } else if match_by_pitch {
130 if ends(&connected) {
131 connected.push(index);
132 } else {
133 connected = vec![index];
134 }
135 } else if tied.all_continue {
136 let before = last.map(|last| &line[last].1);
137 if before.is_some_and(|before| {
138 before.sounds && before.pitches.len() != tied.pitches.len()
139 }) {
140 connected = vec![index];
141 } else {
142 connected.push(index);
143 }
144 } else {
145 connected.clear();
146 }
147 end_match = Some(false);
148 }
149 _ => {}
150 }
151 let end_match = end_match.unwrap_or_else(|| ends(&connected));
152 if !end_match {
153 continue;
154 }
155 connected.push(index);
156 if connected.len() < 2 {
157 connected.clear();
158 continue;
159 }
160 let first = connected[0];
161 let mut sum = 0.0;
162 for &later in &connected[1..] {
163 sum += line[later].1.quarter_length;
164 taken.push((line[later].0, line[first].0));
165 }
166 if sum == 0.0 {
167 return Err(Error::Stream(
168 "aggregated ties have a zero duration sum".to_string(),
169 ));
170 }
171 merged.push((
172 line[first].0,
173 crate::makenotation::op_frac(line[first].1.quarter_length + sum),
174 ));
175 connected.clear();
176 }
177 if merged.is_empty() {
178 return Ok(());
179 }
180
181 let mut moved: Vec<Option<usize>> = (0..leaves.len()).map(Some).collect();
183 for &(gone, kept) in &taken {
184 moved[gone] = Some(kept);
185 }
186 self.move_spanners_everywhere(&moved);
187 let gone: Vec<usize> = taken.iter().map(|(gone, _)| *gone).collect();
188 let mut failure = None;
189 self.retain_leaves(&mut |position, element| {
190 if gone.contains(&position) {
191 return false;
192 }
193 if let Some((_, length)) = merged.iter().find(|(kept, _)| *kept == position)
194 && let Err(error) = lengthen(element, *length)
195 {
196 failure = Some(error);
197 }
198 true
199 });
200 failure.map_or(Ok(()), Err)
201 }
202
203 fn move_spanners_everywhere(&mut self, moved: &[Option<usize>]) {
206 let mut first = 0;
207 self.move_spanners_from(&mut first, moved);
208 }
209
210 fn move_spanners_from(&mut self, first: &mut usize, moved: &[Option<usize>]) {
211 let start = *first;
212 let count = self.leaves().len();
213 let mine: Vec<Option<usize>> = (0..count)
214 .map(|place| {
215 moved
216 .get(start + place)
217 .copied()
218 .flatten()
219 .and_then(|to| to.checked_sub(start))
220 .filter(|to| *to < count)
221 })
222 .collect();
223 for spanner in &mut self.labels.spanners {
224 spanner.move_places(&mine);
225 spanner.drop_repeated_places();
226 }
227 for event in &mut self.events {
228 match &mut event.element {
229 StreamElement::Stream(inner) => inner.move_spanners_from(first, moved),
230 _ => *first += 1,
231 }
232 }
233 }
234}
235
236struct Tied {
238 tie: Option<TieType>,
239 sounds: bool,
241 pitch: Option<Pitch>,
243 pitches: Vec<Pitch>,
245 is_chord: bool,
246 all_continue: bool,
248 all_stop: bool,
250 quarter_length: FloatType,
251}
252
253impl Tied {
254 fn of(element: &StreamElement) -> Option<Self> {
255 let tie_type = |tie: Option<&Tie>| tie.map(Tie::tie_type);
256 let quarter_length = element.quarter_length();
257 Some(match element {
258 StreamElement::Note(note) => Self {
259 tie: tie_type(note.tie()),
260 sounds: true,
261 pitch: Some(note.pitch().clone()),
262 pitches: vec![note.pitch().clone()],
263 is_chord: false,
264 all_continue: false,
265 all_stop: false,
266 quarter_length,
267 },
268 StreamElement::Chord(chord) => {
269 let ties: Vec<Option<TieType>> = chord
270 .notes()
271 .iter()
272 .map(|note| tie_type(note.tie()))
273 .collect();
274 Self {
275 tie: tie_type(chord.tie()),
276 sounds: true,
277 pitch: None,
278 pitches: chord.pitches(),
279 is_chord: true,
280 all_continue: ties.iter().all(|tie| *tie == Some(TieType::Continue)),
281 all_stop: ties.iter().all(|tie| *tie == Some(TieType::Stop)),
282 quarter_length,
283 }
284 }
285 StreamElement::ChordSymbol(symbol) => Self {
286 tie: None,
287 sounds: true,
288 pitch: None,
289 pitches: symbol.pitches().unwrap_or_default(),
290 is_chord: true,
291 all_continue: false,
292 all_stop: false,
293 quarter_length,
294 },
295 StreamElement::Rest(rest) => Self {
296 tie: tie_type(rest.tie()),
297 sounds: false,
298 pitch: None,
299 pitches: Vec::new(),
300 is_chord: false,
301 all_continue: false,
302 all_stop: false,
303 quarter_length,
304 },
305 StreamElement::Unpitched(stroke) => Self {
308 tie: tie_type(stroke.written().tie()),
309 sounds: true,
310 pitch: None,
311 pitches: Vec::new(),
312 is_chord: false,
313 all_continue: false,
314 all_stop: false,
315 quarter_length,
316 },
317 StreamElement::PercussionChord(chord) => Self {
318 tie: tie_type(chord.written().tie()),
319 sounds: true,
320 pitch: None,
321 pitches: chord.pitches(),
322 is_chord: false,
323 all_continue: false,
324 all_stop: false,
325 quarter_length,
326 },
327 _ => return None,
328 })
329 .map(|mut tied| {
330 tied.all_continue =
333 tied.tie == Some(TieType::Continue) && (!tied.is_chord || tied.all_continue);
334 tied
335 })
336 }
337}
338
339fn ends_run(
342 tied: &Tied,
343 before: Option<&Tied>,
344 last: Option<usize>,
345 connected: &[usize],
346 match_by_pitch: bool,
347) -> bool {
348 if !tied.is_chord && tied.tie == Some(TieType::Stop) {
349 return true;
350 }
351 if !match_by_pitch {
352 return tied.is_chord
353 && before.is_some_and(|before| {
354 before.is_chord && tied.all_stop && before.pitches.len() == tied.pitches.len()
355 });
356 }
357 let Some(before) = before else {
358 return false;
359 };
360 let joined = last.is_some_and(|last| connected.contains(&last));
361 if !joined {
362 return false;
363 }
364 if let (Some(previous), Some(pitch)) = (&before.pitch, &tied.pitch) {
365 return previous == pitch;
366 }
367 if tied.pitch.is_none() {
370 return before.pitches.len() == tied.pitches.len()
371 && before
372 .pitches
373 .iter()
374 .zip(&tied.pitches)
375 .all(|(previous, pitch)| {
376 previous.step() == pitch.step() && previous.is_enharmonic(pitch)
377 });
378 }
379 false
380}
381
382fn lengthen(element: &mut StreamElement, length: FloatType) -> Result<()> {
385 let duration = crate::duration::Duration::new(length)?;
386 match element {
387 StreamElement::Note(note) => {
388 note.set_duration(duration);
389 note.set_tie(None);
390 }
391 StreamElement::Chord(chord) => {
392 chord.set_duration(duration);
393 chord.set_tie(None);
394 }
395 StreamElement::ChordSymbol(symbol) => symbol.set_duration(duration),
396 StreamElement::Rest(rest) => {
397 rest.set_duration(duration);
398 rest.set_tie(None);
399 }
400 StreamElement::Unpitched(stroke) => {
401 stroke.written_mut().set_duration(duration);
402 stroke.written_mut().set_tie(None);
403 }
404 StreamElement::PercussionChord(chord) => {
405 chord.written_mut().set_duration(duration);
406 chord.written_mut().set_tie(None);
407 }
408 _ => {}
409 }
410 Ok(())
411}