ym2149_common/
cached_player.rs1use crate::{ChiptunePlayerBase, PlaybackState};
18
19pub const DEFAULT_CACHE_SIZE: usize = 512;
21
22#[derive(Clone)]
55pub struct SampleCache {
56 samples: Vec<f32>,
57 channel_outputs: Vec<[f32; 3]>,
58 pos: usize,
59 len: usize,
60 size: usize,
61}
62
63impl SampleCache {
64 #[must_use]
66 pub fn new(size: usize) -> Self {
67 Self {
68 samples: vec![0.0; size],
69 channel_outputs: vec![[0.0; 3]; size],
70 pos: 0,
71 len: 0,
72 size,
73 }
74 }
75
76 #[inline]
78 #[must_use]
79 pub fn needs_refill(&self) -> bool {
80 self.pos >= self.len
81 }
82
83 pub fn sample_buffer_mut(&mut self) -> &mut [f32] {
85 &mut self.samples[..self.size]
86 }
87
88 pub fn fill_channel_outputs(&mut self, outputs: [f32; 3]) {
93 self.channel_outputs[..self.size].fill(outputs);
94 }
95
96 pub fn mark_filled(&mut self) {
98 self.pos = 0;
99 self.len = self.size;
100 }
101
102 pub fn next_sample(&mut self) -> f32 {
106 if self.len == 0 {
107 return 0.0;
108 }
109 let sample = self.samples[self.pos];
110 self.pos += 1;
111 sample
112 }
113
114 #[must_use]
116 pub fn channel_outputs(&self) -> [f32; 3] {
117 if self.pos > 0 && self.pos <= self.len {
118 self.channel_outputs[self.pos - 1]
119 } else if !self.channel_outputs.is_empty() {
120 self.channel_outputs[0]
121 } else {
122 [0.0, 0.0, 0.0]
123 }
124 }
125
126 pub fn reset(&mut self) {
128 self.pos = 0;
129 self.len = 0;
130 }
131
132 #[must_use]
134 pub fn size(&self) -> usize {
135 self.size
136 }
137}
138
139impl Default for SampleCache {
140 fn default() -> Self {
141 Self::new(DEFAULT_CACHE_SIZE)
142 }
143}
144
145pub trait CacheablePlayer: ChiptunePlayerBase {
154 fn get_channel_outputs(&self) -> [f32; 3];
158
159 fn on_cache_refill(&mut self) {}
163}
164
165pub struct CachedPlayer<P: CacheablePlayer> {
183 player: P,
184 cache: SampleCache,
185}
186
187impl<P: CacheablePlayer> CachedPlayer<P> {
188 pub fn new(player: P, cache_size: usize) -> Self {
190 Self {
191 player,
192 cache: SampleCache::new(cache_size),
193 }
194 }
195
196 pub fn with_default_cache(player: P) -> Self {
198 Self::new(player, DEFAULT_CACHE_SIZE)
199 }
200
201 pub fn inner(&self) -> &P {
203 &self.player
204 }
205
206 pub fn inner_mut(&mut self) -> &mut P {
208 &mut self.player
209 }
210
211 pub fn into_inner(self) -> P {
213 self.player
214 }
215
216 pub fn generate_sample(&mut self) -> f32 {
220 if self.cache.needs_refill() {
221 self.refill_cache();
222 }
223 self.cache.next_sample()
224 }
225
226 pub fn cached_channel_outputs(&self) -> [f32; 3] {
232 self.cache.channel_outputs()
233 }
234
235 pub fn reset_cache(&mut self) {
237 self.cache.reset();
238 }
239
240 fn refill_cache(&mut self) {
242 self.player.on_cache_refill();
243 self.player
244 .generate_samples_into(self.cache.sample_buffer_mut());
245 self.cache
246 .fill_channel_outputs(self.player.get_channel_outputs());
247 self.cache.mark_filled();
248 }
249}
250
251impl<P: CacheablePlayer> ChiptunePlayerBase for CachedPlayer<P> {
253 fn play(&mut self) {
254 self.player.play();
255 }
256
257 fn pause(&mut self) {
258 self.player.pause();
259 }
260
261 fn stop(&mut self) {
262 self.player.stop();
263 self.reset_cache();
264 }
265
266 fn state(&self) -> PlaybackState {
267 self.player.state()
268 }
269
270 fn generate_samples_into(&mut self, buffer: &mut [f32]) {
271 self.player.generate_samples_into(buffer);
273 }
274
275 fn sample_rate(&self) -> u32 {
276 self.player.sample_rate()
277 }
278
279 fn set_channel_mute(&mut self, channel: usize, mute: bool) {
280 self.player.set_channel_mute(channel, mute);
281 }
282
283 fn is_channel_muted(&self, channel: usize) -> bool {
284 self.player.is_channel_muted(channel)
285 }
286
287 fn playback_position(&self) -> f32 {
288 self.player.playback_position()
289 }
290
291 fn subsong_count(&self) -> usize {
292 self.player.subsong_count()
293 }
294
295 fn current_subsong(&self) -> usize {
296 self.player.current_subsong()
297 }
298
299 fn set_subsong(&mut self, index: usize) -> bool {
300 if self.player.set_subsong(index) {
301 self.reset_cache();
302 true
303 } else {
304 false
305 }
306 }
307
308 fn psg_count(&self) -> usize {
309 self.player.psg_count()
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_sample_cache_basic() {
319 let mut cache = SampleCache::new(4);
320 assert!(cache.needs_refill());
321
322 cache
324 .sample_buffer_mut()
325 .copy_from_slice(&[1.0, 2.0, 3.0, 4.0]);
326 cache.fill_channel_outputs([0.1, 0.2, 0.3]);
327 cache.mark_filled();
328
329 assert!(!cache.needs_refill());
330 assert_eq!(cache.next_sample(), 1.0);
331 assert_eq!(cache.next_sample(), 2.0);
332 assert_eq!(cache.channel_outputs(), [0.1, 0.2, 0.3]);
333 }
334
335 #[test]
336 fn test_sample_cache_reset() {
337 let mut cache = SampleCache::new(4);
338 cache
339 .sample_buffer_mut()
340 .copy_from_slice(&[1.0, 2.0, 3.0, 4.0]);
341 cache.mark_filled();
342
343 let _ = cache.next_sample();
344 cache.reset();
345
346 assert!(cache.needs_refill());
347 }
348
349 struct MockPlayer {
351 samples_generated: usize,
352 state: PlaybackState,
353 }
354
355 impl MockPlayer {
356 fn new() -> Self {
357 Self {
358 samples_generated: 0,
359 state: PlaybackState::Playing,
360 }
361 }
362 }
363
364 impl ChiptunePlayerBase for MockPlayer {
365 fn play(&mut self) {
366 self.state = PlaybackState::Playing;
367 }
368
369 fn pause(&mut self) {
370 self.state = PlaybackState::Paused;
371 }
372
373 fn stop(&mut self) {
374 self.state = PlaybackState::Stopped;
375 }
376
377 fn state(&self) -> PlaybackState {
378 self.state
379 }
380
381 fn generate_samples_into(&mut self, buffer: &mut [f32]) {
382 for (i, sample) in buffer.iter_mut().enumerate() {
383 *sample = (self.samples_generated + i) as f32 * 0.001;
384 }
385 self.samples_generated += buffer.len();
386 }
387 }
388
389 impl CacheablePlayer for MockPlayer {
390 fn get_channel_outputs(&self) -> [f32; 3] {
391 [0.1, 0.2, 0.3]
392 }
393 }
394
395 #[test]
396 fn test_cached_player_generates_samples() {
397 let player = MockPlayer::new();
398 let mut cached = CachedPlayer::new(player, 16);
399
400 let s1 = cached.generate_sample();
401 let s2 = cached.generate_sample();
402
403 assert!((s1 - 0.0).abs() < 0.0001);
404 assert!((s2 - 0.001).abs() < 0.0001);
405 }
406
407 #[test]
408 fn test_cached_player_channel_outputs() {
409 let player = MockPlayer::new();
410 let mut cached = CachedPlayer::new(player, 16);
411
412 let _ = cached.generate_sample();
413 let channels = cached.cached_channel_outputs();
414
415 assert_eq!(channels, [0.1, 0.2, 0.3]);
416 }
417
418 #[test]
419 fn test_cache_reset_on_stop() {
420 let player = MockPlayer::new();
421 let mut cached = CachedPlayer::new(player, 16);
422
423 let _ = cached.generate_sample();
424 cached.stop();
425
426 assert!(cached.cache.needs_refill());
428 }
429}