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 state;
15
16pub use state::{DspChainMetrics, PlaybackBufferSettings};
17
18use mix::{spawn_mix_thread, MixThreadArgs};
19
20#[derive(Debug, Clone)]
22pub struct PlayerEngine {
23 pub finished_tracks: Arc<Mutex<Vec<u16>>>,
24 start_time: f64,
25 abort: Arc<AtomicBool>,
26 buffer_map: Arc<Mutex<HashMap<u16, TrackBuffer>>>,
27 buffer_notify: Arc<Condvar>,
28 effects_buffer: Arc<Mutex<Bounded<Vec<f32>>>>,
29 track_weights: Arc<Mutex<HashMap<u16, f32>>>,
30 track_channel_gains: Arc<Mutex<HashMap<u16, Vec<f32>>>>,
31 effects_reset: Arc<AtomicU64>,
32 prot: Arc<Mutex<Prot>>,
33 buffer_settings: Arc<Mutex<PlaybackBufferSettings>>,
34 effects: Arc<Mutex<Vec<crate::dsp::effects::AudioEffect>>>,
35 dsp_metrics: Arc<Mutex<DspChainMetrics>>,
36}
37
38impl PlayerEngine {
39 pub fn new(
41 prot: Arc<Mutex<Prot>>,
42 abort_option: Option<Arc<AtomicBool>>,
43 start_time: f64,
44 buffer_settings: Arc<Mutex<PlaybackBufferSettings>>,
45 effects: Arc<Mutex<Vec<crate::dsp::effects::AudioEffect>>>,
46 dsp_metrics: Arc<Mutex<DspChainMetrics>>,
47 effects_reset: Arc<AtomicU64>,
48 ) -> Self {
49 let buffer_map = init_buffer_map();
50 let buffer_notify = Arc::new(Condvar::new());
51 let track_weights = Arc::new(Mutex::new(HashMap::new()));
52 let track_channel_gains = Arc::new(Mutex::new(HashMap::new()));
53 let finished_tracks: Arc<Mutex<Vec<u16>>> = Arc::new(Mutex::new(Vec::new()));
54 let abort = if abort_option.is_some() {
55 abort_option.unwrap()
56 } else {
57 Arc::new(AtomicBool::new(false))
58 };
59
60 let prot_unlocked = prot.lock().unwrap();
61 let start_buffer_ms = buffer_settings.lock().unwrap().start_buffer_ms;
62 let channels = prot_unlocked.info.channels as usize;
63 let start_samples = ((prot_unlocked.info.sample_rate as f32 * start_buffer_ms) / 1000.0)
64 as usize
65 * channels;
66 let buffer_size = (prot_unlocked.info.sample_rate as usize * 10).max(start_samples * 2);
67 let effects_buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from(vec![
68 0.0;
69 buffer_size
70 ])));
71 drop(prot_unlocked);
72
73 Self {
74 finished_tracks,
75 start_time,
76 buffer_map,
77 buffer_notify,
78 effects_buffer,
79 track_weights,
80 track_channel_gains,
81 effects_reset,
82 abort,
83 prot,
84 buffer_settings,
85 effects,
86 dsp_metrics,
87 }
88 }
89
90 pub fn reception_loop(&mut self, f: &dyn Fn((SamplesBuffer, f64))) {
92 let prot = self.prot.lock().unwrap();
93 let keys = prot.get_keys();
94 drop(prot);
95 self.ready_buffer_map(&keys);
96 let receiver = self.get_receiver();
97
98 for (mixer, length_in_seconds) in receiver {
99 f((mixer, length_in_seconds));
100 }
101 }
102
103 pub fn start_receiver(&mut self) -> Receiver<(SamplesBuffer, f64)> {
105 let prot = self.prot.lock().unwrap();
106 let keys = prot.get_keys();
107 drop(prot);
108 self.ready_buffer_map(&keys);
109 self.get_receiver()
110 }
111
112 fn get_receiver(&self) -> Receiver<(SamplesBuffer, f64)> {
113 let prot = self.prot.lock().unwrap();
114 let audio_info = prot.info.clone();
115 drop(prot);
116
117 spawn_mix_thread(MixThreadArgs {
118 audio_info,
119 buffer_map: self.buffer_map.clone(),
120 buffer_notify: self.buffer_notify.clone(),
121 effects_buffer: self.effects_buffer.clone(),
122 track_weights: self.track_weights.clone(),
123 track_channel_gains: self.track_channel_gains.clone(),
124 effects_reset: self.effects_reset.clone(),
125 finished_tracks: self.finished_tracks.clone(),
126 prot: self.prot.clone(),
127 abort: self.abort.clone(),
128 start_time: self.start_time,
129 buffer_settings: self.buffer_settings.clone(),
130 effects: self.effects.clone(),
131 dsp_metrics: self.dsp_metrics.clone(),
132 })
133 }
134
135 pub fn get_duration(&self) -> f64 {
137 let prot = self.prot.lock().unwrap();
138 *prot.get_duration()
139 }
140
141 fn ready_buffer_map(&mut self, keys: &Vec<u32>) {
142 self.buffer_map = init_buffer_map();
143 self.track_weights.lock().unwrap().clear();
144 self.track_channel_gains.lock().unwrap().clear();
145
146 let prot = self.prot.lock().unwrap();
147 let sample_rate = prot.info.sample_rate;
148 let channels = prot.info.channels as usize;
149 let track_mix_settings = prot.get_track_mix_settings();
150 let start_buffer_ms = self.buffer_settings.lock().unwrap().start_buffer_ms;
151 drop(prot);
152 let start_samples = ((sample_rate as f32 * start_buffer_ms) / 1000.0) as usize * channels;
153 let buffer_size = (sample_rate as usize * 10).max(start_samples * 2);
154
155 for key in keys {
156 let ring_buffer = Arc::new(Mutex::new(dasp_ring_buffer::Bounded::from(vec![
157 0.0;
158 buffer_size
159 ])));
160 self.buffer_map
161 .lock()
162 .unwrap()
163 .insert(*key as u16, ring_buffer);
164 self.track_weights.lock().unwrap().insert(*key as u16, 1.0);
165 let (level, pan) = track_mix_settings
166 .get(&(*key as u16))
167 .copied()
168 .unwrap_or((1.0, 0.0));
169 let gains = compute_track_channel_gains(level, pan, channels);
170 self.track_channel_gains
171 .lock()
172 .unwrap()
173 .insert(*key as u16, gains);
174 }
175 }
176
177 pub fn finished_buffering(&self) -> bool {
179 let finished_tracks = self.finished_tracks.lock().unwrap();
180 let prot = self.prot.lock().unwrap();
181 let keys = prot.get_keys();
182 drop(prot);
183
184 for key in keys {
185 if !finished_tracks.contains(&(key as u16)) {
186 return false;
187 }
188 }
189
190 true
191 }
192}
193
194fn compute_track_channel_gains(level: f32, pan: f32, channels: usize) -> Vec<f32> {
195 let level = level.max(0.0);
196 if channels <= 1 {
197 return vec![level];
198 }
199
200 let pan = pan.clamp(-1.0, 1.0);
201 let left = if pan > 0.0 { 1.0 - pan } else { 1.0 };
202 let right = if pan < 0.0 { 1.0 + pan } else { 1.0 };
203
204 let mut gains = vec![level; channels];
205 gains[0] = level * left;
206 gains[1] = level * right;
207 gains
208}