torque_tracker_engine/audio_processing/
playback.rs1use std::{num::NonZero, ops::ControlFlow};
2
3use crate::{
4 audio_processing::{sample::SamplePlayer, Frame},
5 channel::Pan,
6 manager::PlaybackSettings,
7 project::song::Song,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct PlaybackStatus {
12 pub position: PlaybackPosition,
13 }
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct PlaybackPosition {
21 pub order: Option<u16>,
23 pub pattern: u8,
24 pub row: u16,
25 pub loop_active: bool,
27}
28
29impl PlaybackPosition {
30 #[inline]
31 fn step_row(&mut self, song: &Song) -> ControlFlow<()> {
32 self.row += 1;
33 if self.row >= song.patterns[usize::from(self.pattern)].row_count() {
34 self.row = 0;
36 if let Some(order) = &mut self.order {
38 if let Some(pattern) = song.next_pattern(order) {
40 self.pattern = pattern;
42 ControlFlow::Continue(())
43 } else {
44 if !self.loop_active {
46 return ControlFlow::Break(());
48 }
49 *order = 0;
52 if let Some(pattern) = song.next_pattern(order) {
53 self.pattern = pattern;
54 ControlFlow::Continue(())
55 } else {
56 ControlFlow::Break(())
58 }
59 }
60 } else if self.loop_active {
61 return ControlFlow::Continue(());
63 } else {
64 return ControlFlow::Break(());
66 }
67 } else {
68 ControlFlow::Continue(())
70 }
71 }
72
73 fn new(settings: PlaybackSettings, song: &Song) -> Option<Self> {
75 match settings {
76 PlaybackSettings::Pattern { idx, should_loop } => {
77 if idx < u8::try_from(song.patterns.len()).unwrap() {
79 Some(Self {
80 order: None,
81 pattern: idx,
82 row: 0,
83 loop_active: should_loop,
84 })
85 } else {
86 None
87 }
88 }
89 PlaybackSettings::Order {
90 mut idx,
91 should_loop,
92 } => {
93 let pattern = song.next_pattern(&mut idx)?;
94 Some(Self {
95 order: Some(idx),
96 pattern,
97 row: 0,
98 loop_active: should_loop,
99 })
100 }
101 }
102 }
103}
104
105pub struct PlaybackState {
106 position: PlaybackPosition,
107 is_done: bool,
108 tick: u8,
110 frame: u32,
111
112 samplerate: NonZero<u32>,
114
115 voices: [Option<SamplePlayer>; PlaybackState::VOICES],
116}
117
118impl PlaybackState {
119 pub const VOICES: usize = Song::MAX_CHANNELS;
121
122 pub fn iter<'playback, 'song, const INTERPOLATION: u8>(
123 &'playback mut self,
124 song: &'song Song,
125 ) -> PlaybackIter<'song, 'playback, INTERPOLATION> {
126 PlaybackIter { state: self, song }
127 }
128
129 fn frames_per_tick(samplerate: NonZero<u32>, tempo: NonZero<u8>) -> u32 {
130 (samplerate.get() * 2) / u32::from(tempo.get())
132 }
133
134 pub fn get_status(&self) -> PlaybackStatus {
135 PlaybackStatus {
137 position: self.position,
138 }
139 }
140
141 pub fn set_samplerate(&mut self, samplerate: NonZero<u32>) {
142 self.samplerate = samplerate;
143 self.voices
144 .iter_mut()
145 .flatten()
146 .for_each(|voice| voice.set_out_samplerate(samplerate));
147 }
148
149 pub fn is_done(&self) -> bool {
150 self.is_done
151 }
152}
153
154impl PlaybackState {
155 pub fn new(song: &Song, samplerate: NonZero<u32>, settings: PlaybackSettings) -> Option<Self> {
157 let mut out = Self {
158 position: PlaybackPosition::new(settings, song)?,
159 is_done: false,
160 tick: song.initial_speed.get(),
161 frame: Self::frames_per_tick(samplerate, song.initial_tempo),
162 samplerate,
163 voices: std::array::from_fn(|_| None),
164 };
165 out.iter::<0>(song).create_sample_players();
167 Some(out)
168 }
169}
170
171impl std::fmt::Debug for PlaybackState {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 f.debug_struct("PlaybackState")
174 .field("position", &self.position)
175 .field("tick", &self.tick)
176 .field("frame", &self.frame)
177 .field("samplerate", &self.samplerate)
178 .finish_non_exhaustive()?;
179 write!(
180 f,
181 "active channels: {}",
182 self.voices.iter().filter(|v| v.is_some()).count()
183 )
184 }
185}
186
187pub struct PlaybackIter<'song, 'playback, const INTERPOLATION: u8> {
188 state: &'playback mut PlaybackState,
189 song: &'song Song,
190}
191
192impl<const INTERPOLATION: u8> PlaybackIter<'_, '_, INTERPOLATION> {
193 pub fn frames_per_tick(&self) -> u32 {
194 PlaybackState::frames_per_tick(self.state.samplerate, self.song.initial_tempo)
195 }
196}
197
198impl<const INTERPOLATION: u8> Iterator for PlaybackIter<'_, '_, INTERPOLATION> {
199 type Item = Frame;
200
201 fn next(&mut self) -> Option<Self::Item> {
202 fn scale_vol(vol: u8) -> f32 {
203 (vol as f32) / (u8::MAX as f32)
204 }
205
206 fn scale_pan(pan: u8) -> f32 {
208 debug_assert!((0..=64).contains(&pan));
209 (pan as f32) * const { (1. / 64.) * (std::f32::consts::FRAC_PI_2) }
210 }
211
212 if self.state.is_done {
213 return None;
214 }
215
216 debug_assert!(self.song.volume.len() == self.state.voices.len());
217 debug_assert!(self.song.pan.len() == self.state.voices.len());
218
219 let out: Frame = self
220 .state
221 .voices
222 .iter_mut()
223 .zip(self.song.volume)
224 .zip(self.song.pan)
225 .flat_map(|((channel, vol), pan)| {
226 if let Some(voice) = channel {
227 let mut out = voice.next::<INTERPOLATION>().unwrap();
228 if voice.check_position().is_break() {
230 *channel = None;
231 }
232 let channel_vol = scale_vol(vol);
234 if let Pan::Value(pan) = pan {
235 let angle = scale_pan(pan);
236 out.pan_constant_power(angle);
237 }
238 Some(out * channel_vol)
239 } else {
248 None
249 }
250 })
251 .sum();
252 self.step();
253 let out_vol = scale_vol(self.song.global_volume);
254 Some(out * out_vol)
255 }
256}
257
258impl<const INTERPOLATION: u8> PlaybackIter<'_, '_, INTERPOLATION> {
259 fn step(&mut self) {
260 if self.state.frame > 0 {
262 self.state.frame -= 1;
263 return;
264 } else {
265 self.state.frame = self.frames_per_tick();
266 }
267
268 if self.state.tick > 0 {
269 self.state.tick -= 1;
270 return;
271 } else {
272 self.state.tick = self.song.initial_speed.get();
273 }
274
275 match self.state.position.step_row(self.song) {
276 ControlFlow::Continue(_) => self.create_sample_players(),
277 ControlFlow::Break(_) => self.state.is_done = true,
278 }
279 }
280 fn create_sample_players(&mut self) {
281 for (position, event) in
282 &self.song.patterns[usize::from(self.state.position.pattern)][self.state.position.row]
283 {
284 if let Some((meta, ref sample)) = self.song.samples[usize::from(event.sample_instr)] {
285 let player =
286 SamplePlayer::new(sample.clone(), meta, self.state.samplerate, event.note);
287 self.state.voices[usize::from(position.channel)] = Some(player);
288 }
289 }
290 }
291}