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",
|
"gilrs",
|
||||||
"minifb",
|
"minifb",
|
||||||
"rand",
|
"rand",
|
||||||
|
"samplerate",
|
||||||
"spin_sleep",
|
"spin_sleep",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -385,6 +386,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsamplerate-sys"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28853b399f78f8281cd88d333b54a63170c4275f6faea66726a2bea5cca72e0d"
|
||||||
|
dependencies = [
|
||||||
|
"cmake",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libudev-sys"
|
name = "libudev-sys"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -675,6 +685,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"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]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
|
@ -11,3 +11,4 @@ spin_sleep = "1.1.1"
|
||||||
minifb = "0.23"
|
minifb = "0.23"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
gilrs = "0.10.1"
|
gilrs = "0.10.1"
|
||||||
|
samplerate = "0.2.4"
|
||||||
|
|
|
@ -160,6 +160,7 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
self.gpu.mode = DrawMode::VBlank;
|
self.gpu.mode = DrawMode::VBlank;
|
||||||
self.render_window();
|
self.render_window();
|
||||||
|
self.next_audio();
|
||||||
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
|
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
|
use samplerate::{convert, ConverterType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
processor::memory::{masked_update, Address},
|
processor::{
|
||||||
|
memory::{masked_update, Address},
|
||||||
|
timer::CLOCK_SPEED,
|
||||||
|
Cpu,
|
||||||
|
},
|
||||||
util::{get_bit, set_or_clear_bit},
|
util::{get_bit, set_or_clear_bit},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,6 +52,7 @@ pub struct Apu {
|
||||||
vol_left: u8,
|
vol_left: u8,
|
||||||
vol_right: u8,
|
vol_right: u8,
|
||||||
div_apu: u8,
|
div_apu: u8,
|
||||||
|
buffer: Vec<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Apu {
|
impl Default for Apu {
|
||||||
|
@ -58,10 +65,13 @@ impl Default for Apu {
|
||||||
vol_left: 7,
|
vol_left: 7,
|
||||||
vol_right: 7,
|
vol_right: 7,
|
||||||
div_apu: 0,
|
div_apu: 0,
|
||||||
|
buffer: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SAMPLE_RATE: u32 = 44100;
|
||||||
|
|
||||||
impl Apu {
|
impl Apu {
|
||||||
pub fn div_apu_tick(&mut self) {
|
pub fn div_apu_tick(&mut self) {
|
||||||
self.div_apu = self.div_apu.wrapping_add(1);
|
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 {
|
pub fn get_register(&self, addr: Address) -> u8 {
|
||||||
if addr == 0xFF26
|
if addr == 0xFF26
|
||||||
|| addr == 0xFF11
|
|| addr == 0xFF11
|
||||||
|
@ -189,3 +224,13 @@ impl Apu {
|
||||||
self.mem[addr_el] = masked_update(self.mem[addr_el], data, mask);
|
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) struct PwmChannel {
|
||||||
pub(super) enabled: bool,
|
pub(super) enabled: bool,
|
||||||
pub(super) pan_left: bool,
|
pub(super) pan_left: bool,
|
||||||
pub(super) pan_right: bool,
|
pub(super) pan_right: bool,
|
||||||
sweep: Sweep,
|
sweep: Sweep,
|
||||||
duty_cycle: u8,
|
duty_cycle: DutyCycle,
|
||||||
length_enable: bool,
|
length_enable: bool,
|
||||||
length_timer: u8,
|
length_timer: u8,
|
||||||
envelope: Envelope,
|
envelope: Envelope,
|
||||||
queued_envelope: Envelope,
|
queued_envelope: Envelope,
|
||||||
wavelength: u16,
|
wavelength: u16,
|
||||||
|
wave_timer: u16,
|
||||||
|
wave_position: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PwmChannel {
|
impl PwmChannel {
|
||||||
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
||||||
|
let wavelength = 0x700;
|
||||||
Self {
|
Self {
|
||||||
enabled,
|
enabled,
|
||||||
pan_left,
|
pan_left,
|
||||||
pan_right,
|
pan_right,
|
||||||
sweep: Sweep::default(),
|
sweep: Sweep::default(),
|
||||||
duty_cycle: 2,
|
duty_cycle: DutyCycle::Fifty,
|
||||||
length_enable: false,
|
length_enable: false,
|
||||||
length_timer: 63,
|
length_timer: 63,
|
||||||
envelope: Envelope::default(),
|
envelope: Envelope::default(),
|
||||||
queued_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;
|
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) {
|
pub(super) fn update_sweep(&mut self, pace: u8, mode: EnvelopeMode, slope: u8) {
|
||||||
self.sweep.pace = pace;
|
self.sweep.pace = pace;
|
||||||
self.sweep.mode = mode;
|
self.sweep.mode = mode;
|
||||||
|
@ -88,12 +149,12 @@ impl PwmChannel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn update_length_timer_and_duty_cycle(&mut self, data: u8) {
|
pub(super) fn update_length_timer_and_duty_cycle(&mut self, data: u8) {
|
||||||
self.length_timer = (data & 0b11000000) >> 6;
|
self.duty_cycle = DutyCycle::from(data >> 6);
|
||||||
self.duty_cycle = data & 0b11111;
|
self.length_timer = data & !(0b11 << 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_length_timer_and_duty_cycle(&self) -> u8 {
|
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) {
|
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) struct WaveChannel {
|
||||||
pub(super) enabled: bool,
|
pub(super) enabled: bool,
|
||||||
pub(super) pan_left: bool,
|
pub(super) pan_left: bool,
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl Timers {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hz
|
// Hz
|
||||||
const CLOCK_SPEED: usize = 4194304;
|
pub const CLOCK_SPEED: usize = 4194304;
|
||||||
// this will need to change when cgb mode is implemented
|
// this will need to change when cgb mode is implemented
|
||||||
// as it uses bit 5 in double speed mode
|
// as it uses bit 5 in double speed mode
|
||||||
const AUDIO_BIT: u8 = 4;
|
const AUDIO_BIT: u8 = 4;
|
||||||
|
@ -30,6 +30,7 @@ impl Cpu {
|
||||||
let clock_cycles = (machine_cycles as usize) * 4;
|
let clock_cycles = (machine_cycles as usize) * 4;
|
||||||
|
|
||||||
self.advance_gpu_clock(clock_cycles);
|
self.advance_gpu_clock(clock_cycles);
|
||||||
|
self.advance_apu_clock(clock_cycles);
|
||||||
|
|
||||||
self.timers.div_counter += clock_cycles;
|
self.timers.div_counter += clock_cycles;
|
||||||
let mut div_diff = (self.timers.div_counter / 256) as u8;
|
let mut div_diff = (self.timers.div_counter / 256) as u8;
|
||||||
|
|
Loading…
Reference in a new issue