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 #[inline]
106 pub fn next_sample(&mut self) -> f32 {
107 if self.len == 0 {
108 return 0.0;
109 }
110 let sample = self.samples[self.pos];
111 self.pos += 1;
112 sample
113 }
114
115 #[must_use]
117 pub fn channel_outputs(&self) -> [f32; 3] {
118 if self.pos > 0 && self.pos <= self.len {
119 self.channel_outputs[self.pos - 1]
120 } else if !self.channel_outputs.is_empty() {
121 self.channel_outputs[0]
122 } else {
123 [0.0, 0.0, 0.0]
124 }
125 }
126
127 pub fn reset(&mut self) {
129 self.pos = 0;
130 self.len = 0;
131 }
132
133 #[must_use]
135 pub fn size(&self) -> usize {
136 self.size
137 }
138}
139
140impl Default for SampleCache {
141 fn default() -> Self {
142 Self::new(DEFAULT_CACHE_SIZE)
143 }
144}
145
146pub trait CacheablePlayer: ChiptunePlayerBase {
155 fn get_channel_outputs(&self) -> [f32; 3];
159
160 fn on_cache_refill(&mut self) {}
164}
165
166pub struct CachedPlayer<P: CacheablePlayer> {
184 player: P,
185 cache: SampleCache,
186}
187
188impl<P: CacheablePlayer> CachedPlayer<P> {
189 pub fn new(player: P, cache_size: usize) -> Self {
191 Self {
192 player,
193 cache: SampleCache::new(cache_size),
194 }
195 }
196
197 pub fn with_default_cache(player: P) -> Self {
199 Self::new(player, DEFAULT_CACHE_SIZE)
200 }
201
202 pub fn inner(&self) -> &P {
204 &self.player
205 }
206
207 pub fn inner_mut(&mut self) -> &mut P {
209 &mut self.player
210 }
211
212 pub fn into_inner(self) -> P {
214 self.player
215 }
216
217 pub fn generate_sample(&mut self) -> f32 {
221 if self.cache.needs_refill() {
222 self.refill_cache();
223 }
224 self.cache.next_sample()
225 }
226
227 pub fn cached_channel_outputs(&self) -> [f32; 3] {
233 self.cache.channel_outputs()
234 }
235
236 pub fn reset_cache(&mut self) {
238 self.cache.reset();
239 }
240
241 fn refill_cache(&mut self) {
243 self.player.on_cache_refill();
244 self.player
245 .generate_samples_into(self.cache.sample_buffer_mut());
246 self.cache
247 .fill_channel_outputs(self.player.get_channel_outputs());
248 self.cache.mark_filled();
249 }
250}
251
252impl<P: CacheablePlayer> ChiptunePlayerBase for CachedPlayer<P> {
254 fn play(&mut self) {
255 self.player.play();
256 }
257
258 fn pause(&mut self) {
259 self.player.pause();
260 }
261
262 fn stop(&mut self) {
263 self.player.stop();
264 self.reset_cache();
265 }
266
267 fn state(&self) -> PlaybackState {
268 self.player.state()
269 }
270
271 fn generate_samples_into(&mut self, buffer: &mut [f32]) {
272 self.player.generate_samples_into(buffer);
274 }
275
276 fn sample_rate(&self) -> u32 {
277 self.player.sample_rate()
278 }
279
280 fn set_channel_mute(&mut self, channel: usize, mute: bool) {
281 self.player.set_channel_mute(channel, mute);
282 }
283
284 fn is_channel_muted(&self, channel: usize) -> bool {
285 self.player.is_channel_muted(channel)
286 }
287
288 fn playback_position(&self) -> f32 {
289 self.player.playback_position()
290 }
291
292 fn subsong_count(&self) -> usize {
293 self.player.subsong_count()
294 }
295
296 fn current_subsong(&self) -> usize {
297 self.player.current_subsong()
298 }
299
300 fn set_subsong(&mut self, index: usize) -> bool {
301 if self.player.set_subsong(index) {
302 self.reset_cache();
303 true
304 } else {
305 false
306 }
307 }
308
309 fn psg_count(&self) -> usize {
310 self.player.psg_count()
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn test_sample_cache_basic() {
320 let mut cache = SampleCache::new(4);
321 assert!(cache.needs_refill());
322
323 cache
325 .sample_buffer_mut()
326 .copy_from_slice(&[1.0, 2.0, 3.0, 4.0]);
327 cache.fill_channel_outputs([0.1, 0.2, 0.3]);
328 cache.mark_filled();
329
330 assert!(!cache.needs_refill());
331 assert_eq!(cache.next_sample(), 1.0);
332 assert_eq!(cache.next_sample(), 2.0);
333 assert_eq!(cache.channel_outputs(), [0.1, 0.2, 0.3]);
334 }
335
336 #[test]
337 fn test_sample_cache_reset() {
338 let mut cache = SampleCache::new(4);
339 cache
340 .sample_buffer_mut()
341 .copy_from_slice(&[1.0, 2.0, 3.0, 4.0]);
342 cache.mark_filled();
343
344 let _ = cache.next_sample();
345 cache.reset();
346
347 assert!(cache.needs_refill());
348 }
349
350 struct MockPlayer {
352 samples_generated: usize,
353 state: PlaybackState,
354 }
355
356 impl MockPlayer {
357 fn new() -> Self {
358 Self {
359 samples_generated: 0,
360 state: PlaybackState::Playing,
361 }
362 }
363 }
364
365 impl ChiptunePlayerBase for MockPlayer {
366 fn play(&mut self) {
367 self.state = PlaybackState::Playing;
368 }
369
370 fn pause(&mut self) {
371 self.state = PlaybackState::Paused;
372 }
373
374 fn stop(&mut self) {
375 self.state = PlaybackState::Stopped;
376 }
377
378 fn state(&self) -> PlaybackState {
379 self.state
380 }
381
382 fn generate_samples_into(&mut self, buffer: &mut [f32]) {
383 for (i, sample) in buffer.iter_mut().enumerate() {
384 *sample = (self.samples_generated + i) as f32 * 0.001;
385 }
386 self.samples_generated += buffer.len();
387 }
388 }
389
390 impl CacheablePlayer for MockPlayer {
391 fn get_channel_outputs(&self) -> [f32; 3] {
392 [0.1, 0.2, 0.3]
393 }
394 }
395
396 #[test]
397 fn test_cached_player_generates_samples() {
398 let player = MockPlayer::new();
399 let mut cached = CachedPlayer::new(player, 16);
400
401 let s1 = cached.generate_sample();
402 let s2 = cached.generate_sample();
403
404 assert!((s1 - 0.0).abs() < 0.0001);
405 assert!((s2 - 0.001).abs() < 0.0001);
406 }
407
408 #[test]
409 fn test_cached_player_channel_outputs() {
410 let player = MockPlayer::new();
411 let mut cached = CachedPlayer::new(player, 16);
412
413 let _ = cached.generate_sample();
414 let channels = cached.cached_channel_outputs();
415
416 assert_eq!(channels, [0.1, 0.2, 0.3]);
417 }
418
419 #[test]
420 fn test_cache_reset_on_stop() {
421 let player = MockPlayer::new();
422 let mut cached = CachedPlayer::new(player, 16);
423
424 let _ = cached.generate_sample();
425 cached.stop();
426
427 assert!(cached.cache.needs_refill());
429 }
430}