render pulse channels
This commit is contained in:
parent
7ff06f151e
commit
579d13d0ef
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -248,6 +248,7 @@ dependencies = [
|
|||
"gilrs",
|
||||
"minifb",
|
||||
"rand",
|
||||
"samplerate",
|
||||
"spin_sleep",
|
||||
]
|
||||
|
||||
|
@ -385,6 +386,15 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsamplerate-sys"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28853b399f78f8281cd88d333b54a63170c4275f6faea66726a2bea5cca72e0d"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libudev-sys"
|
||||
version = "0.1.4"
|
||||
|
@ -675,6 +685,15 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "samplerate"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e032b2b24715c4f982f483ea3abdb3c9ba444d9f63e87b2843d6f998f5ba2698"
|
||||
dependencies = [
|
||||
"libsamplerate-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
|
|
|
@ -11,3 +11,4 @@ spin_sleep = "1.1.1"
|
|||
minifb = "0.23"
|
||||
rand = "0.8.5"
|
||||
gilrs = "0.10.1"
|
||||
samplerate = "0.2.4"
|
||||
|
|
|
@ -160,6 +160,7 @@ impl Cpu {
|
|||
}
|
||||
self.gpu.mode = DrawMode::VBlank;
|
||||
self.render_window();
|
||||
self.next_audio();
|
||||
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
use samplerate::{convert, ConverterType};
|
||||
|
||||
use crate::{
|
||||
processor::memory::{masked_update, Address},
|
||||
processor::{
|
||||
memory::{masked_update, Address},
|
||||
timer::CLOCK_SPEED,
|
||||
Cpu,
|
||||
},
|
||||
util::{get_bit, set_or_clear_bit},
|
||||
};
|
||||
|
||||
|
@ -46,6 +52,7 @@ pub struct Apu {
|
|||
vol_left: u8,
|
||||
vol_right: u8,
|
||||
div_apu: u8,
|
||||
buffer: Vec<f32>,
|
||||
}
|
||||
|
||||
impl Default for Apu {
|
||||
|
@ -58,10 +65,13 @@ impl Default for Apu {
|
|||
vol_left: 7,
|
||||
vol_right: 7,
|
||||
div_apu: 0,
|
||||
buffer: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SAMPLE_RATE: u32 = 44100;
|
||||
|
||||
impl Apu {
|
||||
pub fn div_apu_tick(&mut self) {
|
||||
self.div_apu = self.div_apu.wrapping_add(1);
|
||||
|
@ -76,6 +86,31 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self, steps: usize) {
|
||||
self.buffer.append(
|
||||
&mut self
|
||||
.channels
|
||||
.one
|
||||
.tick(steps)
|
||||
.into_iter()
|
||||
.zip(self.channels.two.tick(steps).into_iter())
|
||||
.map(|(one, two)| (one + two) / 2.)
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
fn next_audio(&mut self) {
|
||||
let _converted = convert(
|
||||
CLOCK_SPEED as u32,
|
||||
SAMPLE_RATE,
|
||||
1,
|
||||
ConverterType::Linear,
|
||||
&self.buffer,
|
||||
)
|
||||
.unwrap();
|
||||
self.buffer.clear();
|
||||
}
|
||||
|
||||
pub fn get_register(&self, addr: Address) -> u8 {
|
||||
if addr == 0xFF26
|
||||
|| addr == 0xFF11
|
||||
|
@ -189,3 +224,13 @@ impl Apu {
|
|||
self.mem[addr_el] = masked_update(self.mem[addr_el], data, mask);
|
||||
}
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
pub fn advance_apu_clock(&mut self, steps: usize) {
|
||||
self.memory.apu.tick(steps);
|
||||
}
|
||||
|
||||
pub fn next_audio(&mut self) {
|
||||
self.memory.apu.next_audio();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,32 +39,80 @@ impl Default for Envelope {
|
|||
}
|
||||
}
|
||||
|
||||
enum DutyCycle {
|
||||
// 0b00
|
||||
TwelvePointFive,
|
||||
// 0b01
|
||||
TwentyFive,
|
||||
// 0b10
|
||||
Fifty,
|
||||
// 0b11
|
||||
SeventyFive,
|
||||
}
|
||||
|
||||
impl From<u8> for DutyCycle {
|
||||
fn from(value: u8) -> Self {
|
||||
match value & 0b11 {
|
||||
0b00 => Self::TwelvePointFive,
|
||||
0b01 => Self::TwentyFive,
|
||||
0b10 => Self::Fifty,
|
||||
0b11 => Self::SeventyFive,
|
||||
_ => panic!("bad match!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DutyCycle {
|
||||
fn as_waveform(&self) -> [u8; 8] {
|
||||
match self {
|
||||
DutyCycle::TwelvePointFive => [0, 0, 0, 0, 0, 0, 0, 1],
|
||||
DutyCycle::TwentyFive => [1, 0, 0, 0, 0, 0, 0, 1],
|
||||
DutyCycle::Fifty => [1, 0, 0, 0, 0, 1, 1, 1],
|
||||
DutyCycle::SeventyFive => [0, 1, 1, 1, 1, 1, 1, 0],
|
||||
}
|
||||
}
|
||||
|
||||
fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
DutyCycle::TwelvePointFive => 0b00,
|
||||
DutyCycle::TwentyFive => 0b01,
|
||||
DutyCycle::Fifty => 0b10,
|
||||
DutyCycle::SeventyFive => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct PwmChannel {
|
||||
pub(super) enabled: bool,
|
||||
pub(super) pan_left: bool,
|
||||
pub(super) pan_right: bool,
|
||||
sweep: Sweep,
|
||||
duty_cycle: u8,
|
||||
duty_cycle: DutyCycle,
|
||||
length_enable: bool,
|
||||
length_timer: u8,
|
||||
envelope: Envelope,
|
||||
queued_envelope: Envelope,
|
||||
wavelength: u16,
|
||||
wave_timer: u16,
|
||||
wave_position: usize,
|
||||
}
|
||||
|
||||
impl PwmChannel {
|
||||
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
||||
let wavelength = 0x700;
|
||||
Self {
|
||||
enabled,
|
||||
pan_left,
|
||||
pan_right,
|
||||
sweep: Sweep::default(),
|
||||
duty_cycle: 2,
|
||||
duty_cycle: DutyCycle::Fifty,
|
||||
length_enable: false,
|
||||
length_timer: 63,
|
||||
envelope: Envelope::default(),
|
||||
queued_envelope: Envelope::default(),
|
||||
wavelength: 0x700,
|
||||
wavelength,
|
||||
wave_timer: set_wave_timer(wavelength),
|
||||
wave_position: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +121,19 @@ impl PwmChannel {
|
|||
self.envelope = self.queued_envelope;
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, steps: usize) -> Vec<f32> {
|
||||
(0..steps)
|
||||
.map(|_| {
|
||||
let b;
|
||||
(self.wave_timer, b) = self.wave_timer.overflowing_sub(1);
|
||||
if b {
|
||||
self.wave_position = (self.wave_position + 1) % 8;
|
||||
}
|
||||
(self.duty_cycle.as_waveform()[self.wave_position] as f32 * (-2.)) + 1.
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) fn update_sweep(&mut self, pace: u8, mode: EnvelopeMode, slope: u8) {
|
||||
self.sweep.pace = pace;
|
||||
self.sweep.mode = mode;
|
||||
|
@ -88,12 +149,12 @@ impl PwmChannel {
|
|||
}
|
||||
|
||||
pub(super) fn update_length_timer_and_duty_cycle(&mut self, data: u8) {
|
||||
self.length_timer = (data & 0b11000000) >> 6;
|
||||
self.duty_cycle = data & 0b11111;
|
||||
self.duty_cycle = DutyCycle::from(data >> 6);
|
||||
self.length_timer = data & !(0b11 << 6);
|
||||
}
|
||||
|
||||
pub(super) fn get_length_timer_and_duty_cycle(&self) -> u8 {
|
||||
((self.duty_cycle & 0b11) << 6) | 0b11111
|
||||
(self.duty_cycle.as_u8() << 6) | !(0b11 << 6)
|
||||
}
|
||||
|
||||
pub(super) fn update_volume_and_envelope(&mut self, data: u8) {
|
||||
|
@ -134,6 +195,10 @@ impl PwmChannel {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_wave_timer(wavelength: u16) -> u16 {
|
||||
(2048 - wavelength) * 4
|
||||
}
|
||||
|
||||
pub(super) struct WaveChannel {
|
||||
pub(super) enabled: bool,
|
||||
pub(super) pan_left: bool,
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Timers {
|
|||
}
|
||||
|
||||
// Hz
|
||||
const CLOCK_SPEED: usize = 4194304;
|
||||
pub const CLOCK_SPEED: usize = 4194304;
|
||||
// this will need to change when cgb mode is implemented
|
||||
// as it uses bit 5 in double speed mode
|
||||
const AUDIO_BIT: u8 = 4;
|
||||
|
@ -30,6 +30,7 @@ impl Cpu {
|
|||
let clock_cycles = (machine_cycles as usize) * 4;
|
||||
|
||||
self.advance_gpu_clock(clock_cycles);
|
||||
self.advance_apu_clock(clock_cycles);
|
||||
|
||||
self.timers.div_counter += clock_cycles;
|
||||
let mut div_diff = (self.timers.div_counter / 256) as u8;
|
||||
|
|
Loading…
Reference in a new issue