proteus_lib/playback/engine/
mod.rs1use dasp_ring_buffer::Bounded;
4use rodio::buffer::SamplesBuffer;
5use std::collections::HashMap;
6use std::sync::atomic::AtomicBool;
7use std::sync::atomic::AtomicU64;
8use std::sync::{mpsc::Receiver, Arc, Condvar, Mutex};
9
10use crate::audio::buffer::{init_buffer_map, TrackBuffer};
11use crate::container::prot::Prot;
12
13mod mix;
14mod premix;
15mod state;
16
17pub use state::{DspChainMetrics, PlaybackBufferSettings};
18
19use mix::{spawn_mix_thread, MixThreadArgs};
20
21#[derive(Debug, Clone)]
23pub struct InlineEffectsUpdate {
24 pub effects: Vec<crate::dsp::effects::AudioEffect>,
25 pub transition_ms: f32,
26}
27
28impl InlineEffectsUpdate {
29 pub fn new(effects: Vec<crate::dsp::effects::AudioEffect>, transition_ms: f32) -> Self {
31 Self {
32 effects,
33 transition_ms: transition_ms.max(0.0),
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy)]
40pub struct InlineTrackMixUpdate {
41 pub slot_index: usize,
42 pub level: f32,
43 pub pan: f32,
44}
45
46#[derive(Debug, Clone)]
48pub struct PlayerEngine {
49 pub finished_tracks: Arc<Mutex<Vec<u16>>>,
50 start_time: f64,
51 abort: Arc<AtomicBool>,
52 buffer_map: Arc<Mutex<HashMap<u16, TrackBuffer>>>,
53 buffer_notify: Arc<Condvar>,
54 effects_buffer: Arc<Mutex<Bounded<Vec<f32>>>>,
55 track_weights: Arc<Mutex<HashMap<u16, f32>>>,
56 track_channel_gains: Arc<Mutex<HashMap<u16, Vec<f32>>>>,
57 effects_reset: Arc<AtomicU64>,
58 inline_effects_update: Arc<Mutex<Option<InlineEffectsUpdate>>>,
59 inline_track_mix_updates: Arc<Mutex<Vec<InlineTrackMixUpdate>>>,
60 prot: Arc<Mutex<Prot>>,
61 buffer_settings: Arc<Mutex<PlaybackBufferSettings>>,
62 effects: Arc<Mutex<Vec<crate::dsp::effects::AudioEffect>>>,
63 dsp_metrics: Arc<Mutex<DspChainMetrics>>,
64}
65
66impl PlayerEngine {
67 pub fn new(
69 prot: Arc<Mutex<Prot>>,
70 abort_option: Option<Arc<AtomicBool>>,
71 start_time: f64,
72 buffer_settings: Arc<Mutex<PlaybackBufferSettings>>,
73 effects: Arc<Mutex<Vec<crate::dsp::effects::AudioEffect>>>,
74 dsp_metrics: Arc<Mutex<DspChainMetrics>>,
75 effects_reset: Arc<AtomicU64>,
76 inline_effects_update: Arc<Mutex<Option<InlineEffectsUpdate>>>,
77 inline_track_mix_updates: Arc<Mutex<Vec<InlineTrackMixUpdate>>>,
78 ) -> Self {
79 let buffer_map = init_buffer_map();
80 let buffer_notify = Arc::new(Condvar::new());
81 let track_weights = Arc::new(Mutex::new(HashMap::new()));
82 let track_channel_gains = Arc::new(Mutex::new(HashMap::new()));
83 let finished_tracks: Arc<Mutex<Vec<u16>>> = Arc::new(Mutex::new(Vec::new()));
84 let abort = if abort_option.is_some() {
85 abort_option.unwrap()
86 } else {
87 Arc::new(AtomicBool::new(false))
88 };
89
90 let prot_unlocked = prot.lock().unwrap();
91 let start_buffer_ms = buffer_settings.lock().unwrap().start_buffer_ms;
92 let channels = prot_unlocked.info.channels as usize;
93 let start_samples = ((prot_unlocked.info.sample_rate as f32 * start_buffer_ms) / 1000.0)
94 as usize
95 * channels;
96 let buffer_size = (prot_unlocked.info.sample_rate as usize * 10).max(start_samples * 2);
97 let effects_buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from(vec![
98 0.0;
99 buffer_size
100 ])));
101 drop(prot_unlocked);
102
103 Self {
104 finished_tracks,
105 start_time,
106 buffer_map,
107 buffer_notify,
108 effects_buffer,
109 track_weights,
110 track_channel_gains,
111 effects_reset,
112 inline_effects_update,
113 inline_track_mix_updates,
114 abort,
115 prot,
116 buffer_settings,
117 effects,
118 dsp_metrics,
119 }
120 }
121
122 pub fn reception_loop(&mut self, f: &dyn Fn((SamplesBuffer, f64))) {
124 let prot = self.prot.lock().unwrap();
125 let keys = prot.get_keys();
126 drop(prot);
127 self.ready_buffer_map(&keys);
128 let receiver = self.get_receiver();
129
130 for (mixer, length_in_seconds) in receiver {
131 f((mixer, length_in_seconds));
132 }
133 }
134
135 pub fn start_receiver(&mut self) -> Receiver<(SamplesBuffer, f64)> {
137 let prot = self.prot.lock().unwrap();
138 let keys = prot.get_keys();
139 drop(prot);
140 self.ready_buffer_map(&keys);
141 self.get_receiver()
142 }
143
144 fn get_receiver(&self) -> Receiver<(SamplesBuffer, f64)> {
145 let prot = self.prot.lock().unwrap();
146 let audio_info = prot.info.clone();
147 drop(prot);
148
149 spawn_mix_thread(MixThreadArgs {
150 audio_info,
151 buffer_map: self.buffer_map.clone(),
152 buffer_notify: self.buffer_notify.clone(),
153 effects_buffer: self.effects_buffer.clone(),
154 track_weights: self.track_weights.clone(),
155 track_channel_gains: self.track_channel_gains.clone(),
156 effects_reset: self.effects_reset.clone(),
157 inline_effects_update: self.inline_effects_update.clone(),
158 inline_track_mix_updates: self.inline_track_mix_updates.clone(),
159 finished_tracks: self.finished_tracks.clone(),
160 prot: self.prot.clone(),
161 abort: self.abort.clone(),
162 start_time: self.start_time,
163 buffer_settings: self.buffer_settings.clone(),
164 effects: self.effects.clone(),
165 dsp_metrics: self.dsp_metrics.clone(),
166 })
167 }
168
169 pub fn get_duration(&self) -> f64 {
171 let prot = self.prot.lock().unwrap();
172 *prot.get_duration()
173 }
174
175 fn ready_buffer_map(&mut self, keys: &Vec<u32>) {
176 self.buffer_map = init_buffer_map();
177 self.track_weights.lock().unwrap().clear();
178 self.track_channel_gains.lock().unwrap().clear();
179
180 let prot = self.prot.lock().unwrap();
181 let sample_rate = prot.info.sample_rate;
182 let channels = prot.info.channels as usize;
183 let track_mix_settings = prot.get_track_mix_settings();
184 let start_buffer_ms = self.buffer_settings.lock().unwrap().start_buffer_ms;
185 drop(prot);
186 let start_samples = ((sample_rate as f32 * start_buffer_ms) / 1000.0) as usize * channels;
187 let buffer_size = (sample_rate as usize * 10).max(start_samples * 2);
188
189 for key in keys {
190 let ring_buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from(vec![
191 0.0;
192 buffer_size
193 ])));
194 self.buffer_map
195 .lock()
196 .unwrap()
197 .insert(*key as u16, ring_buffer);
198 self.track_weights.lock().unwrap().insert(*key as u16, 1.0);
199 let (level, pan) = track_mix_settings
200 .get(&(*key as u16))
201 .copied()
202 .unwrap_or((1.0, 0.0));
203 let gains = compute_track_channel_gains(level, pan, channels);
204 self.track_channel_gains
205 .lock()
206 .unwrap()
207 .insert(*key as u16, gains);
208 }
209 }
210
211 pub fn finished_buffering(&self) -> bool {
213 let finished_tracks = self.finished_tracks.lock().unwrap();
214 let prot = self.prot.lock().unwrap();
215 let keys = prot.get_keys();
216 drop(prot);
217
218 for key in keys {
219 if !finished_tracks.contains(&(key as u16)) {
220 return false;
221 }
222 }
223
224 true
225 }
226}
227
228pub(crate) fn compute_track_channel_gains(level: f32, pan: f32, channels: usize) -> Vec<f32> {
229 let level = level.max(0.0);
230 if channels <= 1 {
231 return vec![level];
232 }
233
234 let pan = pan.clamp(-1.0, 1.0);
235 let left = if pan > 0.0 { 1.0 - pan } else { 1.0 };
236 let right = if pan < 0.0 { 1.0 + pan } else { 1.0 };
237
238 let mut gains = vec![level; channels];
239 gains[0] = level * left;
240 gains[1] = level * right;
241 gains
242}
243
244#[cfg(test)]
245mod tests {
246 use super::compute_track_channel_gains;
247
248 #[test]
249 fn channel_gains_apply_level_and_pan() {
250 let gains = compute_track_channel_gains(0.5, 0.5, 2);
251 assert_eq!(gains.len(), 2);
252 assert!((gains[0] - 0.25).abs() < 1e-6);
253 assert!((gains[1] - 0.5).abs() < 1e-6);
254 }
255
256 #[test]
257 fn mono_gain_uses_level_only() {
258 let gains = compute_track_channel_gains(0.8, -1.0, 1);
259 assert_eq!(gains, vec![0.8]);
260 }
261}