Compare commits
10 Commits
367eb60238
...
db421c58f8
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | db421c58f8 | |
Rekai Nyangadzayi Musuka | ce6a83425f | |
Rekai Nyangadzayi Musuka | d145ae44f3 | |
Rekai Nyangadzayi Musuka | 3dbf026d33 | |
Rekai Nyangadzayi Musuka | 7ab6e120f9 | |
Rekai Nyangadzayi Musuka | 3c28dd7a6b | |
Rekai Nyangadzayi Musuka | 91d19a7e66 | |
Rekai Nyangadzayi Musuka | 40ca0abb72 | |
Rekai Nyangadzayi Musuka | 468f7c0f83 | |
Rekai Nyangadzayi Musuka | 548f3867d4 |
|
@ -71,9 +71,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.41"
|
version = "1.0.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
|
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
|
|
|
@ -338,16 +338,16 @@ impl BusIo for Bus {
|
||||||
0x10 => self.snd.ch1.sweep = byte.into(),
|
0x10 => self.snd.ch1.sweep = byte.into(),
|
||||||
0x11 => self.snd.ch1.set_duty(byte),
|
0x11 => self.snd.ch1.set_duty(byte),
|
||||||
0x12 => self.snd.ch1.envelope = byte.into(),
|
0x12 => self.snd.ch1.envelope = byte.into(),
|
||||||
0x13 => self.snd.ch1.freq_lo = byte,
|
0x13 => self.snd.ch1.set_freq_lo(byte),
|
||||||
0x14 => self.snd.ch1.set_freq_hi(byte),
|
0x14 => self.snd.ch1.set_freq_hi(byte),
|
||||||
0x16 => self.snd.ch2.set_duty(byte),
|
0x16 => self.snd.ch2.set_duty(byte),
|
||||||
0x17 => self.snd.ch2.envelope = byte.into(),
|
0x17 => self.snd.ch2.envelope = byte.into(),
|
||||||
0x18 => self.snd.ch2.freq_lo = byte,
|
0x18 => self.snd.ch2.set_freq_lo(byte),
|
||||||
0x19 => self.snd.ch2.set_freq_hi(byte),
|
0x19 => self.snd.ch2.set_freq_hi(byte),
|
||||||
0x1A => self.snd.ch3.set_enabled(byte),
|
0x1A => self.snd.ch3.set_enabled(byte),
|
||||||
0x1B => self.snd.ch3.set_len(byte),
|
0x1B => self.snd.ch3.set_len(byte),
|
||||||
0x1C => self.snd.ch3.set_volume(byte),
|
0x1C => self.snd.ch3.set_volume(byte),
|
||||||
0x1D => self.snd.ch3.freq_lo = byte,
|
0x1D => self.snd.ch3.set_freq_lo(byte),
|
||||||
0x1E => self.snd.ch3.set_freq_hi(byte),
|
0x1E => self.snd.ch3.set_freq_hi(byte),
|
||||||
0x20 => self.snd.ch4.set_len(byte),
|
0x20 => self.snd.ch4.set_len(byte),
|
||||||
0x21 => self.snd.ch4.envelope = byte.into(),
|
0x21 => self.snd.ch4.envelope = byte.into(),
|
||||||
|
|
17
src/emu.rs
17
src/emu.rs
|
@ -4,8 +4,8 @@ use crate::joypad;
|
||||||
use crate::ppu::Ppu;
|
use crate::ppu::Ppu;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use rodio::OutputStreamHandle;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
||||||
pub const CYCLES_IN_FRAME: Cycle = Cycle::new(456 * 154); // 456 Cycles times 154 scanlines
|
pub const CYCLES_IN_FRAME: Cycle = Cycle::new(456 * 154); // 456 Cycles times 154 scanlines
|
||||||
|
@ -28,19 +28,26 @@ pub fn rom_title(game_boy: &SM83) -> &str {
|
||||||
game_boy.rom_title().unwrap_or(DEFAULT_TITLE)
|
game_boy.rom_title().unwrap_or(DEFAULT_TITLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(game_boy: &mut SM83, gamepad: &mut Gilrs, pending: Cycle) -> Cycle {
|
pub fn run(
|
||||||
|
game_boy: &mut SM83,
|
||||||
|
gamepad: &mut Gilrs,
|
||||||
|
input: &WinitInputHelper,
|
||||||
|
pending: Cycle,
|
||||||
|
) -> Cycle {
|
||||||
let mut elapsed = Cycle::new(0);
|
let mut elapsed = Cycle::new(0);
|
||||||
|
|
||||||
while elapsed < pending {
|
while elapsed < pending {
|
||||||
elapsed += run_unsynced(game_boy, gamepad);
|
elapsed += run_unsynced(game_boy, gamepad, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed
|
elapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_unsynced(game_boy: &mut SM83, gamepad: &mut Gilrs) -> Cycle {
|
pub fn run_unsynced(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
|
||||||
if let Some(event) = gamepad.next_event() {
|
if let Some(event) = gamepad.next_event() {
|
||||||
joypad::handle_gamepad_input(&mut game_boy.joypad_mut(), event);
|
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
|
||||||
|
} else {
|
||||||
|
joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
game_boy.step()
|
game_boy.step()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
|
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
|
||||||
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Joypad {
|
pub struct Joypad {
|
||||||
|
@ -109,6 +110,63 @@ impl ButtonEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) {
|
||||||
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
// TODO: What do I have to do to get a match statement here?
|
||||||
|
|
||||||
|
let state = &mut pad.ext;
|
||||||
|
let irq = &mut pad.interrupt;
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Down) {
|
||||||
|
state.dpad_down.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Down) {
|
||||||
|
state.dpad_down.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Up) {
|
||||||
|
state.dpad_up.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Up) {
|
||||||
|
state.dpad_up.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Left) {
|
||||||
|
state.dpad_left.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Left) {
|
||||||
|
state.dpad_left.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Right) {
|
||||||
|
state.dpad_right.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Right) {
|
||||||
|
state.dpad_right.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::T) {
|
||||||
|
state.start.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::T) {
|
||||||
|
state.start.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Y) {
|
||||||
|
state.select.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Y) {
|
||||||
|
state.select.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::Z) {
|
||||||
|
state.south.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::Z) {
|
||||||
|
state.south.update(false, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.key_pressed(VirtualKeyCode::X) {
|
||||||
|
state.east.update(true, irq);
|
||||||
|
} else if input.key_released(VirtualKeyCode::X) {
|
||||||
|
state.east.update(false, irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) {
|
pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) {
|
||||||
use Button::*;
|
use Button::*;
|
||||||
use GamepadEventType::*;
|
use GamepadEventType::*;
|
||||||
|
|
|
@ -8,11 +8,10 @@ use std::time::Instant;
|
||||||
use winit::dpi::LogicalSize;
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{Event, VirtualKeyCode};
|
use winit::event::{Event, VirtualKeyCode};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use winit::platform::windows::WindowBuilderExtWindows;
|
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::window::{Window, WindowBuilder};
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
const SCALE: f64 = 5.0;
|
const SCALE: f64 = 2.0;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let app = App::new(crate_name!())
|
let app = App::new(crate_name!())
|
||||||
|
@ -128,7 +127,7 @@ fn main() -> Result<()> {
|
||||||
now = Instant::now();
|
now = Instant::now();
|
||||||
|
|
||||||
let pending = Cycle::new(delta / gb::emu::SM83_CYCLE_TIME.subsec_nanos());
|
let pending = Cycle::new(delta / gb::emu::SM83_CYCLE_TIME.subsec_nanos());
|
||||||
cycle_count += gb::emu::run(&mut game_boy, &mut gamepad, pending);
|
cycle_count += gb::emu::run(&mut game_boy, &mut gamepad, &input, pending);
|
||||||
|
|
||||||
if cycle_count >= gb::emu::CYCLES_IN_FRAME {
|
if cycle_count >= gb::emu::CYCLES_IN_FRAME {
|
||||||
// Draw Frame
|
// Draw Frame
|
||||||
|
@ -150,6 +149,6 @@ fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
|
||||||
.with_resizable(true)
|
.with_resizable(true)
|
||||||
.with_decorations(true)
|
.with_decorations(true)
|
||||||
.with_transparent(false)
|
.with_transparent(false)
|
||||||
.with_drag_and_drop(false) // OleInitialize failed error if this is set to true
|
// .with_drag_and_drop(false) // OleInitialize failed error if this is set to true
|
||||||
.build(event_loop)?)
|
.build(event_loop)?)
|
||||||
}
|
}
|
||||||
|
|
379
src/sound.rs
379
src/sound.rs
|
@ -3,11 +3,10 @@ use crossbeam_channel::{Receiver, Sender};
|
||||||
use rodio::Source;
|
use rodio::Source;
|
||||||
|
|
||||||
use crate::emu::SM83_CLOCK_SPEED;
|
use crate::emu::SM83_CLOCK_SPEED;
|
||||||
use crate::Cycle;
|
|
||||||
|
|
||||||
const WAVE_PATTERN_RAM_LEN: usize = 0x10;
|
const WAVE_PATTERN_RAM_LEN: usize = 0x10;
|
||||||
const SAMPLE_RATE: u32 = 4800; // Hz
|
const SAMPLE_RATE: u32 = 48000; // Hz
|
||||||
const SAMPLE_RATE_IN_CYCLES: Cycle = Cycle::new((SM83_CLOCK_SPEED / SAMPLE_RATE as u64) as u32);
|
const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct Sound {
|
pub(crate) struct Sound {
|
||||||
|
@ -26,13 +25,13 @@ pub(crate) struct Sound {
|
||||||
div_prev: Option<u8>,
|
div_prev: Option<u8>,
|
||||||
|
|
||||||
sender: Option<SampleSender>,
|
sender: Option<SampleSender>,
|
||||||
cycle: Cycle,
|
sample_counter: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sound {
|
impl Sound {
|
||||||
pub(crate) fn clock(&mut self, div: u16) {
|
pub(crate) fn clock(&mut self, div: u16) {
|
||||||
use FrameSequencerState::*;
|
use FrameSequencerState::*;
|
||||||
self.cycle += 1;
|
self.sample_counter += SAMPLE_INCREMENT;
|
||||||
|
|
||||||
// the 5th bit of the high byte
|
// the 5th bit of the high byte
|
||||||
let bit_5 = (div >> 13 & 0x01) as u8;
|
let bit_5 = (div >> 13 & 0x01) as u8;
|
||||||
|
@ -59,19 +58,38 @@ impl Sound {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ch1.clock();
|
||||||
|
self.ch2.clock();
|
||||||
|
self.ch3.clock();
|
||||||
|
self.ch4.clock();
|
||||||
|
|
||||||
self.div_prev = Some(bit_5);
|
self.div_prev = Some(bit_5);
|
||||||
|
|
||||||
// TODO: Should the FrameSequencer be run first?
|
if self.sample_counter >= SM83_CLOCK_SPEED {
|
||||||
if self.cycle > SAMPLE_RATE_IN_CYCLES {
|
self.sample_counter %= SM83_CLOCK_SPEED;
|
||||||
// Sample the APU
|
// Sample the APU
|
||||||
self.cycle %= SAMPLE_RATE_IN_CYCLES;
|
|
||||||
|
|
||||||
let left_sample = self.ch2.clock();
|
let ch1_amplitude = self.ch1.amplitude();
|
||||||
let right_sample = self.ch2.clock();
|
let ch1_left = self.ctrl.output.ch1_left() as u8 as f32 * ch1_amplitude;
|
||||||
|
let ch1_right = self.ctrl.output.ch1_right() as u8 as f32 * ch1_amplitude;
|
||||||
|
|
||||||
|
let ch2_amplitude = self.ch2.amplitude();
|
||||||
|
let ch2_left = self.ctrl.output.ch2_left() as u8 as f32 * ch2_amplitude;
|
||||||
|
let ch2_right = self.ctrl.output.ch2_right() as u8 as f32 * ch2_amplitude;
|
||||||
|
|
||||||
|
let ch3_amplitude = self.ch3.amplitude();
|
||||||
|
let ch3_left = self.ctrl.output.ch3_left() as u8 as f32 * ch3_amplitude;
|
||||||
|
let ch3_right = self.ctrl.output.ch3_right() as u8 as f32 * ch3_amplitude;
|
||||||
|
|
||||||
|
let ch4_amplitude = self.ch4.amplitude();
|
||||||
|
let ch4_left = self.ctrl.output.ch4_left() as u8 as f32 * ch4_amplitude;
|
||||||
|
let ch4_right = self.ctrl.output.ch4_right() as u8 as f32 * ch4_amplitude;
|
||||||
|
|
||||||
|
let left_sample = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
|
||||||
|
let right_sample = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
|
||||||
|
|
||||||
if let Some(send) = self.sender.as_ref() {
|
if let Some(send) = self.sender.as_ref() {
|
||||||
send.add_sample(left_sample);
|
send.add_sample(left_sample, right_sample);
|
||||||
send.add_sample(right_sample);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,46 +98,54 @@ impl Sound {
|
||||||
self.sender = Some(sender);
|
self.sender = Some(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
|
||||||
|
if freq_hi.idk() && *length_timer > 0 {
|
||||||
|
*length_timer -= 1;
|
||||||
|
|
||||||
|
// Check in this scope ensures (only) the above subtraction
|
||||||
|
// made length_timer 0
|
||||||
|
if *length_timer == 0 {
|
||||||
|
*enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_length_ch4(freq_data: &Channel4Frequency, length_timer: &mut u16, enabled: &mut bool) {
|
||||||
|
if freq_data.idk() && *length_timer > 0 {
|
||||||
|
*length_timer -= 1;
|
||||||
|
|
||||||
|
// Check in this scope ensures (only) the above subtraction
|
||||||
|
// made length_timer 0
|
||||||
|
if *length_timer == 0 {
|
||||||
|
*enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_length(&mut self) {
|
fn handle_length(&mut self) {
|
||||||
if self.ch1.freq_hi.idk() && self.ch1.length_timer > 0 {
|
Self::clock_length(
|
||||||
self.ch1.length_timer -= 1;
|
&self.ch1.freq_hi,
|
||||||
|
&mut self.ch1.length_timer,
|
||||||
|
&mut self.ch1.enabled,
|
||||||
|
);
|
||||||
|
|
||||||
// Check in this scope ensures (only) the above subtraction
|
Self::clock_length(
|
||||||
// made length_timer 0
|
&self.ch2.freq_hi,
|
||||||
if self.ch1.length_timer == 0 {
|
&mut self.ch2.length_timer,
|
||||||
self.ch1.enabled = false;
|
&mut self.ch2.enabled,
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch2.freq_hi.idk() && self.ch2.length_timer > 0 {
|
Self::clock_length(
|
||||||
self.ch2.length_timer -= 1;
|
&self.ch3.freq_hi,
|
||||||
|
&mut self.ch3.length_timer,
|
||||||
|
&mut self.ch3.enabled,
|
||||||
|
);
|
||||||
|
|
||||||
// Check in this scope ensures (only) the above subtraction
|
Self::clock_length_ch4(
|
||||||
// made length_timer 0
|
&self.ch4.freq_data,
|
||||||
if self.ch2.length_timer == 0 {
|
&mut self.ch4.length_timer,
|
||||||
self.ch2.enabled = false;
|
&mut self.ch4.enabled,
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch3.freq_hi.idk() && self.ch3.length_timer > 0 {
|
|
||||||
self.ch3.length_timer -= 1;
|
|
||||||
|
|
||||||
// Check in this scope ensures (only) the above subtraction
|
|
||||||
// made length_timer 0
|
|
||||||
if self.ch3.length_timer == 0 {
|
|
||||||
self.ch3.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch4.freq_data.idk() && self.ch4.length_timer > 0 {
|
|
||||||
self.ch4.length_timer -= 1;
|
|
||||||
|
|
||||||
// Check in this scope ensures (only) the above subtraction
|
|
||||||
// made length_timer 0
|
|
||||||
if self.ch4.length_timer == 0 {
|
|
||||||
self.ch4.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_sweep(&mut self) {
|
fn handle_sweep(&mut self) {
|
||||||
|
@ -147,57 +173,46 @@ impl Sound {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_volume(&mut self) {
|
fn clock_envelope(envelope: &VolumeEnvelope, period_timer: &mut u8, current_volume: &mut u8) {
|
||||||
use EnvelopeDirection::*;
|
use EnvelopeDirection::*;
|
||||||
|
|
||||||
|
if envelope.period() != 0 {
|
||||||
|
if *period_timer != 0 {
|
||||||
|
*period_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *period_timer == 0 {
|
||||||
|
*period_timer = envelope.period();
|
||||||
|
|
||||||
|
match envelope.direction() {
|
||||||
|
Decrease if *current_volume > 0x00 => *current_volume -= 1,
|
||||||
|
Increase if *current_volume < 0x0F => *current_volume += 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_volume(&mut self) {
|
||||||
// Channels 1, 2 and 4 have Volume Envelopes
|
// Channels 1, 2 and 4 have Volume Envelopes
|
||||||
|
|
||||||
if self.ch1.envelope.period() != 0 {
|
Self::clock_envelope(
|
||||||
if self.ch1.period_timer > 0 {
|
&self.ch1.envelope,
|
||||||
self.ch1.period_timer -= 1;
|
&mut self.ch1.period_timer,
|
||||||
}
|
&mut self.ch1.current_volume,
|
||||||
|
);
|
||||||
|
|
||||||
if self.ch1.period_timer == 0 {
|
Self::clock_envelope(
|
||||||
self.ch1.period_timer = self.ch1.envelope.period();
|
&self.ch2.envelope,
|
||||||
|
&mut self.ch2.period_timer,
|
||||||
|
&mut self.ch2.current_volume,
|
||||||
|
);
|
||||||
|
|
||||||
match self.ch1.envelope.direction() {
|
Self::clock_envelope(
|
||||||
Decrease if self.ch1.current_volume > 0x00 => self.ch1.current_volume -= 1,
|
&self.ch4.envelope,
|
||||||
Increase if self.ch1.current_volume < 0x0F => self.ch1.current_volume += 1,
|
&mut self.ch4.period_timer,
|
||||||
_ => {}
|
&mut self.ch4.current_volume,
|
||||||
}
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch2.envelope.period() != 0 {
|
|
||||||
if self.ch2.period_timer > 0 {
|
|
||||||
self.ch2.period_timer -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch2.period_timer == 0 {
|
|
||||||
self.ch2.period_timer = self.ch2.envelope.period();
|
|
||||||
|
|
||||||
match self.ch2.envelope.direction() {
|
|
||||||
Decrease if self.ch2.current_volume > 0x00 => self.ch2.current_volume -= 1,
|
|
||||||
Increase if self.ch2.current_volume < 0x0F => self.ch2.current_volume += 1,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch4.envelope.period() != 0 {
|
|
||||||
if self.ch4.period_timer > 0 {
|
|
||||||
self.ch4.period_timer -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ch4.period_timer == 0 {
|
|
||||||
self.ch4.period_timer = self.ch4.envelope.period();
|
|
||||||
|
|
||||||
match self.ch4.envelope.direction() {
|
|
||||||
Decrease if self.ch4.current_volume > 0x00 => self.ch4.current_volume -= 1,
|
|
||||||
Increase if self.ch4.current_volume < 0x0F => self.ch4.current_volume += 1,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +357,7 @@ pub(crate) struct Channel1 {
|
||||||
/// 0xFF12 | NR12 - Channel 1 Volume Envelope
|
/// 0xFF12 | NR12 - Channel 1 Volume Envelope
|
||||||
pub(crate) envelope: VolumeEnvelope,
|
pub(crate) envelope: VolumeEnvelope,
|
||||||
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
||||||
pub(crate) freq_lo: u8,
|
freq_lo: u8,
|
||||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||||
freq_hi: FrequencyHigh,
|
freq_hi: FrequencyHigh,
|
||||||
|
|
||||||
|
@ -358,13 +373,34 @@ pub(crate) struct Channel1 {
|
||||||
// Length Functionality
|
// Length Functionality
|
||||||
length_timer: u16,
|
length_timer: u16,
|
||||||
|
|
||||||
|
freq_timer: u16,
|
||||||
|
duty_pos: u8,
|
||||||
|
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel1 {
|
impl Channel1 {
|
||||||
|
fn amplitude(&self) -> f32 {
|
||||||
|
let dac_input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||||
|
|
||||||
|
(dac_input as f32 / 7.5) - 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) {
|
||||||
|
if self.freq_timer != 0 {
|
||||||
|
self.freq_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.freq_timer == 0 {
|
||||||
|
// TODO: Why is this 2048?
|
||||||
|
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||||
|
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
|
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
|
||||||
pub(crate) fn duty(&self) -> u8 {
|
pub(crate) fn duty(&self) -> u8 {
|
||||||
self.duty.into()
|
u8::from(self.duty) & 0xC0 // Only bits 7 and 6 can be read
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
|
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
|
||||||
|
@ -373,9 +409,14 @@ impl Channel1 {
|
||||||
self.length_timer = 64 - self.duty.sound_length() as u16;
|
self.length_timer = 64 - self.duty.sound_length() as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
||||||
|
pub(crate) fn set_freq_lo(&mut self, byte: u8) {
|
||||||
|
self.freq_lo = byte;
|
||||||
|
}
|
||||||
|
|
||||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||||
pub(crate) fn freq_hi(&self) -> u8 {
|
pub(crate) fn freq_hi(&self) -> u8 {
|
||||||
self.freq_hi.into()
|
u8::from(self.freq_hi) & 0x40 // Only bit 6 is readable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||||
|
@ -502,7 +543,7 @@ pub(crate) struct Channel2 {
|
||||||
/// 0xFF17 | NR22 - Channel 2 Volume ENvelope
|
/// 0xFF17 | NR22 - Channel 2 Volume ENvelope
|
||||||
pub(crate) envelope: VolumeEnvelope,
|
pub(crate) envelope: VolumeEnvelope,
|
||||||
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
||||||
pub(crate) freq_lo: u8,
|
freq_lo: u8,
|
||||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||||
freq_hi: FrequencyHigh,
|
freq_hi: FrequencyHigh,
|
||||||
|
|
||||||
|
@ -520,21 +561,27 @@ pub(crate) struct Channel2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel2 {
|
impl Channel2 {
|
||||||
fn clock(&mut self) -> u8 {
|
fn amplitude(&self) -> f32 {
|
||||||
|
let dac_input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||||
|
|
||||||
|
(dac_input as f32 / 7.5) - 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) {
|
||||||
|
if self.freq_timer != 0 {
|
||||||
self.freq_timer -= 1;
|
self.freq_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if self.freq_timer == 0 {
|
if self.freq_timer == 0 {
|
||||||
// TODO: Why is this 2048?
|
// TODO: Why is this 2048?
|
||||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.duty.wave_pattern().amplitude(self.duty_pos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
||||||
pub(crate) fn duty(&self) -> u8 {
|
pub(crate) fn duty(&self) -> u8 {
|
||||||
self.duty.into()
|
u8::from(self.duty) & 0xC0 // Only bits 7 & 6 are readable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
||||||
|
@ -543,9 +590,14 @@ impl Channel2 {
|
||||||
self.length_timer = 64 - self.duty.sound_length() as u16;
|
self.length_timer = 64 - self.duty.sound_length() as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
||||||
|
pub(crate) fn set_freq_lo(&mut self, byte: u8) {
|
||||||
|
self.freq_lo = byte;
|
||||||
|
}
|
||||||
|
|
||||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||||
pub(crate) fn freq_hi(&self) -> u8 {
|
pub(crate) fn freq_hi(&self) -> u8 {
|
||||||
self.freq_hi.into()
|
u8::from(self.freq_hi) & 0x40 // only bit 6 is readable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||||
|
@ -717,13 +769,16 @@ pub(crate) struct Channel3 {
|
||||||
/// 0xFF1C | NR32 - Channel 3 Volume
|
/// 0xFF1C | NR32 - Channel 3 Volume
|
||||||
volume: Channel3Volume,
|
volume: Channel3Volume,
|
||||||
/// 0xFF1D | NR33 - Channel 3 Frequency low (lower 8 bits)
|
/// 0xFF1D | NR33 - Channel 3 Frequency low (lower 8 bits)
|
||||||
pub(crate) freq_lo: u8,
|
freq_lo: u8,
|
||||||
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
||||||
freq_hi: FrequencyHigh,
|
freq_hi: FrequencyHigh,
|
||||||
pub(crate) wave_ram: [u8; WAVE_PATTERN_RAM_LEN],
|
pub(crate) wave_ram: [u8; WAVE_PATTERN_RAM_LEN],
|
||||||
|
|
||||||
// Length Functionality
|
// Length Functionality
|
||||||
length_timer: u16,
|
length_timer: u16,
|
||||||
|
|
||||||
|
freq_timer: u16,
|
||||||
|
offset: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel3 {
|
impl Channel3 {
|
||||||
|
@ -738,9 +793,14 @@ impl Channel3 {
|
||||||
self.length_timer = 256 - self.len as u16;
|
self.length_timer = 256 - self.len as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 0xFF1D | NR33 - Channel 3 Frequency low (lower 8 bits)
|
||||||
|
pub(crate) fn set_freq_lo(&mut self, byte: u8) {
|
||||||
|
self.freq_lo = byte;
|
||||||
|
}
|
||||||
|
|
||||||
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
||||||
pub(crate) fn freq_hi(&self) -> u8 {
|
pub(crate) fn freq_hi(&self) -> u8 {
|
||||||
self.freq_hi.into()
|
u8::from(self.freq_hi) & 0x40 // Only bit 6 readable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
||||||
|
@ -756,7 +816,7 @@ impl Channel3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn enabled(&self) -> u8 {
|
pub(crate) fn enabled(&self) -> u8 {
|
||||||
self.enabled as u8
|
(self.enabled as u8) << 7
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_enabled(&mut self, byte: u8) {
|
pub(crate) fn set_enabled(&mut self, byte: u8) {
|
||||||
|
@ -778,6 +838,39 @@ impl Channel3 {
|
||||||
_ => unreachable!("{:#04X} is not a valid value for Channel3Volume", byte),
|
_ => unreachable!("{:#04X} is not a valid value for Channel3Volume", byte),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn amplitude(&self) -> f32 {
|
||||||
|
let dac_input = self.read_sample(self.offset) >> self.volume.shift_count();
|
||||||
|
|
||||||
|
(dac_input as f32 / 7.5) - 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) {
|
||||||
|
if self.freq_timer != 0 {
|
||||||
|
self.freq_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.freq_timer == 0 {
|
||||||
|
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||||
|
self.offset = (self.offset + 1) % 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sample(&self, index: u8) -> u8 {
|
||||||
|
let i = index as usize / 2;
|
||||||
|
|
||||||
|
if index % 2 == 0 {
|
||||||
|
// We grab the high nibble on even indexes
|
||||||
|
self.wave_ram[i] >> 4
|
||||||
|
} else {
|
||||||
|
// We grab the low nibble on odd indexes
|
||||||
|
self.wave_ram[i] & 0x0F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frequency(&self) -> u16 {
|
||||||
|
(self.freq_hi.freq_bits() as u16) << 8 | self.freq_lo as u16
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -826,7 +919,9 @@ pub(crate) struct Channel4 {
|
||||||
length_timer: u16,
|
length_timer: u16,
|
||||||
|
|
||||||
/// Linear Feedback Shift Register (15-bit)
|
/// Linear Feedback Shift Register (15-bit)
|
||||||
shift_register: u16,
|
lf_shift: u16,
|
||||||
|
|
||||||
|
freq_timer: u16,
|
||||||
|
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
@ -845,7 +940,7 @@ impl Channel4 {
|
||||||
|
|
||||||
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
||||||
pub(crate) fn freq_data(&self) -> u8 {
|
pub(crate) fn freq_data(&self) -> u8 {
|
||||||
self.freq_data.into()
|
u8::from(self.freq_data) & 0x40 // only bit 6 readable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
||||||
|
@ -863,17 +958,49 @@ impl Channel4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LFSR behaviour during trigger event
|
// LFSR behaviour during trigger event
|
||||||
self.shift_register = 0x7FFF;
|
self.lf_shift = 0x7FFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn amplitude(&self) -> f32 {
|
||||||
|
let dac_input = (!self.lf_shift & 0x01) as u8 * self.current_volume;
|
||||||
|
|
||||||
|
(dac_input as f32 / 7.5) - 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) {
|
||||||
|
if self.freq_timer != 0 {
|
||||||
|
self.freq_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.freq_timer == 0 {
|
||||||
|
let divisor = Self::divisor(self.poly.divisor_code()) as u16;
|
||||||
|
self.freq_timer = divisor << self.poly.shift_count();
|
||||||
|
|
||||||
|
let xor_result = (self.lf_shift & 0x01) ^ ((self.lf_shift & 0x02) >> 1);
|
||||||
|
self.lf_shift = (self.lf_shift >> 1) | xor_result << 14;
|
||||||
|
|
||||||
|
if let CounterWidth::Long = self.poly.counter_width() {
|
||||||
|
self.lf_shift = (self.lf_shift & !(0x01 << 6)) | xor_result << 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divisor(code: u8) -> u8 {
|
||||||
|
if code == 0 {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
code << 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
pub struct PolynomialCounter(u8);
|
pub struct PolynomialCounter(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
freq, set_freq: 7, 4;
|
shift_count, set_shift_count: 7, 4;
|
||||||
from into CounterWidth, counter_width, set_counter_width: 3, 3;
|
from into CounterWidth, counter_width, set_counter_width: 3, 3;
|
||||||
div_ratio, set_div_ratio: 2, 0;
|
divisor_code, set_divisor_code: 2, 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Copy for PolynomialCounter {}
|
impl Copy for PolynomialCounter {}
|
||||||
|
@ -958,14 +1085,14 @@ impl From<Channel4Frequency> for u8 {
|
||||||
bitfield! {
|
bitfield! {
|
||||||
pub struct SoundOutput(u8);
|
pub struct SoundOutput(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
pub snd4_so2, set_snd4_so2: 7;
|
pub ch4_left, set_ch4_left: 7;
|
||||||
pub snd3_so2, set_snd3_so2: 6;
|
pub ch3_left, set_ch3_left: 6;
|
||||||
pub snd2_so2, set_snd2_so2: 5;
|
pub ch2_left, set_ch2_left: 5;
|
||||||
pub snd1_so2, set_snd1_so2: 4;
|
pub ch1_left, set_ch1_left: 4;
|
||||||
pub snd4_so1, set_snd4_so1: 3;
|
pub ch4_right, set_ch4_right: 3;
|
||||||
pub snd3_so1, set_snd3_so1: 2;
|
pub ch3_right, set_ch3_right: 2;
|
||||||
pub snd2_so1, set_snd2_so1: 1;
|
pub ch2_right, set_ch2_right: 1;
|
||||||
pub snd1_so1, set_snd1_so1: 0;
|
pub ch1_right, set_ch1_right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Copy for SoundOutput {}
|
impl Copy for SoundOutput {}
|
||||||
|
@ -1039,19 +1166,23 @@ impl AudioSenderReceiver {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SampleSender {
|
pub struct SampleSender {
|
||||||
send: Sender<u8>,
|
send: Sender<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SampleSender {
|
impl SampleSender {
|
||||||
fn add_sample(&self, sample: u8) {
|
fn add_sample(&self, left: f32, right: f32) {
|
||||||
self.send
|
self.send
|
||||||
.send(sample)
|
.send(left)
|
||||||
|
.expect("Send audio sample across threads");
|
||||||
|
|
||||||
|
self.send
|
||||||
|
.send(right)
|
||||||
.expect("Send audio sample across threads");
|
.expect("Send audio sample across threads");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SampleReceiver {
|
pub struct SampleReceiver {
|
||||||
recv: Receiver<u8>,
|
recv: Receiver<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for SampleReceiver {
|
impl Iterator for SampleReceiver {
|
||||||
|
@ -1059,7 +1190,7 @@ impl Iterator for SampleReceiver {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// TODO: Should this never return none?
|
// TODO: Should this never return none?
|
||||||
self.recv.recv().ok().map(|num| num as f32)
|
self.recv.recv().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue