use crate::zx::{
constants::FPS,
sound::{
beeper::ZXBeeper,
sample::{SampleGenerator, SoundSample},
},
};
#[cfg(feature = "ay")]
use crate::zx::sound::ay::{ZXAYMode, ZXAyChip};
use alloc::collections::VecDeque;
pub(crate) struct ZXMixer {
pub beeper: ZXBeeper,
#[cfg(feature = "ay")]
pub ay: ZXAyChip,
ring_buffer: VecDeque<SoundSample<f32>>,
last_pos: usize,
last_sample: SoundSample<f32>,
master_volume: f64,
#[cfg(feature = "ay")]
use_ay: bool,
use_beeper: bool,
sample_rate: usize,
}
impl ZXMixer {
pub fn new(
use_beeper: bool,
#[cfg(feature = "ay")] use_ay: bool,
#[cfg(feature = "ay")] ay_mode: ZXAYMode,
sample_rate: usize,
) -> ZXMixer {
ZXMixer {
beeper: ZXBeeper::default(),
#[cfg(feature = "ay")]
ay: ZXAyChip::new(sample_rate, ay_mode),
ring_buffer: VecDeque::with_capacity(sample_rate),
last_pos: 0,
last_sample: SoundSample::new(0.0, 0.0),
master_volume: 0.5,
#[cfg(feature = "ay")]
use_ay,
use_beeper,
sample_rate,
}
}
pub fn volume(&mut self, volume: f64) {
self.master_volume = volume;
}
pub fn process(&mut self, current_time: f64) {
if self.ring_buffer.len() >= self.samples_per_frame() {
return;
}
let curr_pos = self.sample_count_for_frame_fraction(current_time);
if curr_pos <= self.last_pos {
return;
}
let sample_count = curr_pos - self.last_pos;
self.last_pos = curr_pos;
for _ in 0..sample_count {
let sample = self.gen_sample();
self.ring_buffer.push_back(sample);
}
}
pub fn new_frame(&mut self) {
if self.ring_buffer.len() < self.samples_per_frame() {
for _ in self.ring_buffer.len()..self.samples_per_frame() {
self.ring_buffer.push_back(self.last_sample);
}
}
self.last_pos = 0;
}
pub fn pop(&mut self) -> Option<SoundSample<f32>> {
self.ring_buffer.pop_front()
}
fn gen_sample(&mut self) -> SoundSample<f32> {
let mut master_float = if self.use_beeper {
self.beeper.gen_sample()
} else {
SoundSample::new(0.0, 0.0)
};
#[cfg(feature = "ay")]
if self.use_ay {
master_float.mix(&self.ay.gen_sample());
}
let master = master_float.mul_eq(self.master_volume).into_f32();
self.last_sample = master;
master
}
fn samples_per_frame(&self) -> usize {
self.sample_rate / FPS
}
fn sample_count_for_frame_fraction(&self, fraction: f64) -> usize {
if fraction >= 1f64 {
return self.samples_per_frame();
}
(self.samples_per_frame() as f64 * fraction) as usize
}
}