torque_tracker_engine/
manager.rs1use std::{
2 fmt::Debug,
3 num::{NonZero, NonZeroU16},
4 time::Duration,
5};
6
7use simple_left_right::{WriteGuard, Writer};
8
9use crate::{
10 audio_processing::playback::PlaybackStatus,
11 live_audio::LiveAudio,
12 project::{
13 note_event::NoteEvent,
14 song::{Song, SongOperation, ValidOperation},
15 },
16 sample::Sample,
17};
18
19#[derive(Debug, Clone, Copy)]
20pub enum ToWorkerMsg {
21 Playback(PlaybackSettings),
22 StopPlayback,
23 PlayEvent(NoteEvent),
24 StopLiveNote,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[must_use]
29pub enum SendResult {
30 Success,
31 BufferFull,
32 AudioInactive,
33}
34
35impl SendResult {
36 #[track_caller]
37 pub fn unwrap(self) {
38 match self {
39 SendResult::Success => (),
40 SendResult::BufferFull => panic!("Buffer full"),
41 SendResult::AudioInactive => panic!("Audio inactive"),
42 }
43 }
44
45 pub fn is_success(self) -> bool {
46 self == Self::Success
47 }
48}
49
50#[derive(Debug)]
52struct ActiveStreamComms {
53 buffer_time: Duration,
54 send: rtrb::Producer<ToWorkerMsg>,
55 status: triple_buffer::Output<Option<PlaybackStatus>>,
56}
57
58#[derive(Debug, Default)]
59pub(crate) struct Collector {
60 samples: Vec<Sample>,
61}
62
63impl Collector {
64 pub fn add_sample(&mut self, sample: Sample) {
65 self.samples.push(sample);
66 }
67
68 fn collect(&mut self) {
69 self.samples.retain(|s| {
70 s.strongcount() != 1
72 });
73 }
74}
75
76pub struct AudioManager {
83 song: Writer<Song, ValidOperation>,
84 gc: Collector,
85 stream_comms: Option<ActiveStreamComms>,
86}
87
88impl AudioManager {
89 pub fn new(song: Song) -> Self {
90 let mut gc = Collector::default();
91 for (_, sample) in song.samples.iter().flatten() {
92 gc.add_sample(sample.clone());
93 }
94 let left_right = simple_left_right::Writer::new(song);
95
96 Self {
97 song: left_right,
98 gc,
99 stream_comms: None,
100 }
101 }
102
103 pub fn try_edit_song(&mut self) -> Option<SongEdit<'_>> {
106 self.song.try_lock().map(|song| SongEdit {
107 song,
108 gc: &mut self.gc,
109 })
110 }
111
112 pub fn get_song(&self) -> &Song {
113 self.song.read()
114 }
115
116 pub fn collect_garbage(&mut self) {
117 self.gc.collect();
118 }
119
120 pub fn try_msg_worker(&mut self, msg: ToWorkerMsg) -> SendResult {
121 if let Some(stream) = &mut self.stream_comms {
122 match stream.send.push(msg) {
123 Ok(_) => SendResult::Success,
124 Err(_) => SendResult::BufferFull,
125 }
126 } else {
127 SendResult::AudioInactive
128 }
129 }
130
131 pub fn playback_status(&mut self) -> Option<&Option<PlaybackStatus>> {
133 self.stream_comms.as_mut().map(|s| s.status.read())
134 }
135
136 pub fn buffer_time(&self) -> Option<Duration> {
142 self.stream_comms.as_ref().map(|s| s.buffer_time)
143 }
144
145 pub fn get_callback<Sample: dasp::sample::Sample + dasp::sample::FromSample<f32>>(
161 &mut self,
162 config: OutputConfig,
163 ) -> impl FnMut(&mut [Sample]) {
164 const TO_WORKER_CAPACITY: usize = 5;
165
166 assert!(self.stream_comms.is_none(), "Stream already active");
167 let from_worker = triple_buffer::triple_buffer(&None);
168 let to_worker = rtrb::RingBuffer::new(TO_WORKER_CAPACITY);
169 let reader = self.song.build_reader().unwrap();
170
171 let audio_worker = LiveAudio::new(reader, to_worker.1, from_worker.0, config);
172 let buffer_time =
173 Duration::from_millis((config.buffer_size * 1000 / config.sample_rate).into());
174
175 self.stream_comms = Some(ActiveStreamComms {
176 buffer_time,
177 send: to_worker.0,
178 status: from_worker.1,
179 });
180
181 audio_worker.get_typed_callback()
182 }
183
184 pub fn stream_closed(&mut self) {
186 self.stream_comms = None
187 }
188}
189
190impl Drop for AudioManager {
191 fn drop(&mut self) {
192 if let Some(stream) = &mut self.stream_comms {
194 eprintln!("AudioManager dropped while audio Stream still active.");
195 let msg1 = stream.send.push(ToWorkerMsg::StopLiveNote);
196 let msg2 = stream.send.push(ToWorkerMsg::StopPlayback);
197 if msg1.is_err() || msg2.is_err() {
198 eprintln!("Audio playback couldn't be stopped completely");
200 } else {
201 eprintln!("Audio playback was stopped");
202 }
203 }
204 }
205}
206
207#[derive(Debug)]
213pub struct SongEdit<'a> {
214 song: WriteGuard<'a, Song, ValidOperation>,
215 gc: &'a mut Collector,
216}
217
218impl SongEdit<'_> {
219 pub fn apply_operation(&mut self, op: SongOperation) -> Result<(), SongOperation> {
220 let valid_operation = ValidOperation::new(op, self.gc, self.song.read())?;
221 self.song.apply_op(valid_operation);
222 Ok(())
223 }
224
225 pub fn song(&self) -> &Song {
226 self.song.read()
227 }
228
229 pub fn finish(self) {}
232}
233
234#[derive(Debug, Clone, Copy)]
235pub struct OutputConfig {
236 pub buffer_size: u32,
237 pub channel_count: NonZeroU16,
238 pub sample_rate: NonZero<u32>,
239}
240
241#[derive(Debug, Clone, Copy)]
242pub enum PlaybackSettings {
243 Pattern { idx: u8, should_loop: bool },
244 Order { idx: u16, should_loop: bool },
245}