#![allow(missing_docs)]
use super::*;
#[derive(Debug, Clone, Copy)]
pub struct FrequencySweep {
pub frequency_setting: u16,
pub rate: i8,
pub shift: u8,
pub samples: usize,
}
impl Default for FrequencySweep {
fn default() -> Self {
Self::new(0, 0, 0, 1)
}
}
impl FrequencySweep {
pub fn setting_for(target: i32) -> u16 {
2048 - ((131_072 / target.max(64)) as u16).max(1)
}
pub fn samples_until_trigger(rate: i8, samples_per_second: i32) -> usize {
debug_assert!(samples_per_second > 0);
debug_assert!(rate < 8 && rate > -8);
if rate != 0 {
((samples_per_second / 128) * i32::from(rate.abs())) as usize
} else {
core::usize::MAX
}
}
pub fn new(freq: i32, rate: i8, shift: u8, samples_per_second: i32) -> Self {
debug_assert!(samples_per_second > 0);
debug_assert!(rate < 8 && rate > -8);
debug_assert!(shift < 8);
Self {
frequency_setting: Self::setting_for(freq),
rate,
shift,
samples: Self::samples_until_trigger(rate, samples_per_second),
}
}
pub fn frequency(&self) -> i32 {
debug_assert!(self.frequency_setting < 2048);
131_072 / (2048 - i32::from(self.frequency_setting))
}
pub fn in_samples_per_out_sample(&self, samples_per_second: i32) -> f32 {
(32.0 * self.frequency() as f32) / samples_per_second as f32
}
pub fn tick_sample(&mut self, samples_per_second: i32) -> SweepTickResult {
debug_assert!(self.samples > 0);
self.samples -= 1;
let out = if self.samples == 0 {
let sweep_dir: i32 = i32::from(self.rate.signum());
let t_0: i32 = i32::from(self.frequency_setting);
let t_next: i32 = t_0 + (sweep_dir * (t_0 / (2 << self.shift)).max(1));
if t_next > 0 && t_next < 0b111_1111_1111 {
self.frequency_setting = t_next as u16;
self.samples = Self::samples_until_trigger(self.rate, samples_per_second);
SweepTickResult::FrequencyChange
} else {
self.samples = core::usize::MAX;
SweepTickResult::CancelSound
}
} else {
SweepTickResult::NoChange
};
debug_assert!(self.samples > 0);
out
}
}
#[derive(Debug, Clone, Copy)]
pub enum SweepTickResult {
NoChange,
FrequencyChange,
CancelSound,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct LevelEnvelope {
pub rate: i8,
pub level: f32,
pub samples: usize,
}
impl LevelEnvelope {
pub fn samples_until_trigger(rate: i8, samples_per_second: i32) -> usize {
debug_assert!(samples_per_second > 0);
if rate != 0 {
((samples_per_second / 64) * i32::from(rate.abs())) as usize
} else {
core::usize::MAX
}
}
pub fn new(rate: i8, level: f32, samples_per_second: i32) -> Self {
debug_assert!(rate < 8 && rate > -8);
debug_assert!(level >= 0.0 && level <= 1.0);
debug_assert!(samples_per_second > 0);
Self {
rate,
level,
samples: Self::samples_until_trigger(rate, samples_per_second),
}
}
pub fn tick_sample(&mut self, samples_per_second: i32) {
debug_assert!(self.samples > 0);
self.samples -= 1;
if self.samples == 0 {
const ONE_FIFTEENTH: f32 = 1.0 / 15.0;
let delta = f32::from(self.rate.signum()) * ONE_FIFTEENTH;
self.level = self.level + delta;
if self.level > 1.0 {
self.level = 1.0;
self.samples = core::usize::MAX;
} else if self.level < 0.0 {
self.level = 0.0;
self.samples = core::usize::MAX;
} else {
self.samples = Self::samples_until_trigger(self.rate, samples_per_second);
}
}
debug_assert!(self.samples > 0);
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct PulseVoice {
pub play_samples_remaining: usize,
pub sweep: FrequencySweep,
pub envelope: LevelEnvelope,
pub duty: f32,
pub wave_point: f32,
}
impl PulseVoice {
pub fn fill_sound_buffer(
&mut self, samples_per_second: i32, buf: &mut [StereoI16], left_max: i16, right_max: i16,
) -> usize {
debug_assert!(self.wave_point < 32.0);
let left_max_f = f32::from(left_max);
let right_max_f = f32::from(right_max);
let mut samples_written: usize = 0;
let mut in_samples_per_out_sample = self.sweep.in_samples_per_out_sample(samples_per_second);
let mut buf_iter = buf.iter_mut();
let duty_point = self.duty * 32.0;
while self.play_samples_remaining > 0 {
self.play_samples_remaining -= 1;
if let Some(sample_mut) = buf_iter.next() {
let here_level_norm: f32 = if in_samples_per_out_sample > 1.0 {
let mut total = 0.0;
let count = in_samples_per_out_sample as u32;
for _ in 0..count {
self.wave_point += 1.0;
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
total += if self.wave_point <= duty_point {
self.envelope.level
} else {
0.0
};
}
self.wave_point += in_samples_per_out_sample.fract();
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
total += if self.wave_point <= duty_point {
self.envelope.level * in_samples_per_out_sample.fract()
} else {
0.0
};
total / in_samples_per_out_sample
} else {
self.wave_point += in_samples_per_out_sample;
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
if self.wave_point <= duty_point {
self.envelope.level
} else {
0.0
}
};
debug_assert!(here_level_norm >= 0.0 && here_level_norm <= 1.0);
let here_pos_neg_norm = (here_level_norm * 2.0) - 1.0;
sample_mut.left += (left_max_f * here_pos_neg_norm) as i16;
sample_mut.right += (right_max_f * here_pos_neg_norm) as i16;
samples_written += 1;
match self.sweep.tick_sample(samples_per_second) {
SweepTickResult::NoChange => {}
SweepTickResult::FrequencyChange => {
in_samples_per_out_sample = self.sweep.in_samples_per_out_sample(samples_per_second);
}
SweepTickResult::CancelSound => {
self.play_samples_remaining = 0;
}
}
self.envelope.tick_sample(samples_per_second);
} else {
break;
}
}
samples_written
}
}
#[derive(Debug, Clone)]
pub struct WaveVoice {
pub play_samples_remaining: usize,
pub sweep: FrequencySweep,
pub wave: Vec<f32>,
pub wave_point: f32,
}
impl Default for WaveVoice {
fn default() -> Self {
Self {
play_samples_remaining: 0,
sweep: Default::default(),
wave: vec![0.0; 32],
wave_point: 0.0,
}
}
}
impl WaveVoice {
pub fn fill_sound_buffer(
&mut self, samples_per_second: i32, buf: &mut [StereoI16], left_max: i16, right_max: i16,
) -> usize {
debug_assert!(self.wave_point < 32.0);
debug_assert_eq!(self.wave.len(), 32);
let left_max_f = f32::from(left_max);
let right_max_f = f32::from(right_max);
let mut samples_written: usize = 0;
let mut in_samples_per_out_sample = self.sweep.in_samples_per_out_sample(samples_per_second);
let mut buf_iter = buf.iter_mut();
while self.play_samples_remaining > 0 {
self.play_samples_remaining -= 1;
if let Some(sample_mut) = buf_iter.next() {
let here_level_norm: f32 = if in_samples_per_out_sample > 1.0 {
let mut total = 0.0;
let count = in_samples_per_out_sample as u32;
for _ in 0..count {
self.wave_point += 1.0;
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
total += self.wave[self.wave_point as usize];
}
self.wave_point += in_samples_per_out_sample.fract();
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
total += self.wave[self.wave_point as usize];
total / in_samples_per_out_sample
} else {
self.wave_point += in_samples_per_out_sample;
if self.wave_point >= 32.0 {
self.wave_point -= 32.0;
}
self.wave[self.wave_point as usize]
};
debug_assert!(here_level_norm >= 0.0 && here_level_norm <= 1.0);
let here_pos_neg_norm = (here_level_norm * 2.0) - 1.0;
sample_mut.left += (left_max_f * here_pos_neg_norm) as i16;
sample_mut.right += (right_max_f * here_pos_neg_norm) as i16;
samples_written += 1;
match self.sweep.tick_sample(samples_per_second) {
SweepTickResult::NoChange => {}
SweepTickResult::FrequencyChange => {
in_samples_per_out_sample = self.sweep.in_samples_per_out_sample(samples_per_second);
}
SweepTickResult::CancelSound => {
self.play_samples_remaining = 0;
}
}
} else {
break;
}
}
samples_written
}
}
#[derive(Debug, Clone, Copy)]
pub struct NoiseCounter {
pub counter: u16,
pub is_7bit: bool,
}
impl Default for NoiseCounter {
fn default() -> Self {
Self {
counter: core::u16::MAX,
is_7bit: false,
}
}
}
impl NoiseCounter {
pub fn tick(&mut self) {
let a = self.counter & 0b1;
let b = (self.counter & 0b10) >> 1;
let new_bit = a ^ b;
self.counter >>= 1;
self.counter |= new_bit << 14;
if self.is_7bit {
self.counter &= !(1 << 6);
self.counter |= new_bit << 6;
}
}
pub fn high(&self) -> bool {
(self.counter & 0b1) > 0
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NoiseVoice {
pub play_samples_remaining: usize,
pub level: LevelEnvelope,
pub frequency: i32,
pub counter: NoiseCounter,
pub freq_point: f32,
}
impl NoiseVoice {
pub fn frequency_for(shift: u8, ratio: u8) -> i32 {
debug_assert!(shift < 8);
debug_assert!(ratio < 8);
if ratio == 0 {
(524_288 * 2) / 2i32.pow(u32::from(shift) + 1)
} else {
524_288 / i32::from(ratio) / 2i32.pow(u32::from(shift) + 1)
}
}
pub fn fill_sound_buffer(
&mut self, samples_per_second: i32, buf: &mut [StereoI16], left_max: i16, right_max: i16,
) -> usize {
debug_assert!(self.freq_point < 1.0);
let left_max_f = f32::from(left_max);
let right_max_f = f32::from(right_max);
let mut samples_written: usize = 0;
let in_samples_per_out_sample = self.frequency as f32 / samples_per_second as f32;
let mut buf_iter = buf.iter_mut();
while self.play_samples_remaining > 0 {
self.play_samples_remaining -= 1;
if let Some(sample_mut) = buf_iter.next() {
let here_level_norm: f32 = if in_samples_per_out_sample > 1.0 {
let mut total = 0.0;
let count = in_samples_per_out_sample as u32;
for _ in 0..count {
self.counter.tick();
total += if self.counter.high() { self.level.level } else { 0.0 };
}
self.freq_point += in_samples_per_out_sample.fract();
if self.freq_point >= 1.0 {
self.counter.tick();
self.freq_point -= 1.0;
}
total += if self.counter.high() { self.level.level } else { 0.0 };
total / in_samples_per_out_sample
} else {
self.freq_point += in_samples_per_out_sample;
if self.freq_point >= 1.0 {
self.counter.tick();
self.freq_point -= 1.0;
}
if self.counter.high() { self.level.level } else { 0.0 }
};
debug_assert!(here_level_norm >= 0.0 && here_level_norm <= 1.0);
let here_pos_neg_norm = (here_level_norm * 2.0) - 1.0;
sample_mut.left += (left_max_f * here_pos_neg_norm) as i16;
sample_mut.right += (right_max_f * here_pos_neg_norm) as i16;
samples_written += 1;
self.level.tick_sample(samples_per_second);
} else {
break;
}
}
samples_written
}
}
#[derive(Debug, Default, Clone)]
pub struct DMGAPU {
pub pulse_a: PulseVoice,
pub pulse_b: PulseVoice,
pub wave: WaveVoice,
pub noise: NoiseVoice,
}
impl DMGAPU {
pub fn fill_sound_buffer(&mut self, samples_per_second: i32, buf: &mut [StereoI16]) {
let sps = samples_per_second;
self.pulse_a.fill_sound_buffer(sps, buf, 7000, 7000);
self.pulse_b.fill_sound_buffer(sps, buf, 7000, 7000);
self.wave.fill_sound_buffer(sps, buf, 7000, 7000);
self.noise.fill_sound_buffer(sps, buf, 7000, 7000);
}
}