sim_lib_music_serial/
time_point.rs1use std::num::NonZeroU16;
4
5use sim_lib_music_core::Time;
6use sim_lib_serial_core::{
7 AggregateRule, AlphabetError, AlphabetId, FiniteAlphabet, SerialAlphabet, Series, SeriesError,
8};
9use thiserror::Error;
10
11use crate::rotate_sequence_left;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct TimePointAlphabet {
16 modulus: NonZeroU16,
17 inner: FiniteAlphabet<u16>,
18}
19
20impl TimePointAlphabet {
21 pub fn try_new(modulus: NonZeroU16) -> Result<Self, TimePointError> {
23 let symbols = (0..modulus.get()).collect::<Vec<_>>();
24 let inner = FiniteAlphabet::try_new(
25 AlphabetId::try_new(format!("time-point/{}-v1", modulus.get()))?,
26 symbols,
27 )?;
28 Ok(Self { modulus, inner })
29 }
30
31 pub const fn modulus(&self) -> NonZeroU16 {
33 self.modulus
34 }
35
36 pub fn classes(&self) -> &[u16] {
38 self.inner.symbols()
39 }
40}
41
42impl SerialAlphabet for TimePointAlphabet {
43 type Symbol = u16;
44
45 fn id(&self) -> &AlphabetId {
46 self.inner.id()
47 }
48
49 fn symbols(&self) -> &[Self::Symbol] {
50 self.inner.symbols()
51 }
52}
53
54#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct TimePointSystem {
57 pub modulus: NonZeroU16,
59 pub unit: Time,
61}
62
63impl TimePointSystem {
64 pub fn alphabet(&self) -> Result<TimePointAlphabet, TimePointError> {
66 TimePointAlphabet::try_new(self.modulus)
67 }
68
69 pub fn onset_for(&self, point: u16) -> Option<Time> {
71 (point < self.modulus.get()).then(|| self.unit * i64::from(point))
72 }
73}
74
75#[derive(Clone, Debug, PartialEq, Eq)]
77pub struct TimePointRow {
78 pub points: Series<TimePointAlphabet>,
80}
81
82impl TimePointRow {
83 pub fn try_new(system: &TimePointSystem, points: Vec<u16>) -> Result<Self, TimePointError> {
85 Ok(Self {
86 points: Series::try_new(
87 system.alphabet()?,
88 AggregateRule::exhaustive_exactly_once(),
89 points,
90 )?,
91 })
92 }
93
94 pub fn modulus(&self) -> NonZeroU16 {
96 self.points.alphabet().modulus()
97 }
98
99 pub fn order(&self) -> &[u16] {
101 self.points.order()
102 }
103
104 pub fn onsets(&self, system: &TimePointSystem) -> Result<Vec<Time>, TimePointError> {
106 if system.modulus != self.modulus() {
107 return Err(TimePointError::SystemMismatch {
108 expected: self.modulus(),
109 found: system.modulus,
110 });
111 }
112 self.order()
113 .iter()
114 .map(|&point| {
115 system
116 .onset_for(point)
117 .ok_or(TimePointError::PointOutsideSystem {
118 point,
119 modulus: system.modulus,
120 })
121 })
122 .collect()
123 }
124
125 pub fn rotate(&self, steps: usize) -> Result<Self, TimePointError> {
127 Ok(Self {
128 points: Series::try_new(
129 self.points.alphabet().clone(),
130 self.points.rule().clone(),
131 rotate_sequence_left(self.points.order(), steps),
132 )?,
133 })
134 }
135}
136
137#[derive(Clone, Debug, PartialEq, Eq, Error)]
139pub enum TimePointError {
140 #[error(transparent)]
142 Alphabet(#[from] AlphabetError),
143 #[error(transparent)]
145 Series(#[from] SeriesError),
146 #[error("time-point row expects modulus {expected}, got {found}")]
148 SystemMismatch {
149 expected: NonZeroU16,
151 found: NonZeroU16,
153 },
154 #[error("time-point class {point} lies outside modulus {modulus}")]
156 PointOutsideSystem {
157 point: u16,
159 modulus: NonZeroU16,
161 },
162}