redact_composer_musical/rhythm/
mod.rs

1use std::cmp::Ordering;
2use std::ops::{Add, Range};
3
4use crate::rhythm::DivType::Div;
5use crate::timing::TimeSignature;
6use rand::distributions::WeightedIndex;
7use rand::{seq::SliceRandom, Rng};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[cfg(feature = "redact-composer")]
13use redact_composer_core::{derive::Element, timing::Timing};
14
15#[cfg(test)]
16mod test;
17
18/// A rhythm subdivision.
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct Subdivision {
22    /// The starting time of the subdivision relative to the rhythm's start.
23    pub start: i32,
24    /// The end time of the subdivision relative to the rhythm's start.
25    pub end: i32,
26    /// `true` if the subdivision is a rest.
27    pub is_rest: bool,
28}
29
30impl Subdivision {
31    /// Returns the subdivision's timing as a [`Range<i32>`].
32    pub fn timing(&self) -> Range<i32> {
33        self.start..self.end
34    }
35}
36
37impl From<Subdivision> for Range<i32> {
38    fn from(value: Subdivision) -> Self {
39        value.timing()
40    }
41}
42
43impl From<&Subdivision> for Range<i32> {
44    fn from(value: &Subdivision) -> Self {
45        value.timing()
46    }
47}
48
49#[cfg(feature = "redact-composer")]
50impl From<&Subdivision> for Timing {
51    fn from(value: &Subdivision) -> Self {
52        value.timing().into()
53    }
54}
55
56#[cfg(feature = "redact-composer")]
57impl From<Subdivision> for Timing {
58    fn from(value: Subdivision) -> Self {
59        value.timing().into()
60    }
61}
62
63impl From<Range<i32>> for Subdivision {
64    fn from(value: Range<i32>) -> Self {
65        Subdivision {
66            start: value.start,
67            end: value.end,
68            is_rest: false,
69        }
70    }
71}
72
73impl From<&Range<i32>> for Subdivision {
74    fn from(value: &Range<i32>) -> Self {
75        Subdivision {
76            start: value.start,
77            end: value.end,
78            is_rest: false,
79        }
80    }
81}
82
83impl From<i32> for Subdivision {
84    fn from(value: i32) -> Self {
85        Subdivision {
86            start: 0,
87            end: value,
88            is_rest: false,
89        }
90    }
91}
92
93impl From<&i32> for Subdivision {
94    fn from(value: &i32) -> Self {
95        Subdivision {
96            start: 0,
97            end: *value,
98            is_rest: false,
99        }
100    }
101}
102
103impl<I: IntoIterator<Item = T>, T: Into<Subdivision>> From<I> for Rhythm {
104    fn from(value: I) -> Self {
105        let mut stuff = value
106            .into_iter()
107            .map(Into::into)
108            .collect::<Vec<Subdivision>>();
109        stuff.sort_by_key(|div| div.start);
110
111        let result = stuff
112            .into_iter()
113            .scan(None::<Subdivision>, |opt_prev, item| {
114                let next = match opt_prev {
115                    None => {
116                        vec![item]
117                    }
118                    Some(prev) => {
119                        if item.start == 0 {
120                            vec![Subdivision {
121                                start: prev.end,
122                                end: item.end + prev.end,
123                                is_rest: false,
124                            }]
125                        } else if item.end <= prev.end {
126                            vec![]
127                        } else if item.start > prev.end {
128                            vec![
129                                Subdivision {
130                                    start: prev.end,
131                                    end: item.start,
132                                    is_rest: true,
133                                },
134                                Subdivision {
135                                    start: item.start,
136                                    end: item.end,
137                                    is_rest: item.is_rest,
138                                },
139                            ]
140                        } else {
141                            vec![Subdivision {
142                                start: prev.end,
143                                end: item.end,
144                                is_rest: item.is_rest,
145                            }]
146                        }
147                    }
148                };
149
150                if let Some(next) = next.last() {
151                    opt_prev.replace(*next);
152                }
153
154                Some(next)
155            })
156            .flatten()
157            .collect::<Vec<_>>();
158
159        Rhythm(result)
160    }
161}
162
163/// Rhythm division type used during construction of [`Rhythm`]s.
164#[derive(Debug)]
165pub enum DivType {
166    /// Non-rest subdivision length.
167    Div(i32),
168    /// Rest subdivision length.
169    Rest(i32),
170}
171
172impl From<DivType> for Subdivision {
173    fn from(value: DivType) -> Self {
174        match value {
175            Div(len) => Subdivision {
176                start: 0,
177                end: len,
178                is_rest: false,
179            },
180            DivType::Rest(len) => Subdivision {
181                start: 0,
182                end: len,
183                is_rest: true,
184            },
185        }
186    }
187}
188
189impl Add<Rhythm> for Rhythm {
190    type Output = Rhythm;
191
192    fn add(self, mut rhs: Rhythm) -> Self::Output {
193        let first_length = self.len();
194        let mut new_subdivisions = self.0.into_iter().collect::<Vec<_>>();
195        new_subdivisions.append(&mut rhs.offset(first_length).0);
196
197        Rhythm(new_subdivisions)
198    }
199}
200
201/// Represents a rhythm as a sequence of timing divisions ([`Vec<Subdivision>`]).
202#[derive(Debug, Clone)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
204#[cfg_attr(feature = "redact-composer", derive(Element))]
205pub struct Rhythm(pub Vec<Subdivision>);
206impl Rhythm {
207    /// Creates a new empty rhythm.
208    pub fn new() -> Rhythm {
209        Rhythm(vec![])
210    }
211
212    /// Returns an iterator over the rhythm's non-rest subdivisions.
213    pub fn iter(&self) -> impl Iterator<Item = &Subdivision> {
214        self.0.iter().filter(|div| !div.is_rest)
215    }
216
217    /// Returns an iterator over the rhythm's subdivisions (both rests and non-rests).
218    pub fn iter_including_rests(&self) -> impl Iterator<Item = &Subdivision> {
219        self.0.iter()
220    }
221
222    /// Generates a random rhythm where subdivision lengths are relatively balanced.
223    pub fn balanced_timing(
224        length: i32,
225        subdivisions: i32,
226        time_signature: &TimeSignature,
227        rng: &mut impl Rng,
228    ) -> Rhythm {
229        let ts = time_signature;
230        let mut rhythm = vec![length];
231
232        let lengths = [ts.bar(), ts.beat(), ts.half_beat(), ts.quarter_beat()];
233
234        while (rhythm.len() as i32) < subdivisions {
235            let longest = rhythm
236                .iter()
237                .copied()
238                .enumerate()
239                .max_by(|(_, a), (_, b)| a.cmp(b))
240                .unwrap();
241            let opt_subdivision = lengths.iter().find(|s| longest.1 / *s > 1);
242
243            if let Some(subdivision) = opt_subdivision {
244                rhythm.remove(longest.0);
245
246                let split_length = (longest.1 / subdivision) / 2 * subdivision;
247                rhythm.push(split_length);
248                rhythm.push(longest.1 - split_length);
249            } else {
250                break;
251            }
252        }
253
254        if (rhythm.len() as i32) != subdivisions {
255            panic!("Couldn't divide into desired number of subdivisions")
256        }
257
258        rhythm.shuffle(rng);
259
260        Rhythm::from(rhythm)
261    }
262
263    /// Generates a random rhythm.
264    pub fn random(
265        length: i32,
266        time_signature: &TimeSignature,
267        division_probability: impl Fn(i32) -> f32,
268        rest_probability: impl Fn(i32) -> f32,
269        rng: &mut impl Rng,
270    ) -> Rhythm {
271        let ts = time_signature;
272        let mut rhythm_build = vec![(length, false)];
273
274        let div_choices = |div: i32| match div {
275            div if div > ts.bar() => {
276                let mut bar_divisions = vec![ts.bar(); (div / ts.bar()) as usize];
277                if div % ts.bar() != 0 {
278                    bar_divisions.push(div % ts.bar());
279                }
280
281                Some(vec![bar_divisions])
282            }
283            div if div > ts.beats(2) => Some(vec![
284                vec![ts.beats(2), div - ts.beats(2)],
285                vec![ts.beat(), div - ts.beat()],
286            ]),
287            div if div == ts.beats(2) => Some(vec![
288                vec![ts.triplet(); 3],
289                vec![ts.beat() + ts.half_beat(), ts.half_beat()],
290                vec![ts.beat(), div - ts.beat()],
291            ]),
292            div if div >= ts.beat() => Some(vec![vec![ts.half_beat(), div - ts.half_beat()]]),
293            div if div >= ts.half_beat() => {
294                Some(vec![vec![ts.quarter_beat(), div - ts.quarter_beat()]])
295            }
296            _ => None,
297        };
298
299        while !rhythm_build.iter().all(|(_, stop_dividing)| *stop_dividing) {
300            rhythm_build = rhythm_build
301                .into_iter()
302                .flat_map(|(current_division, stop_dividing)| {
303                    if !stop_dividing
304                        && rng.gen_bool(
305                            division_probability(current_division)
306                                .clamp(0.0, 1.0)
307                                .into(),
308                        )
309                    {
310                        if let Some(choices) = div_choices(current_division) {
311                            let choice = choices.choose(rng).unwrap();
312
313                            choice.iter().map(|b| (*b, false)).collect::<Vec<_>>()
314                        } else {
315                            vec![(current_division, true)]
316                        }
317                    } else {
318                        vec![(current_division, true)]
319                    }
320                })
321                .collect();
322        }
323
324        Self(
325            rhythm_build
326                .into_iter()
327                .scan((0, 0), |acc, (x, _)| {
328                    acc.0 = acc.1;
329                    acc.1 = acc.0 + x;
330
331                    Some(Subdivision {
332                        start: acc.0,
333                        end: acc.1,
334                        is_rest: rng.gen_bool(rest_probability(x).clamp(0.0, 1.0).into()),
335                    })
336                })
337                .collect(),
338        )
339    }
340
341    /// Generates a random rhythm using a set of sub-sequences of subdivision lengths. Random sub-sequences are chosen
342    /// until their combined length reaches the target length.
343    pub fn random_with_subdivisions_weights(
344        length: i32,
345        subdivision_weights: &[(Vec<i32>, i32)],
346        rng: &mut impl Rng,
347    ) -> Rhythm {
348        let mut timings = vec![];
349        let mut sum = 0;
350
351        if subdivision_weights
352            .iter()
353            .all(|(t, _)| t.iter().sum::<i32>() > length)
354        {
355            return Rhythm::new();
356        }
357
358        while sum < length {
359            let remaining = length - sum;
360            let sizeable_choices = subdivision_weights
361                .iter()
362                .filter(|(t, _)| t.iter().sum::<i32>() <= remaining)
363                .collect::<Vec<_>>();
364            let dist = WeightedIndex::new(sizeable_choices.iter().map(|(_, i)| i)).unwrap();
365
366            let choice = &sizeable_choices[rng.sample(dist)].0;
367
368            timings.push(choice.clone());
369            sum += choice.iter().sum::<i32>();
370        }
371
372        timings.shuffle(rng);
373
374        Rhythm(
375            timings
376                .into_iter()
377                .flatten()
378                .scan((0, 0), |acc, x| {
379                    acc.0 = acc.1;
380                    acc.1 = acc.0 + x;
381                    Some(*acc)
382                })
383                .map(|t| Subdivision {
384                    start: t.0,
385                    end: t.1,
386                    is_rest: false,
387                })
388                .collect::<Vec<_>>(),
389        )
390    }
391
392    /// Returns a new [`Rhythm`], based on the input [`Rhythm`] offset by a given `amount`.
393    pub fn offset(&mut self, amount: i32) -> Rhythm {
394        Rhythm(
395            self.0
396                .iter()
397                .map(|s| Subdivision {
398                    start: s.start + amount,
399                    end: s.end + amount,
400                    is_rest: s.is_rest,
401                })
402                .collect::<Vec<_>>(),
403        )
404    }
405
406    /// Creates a new [`Rhythm`] from this one sized according to the `frame_size`. If the rhythm is
407    /// smaller than `frame_size` it it will be extended with rest. If the rhythm is larger
408    /// than `frame_size` it will be truncated to fit `frame_size` exactly.
409    pub fn frame(&self, frame_size: i32) -> Rhythm {
410        let mut divs = self
411            .0
412            .iter()
413            .take_while(|div| div.start < frame_size)
414            .copied()
415            .collect::<Vec<_>>();
416
417        if let Some(last) = divs.last_mut() {
418            let copied_end = last.end;
419            match last.end.cmp(&frame_size) {
420                Ordering::Less => {
421                    if last.is_rest {
422                        last.end = frame_size;
423                    } else {
424                        divs.push(Subdivision {
425                            start: copied_end,
426                            end: frame_size,
427                            is_rest: true,
428                        });
429                    }
430                }
431                Ordering::Greater => {
432                    last.end = frame_size;
433                }
434                Ordering::Equal => {}
435            }
436
437            Rhythm(divs)
438        } else {
439            Rhythm(Vec::new())
440        }
441    }
442
443    /// Convenience alias of [`Self::frame`].
444    pub fn repeat_every(&self, length: i32) -> Rhythm {
445        self.frame(length)
446    }
447
448    /// Returns the length of the rhythm in ticks. Includes leading and trailing rests.
449    #[allow(clippy::len_without_is_empty)]
450    pub fn len(&self) -> i32 {
451        self.0.last().map(|r| r.end).unwrap_or_default()
452    }
453
454    /// Repeats the [`Rhythm`] over the given time range. If the range is smaller than the rhythm
455    /// however, it will be truncated to fit.
456    pub fn over(&self, range: impl Into<Range<i32>>) -> Vec<Subdivision> {
457        self.iter_over(range.into()).collect()
458    }
459
460    /// Iterates over rhythm [`Subdivision`]s over the time range, following the rhythmic pattern
461    /// of this [`Rhythm`]. The rhythm will repeat if the time range is longer than the rhythm,
462    /// or be truncated if shorter.
463    pub fn iter_over<'a>(
464        &'a self,
465        range: impl Into<Range<i32>> + 'a,
466    ) -> impl Iterator<Item = Subdivision> + '_ {
467        let rhythm_length = self.len();
468        let range = range.into();
469
470        self.0
471            .iter()
472            .cycle()
473            .scan(range.start, move |offset, subdivision| {
474                if let Some(last_division) = self.0.last() {
475                    let offset_subdivision = Subdivision {
476                        start: *offset + subdivision.start,
477                        end: *offset + subdivision.end,
478                        is_rest: subdivision.is_rest,
479                    };
480
481                    if subdivision == last_division {
482                        *offset += rhythm_length;
483                    }
484
485                    if offset_subdivision.end <= range.end {
486                        Some(offset_subdivision)
487                    } else {
488                        None
489                    }
490                } else {
491                    None
492                }
493            })
494            .filter(|div| !div.is_rest)
495    }
496}
497
498impl Default for Rhythm {
499    fn default() -> Self {
500        Rhythm::new()
501    }
502}