Skip to content

Commit

Permalink
Back FunDSP…
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaacMarovitz committed Feb 27, 2024
1 parent 33bc7c4 commit f6206a3
Show file tree
Hide file tree
Showing 12 changed files with 870 additions and 183 deletions.
538 changes: 480 additions & 58 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ version = "0.1.0"
edition = "2021"

[dependencies]
winit = { version = "0.29.10", features = ["serde"] }
winit = { version = "0.29.11", features = ["serde"] }
assert_no_alloc = "1.1.2"
cpal = "0.15.2"
fundsp = "0.16.0"
clap = { version = "4.5.1", features = ["derive"] }
bitflags = "2.4.2"
log = "0.4.20"
Expand Down
1 change: 1 addition & 0 deletions src/components/mmu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl Memory for MMU {
}
}
0xFEA0..=0xFEFF => {}
0xFF7F => {}
0xFFFF => self.inte = Interrupts::from_bits_truncate(v),
_ => panic!("Write to unsupported address ({:#06x})!", a),
}
Expand Down
177 changes: 96 additions & 81 deletions src/sound/apu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use bitflags::bitflags;
use cpal::{SampleFormat, Stream};
use cpal::traits::{DeviceTrait, HostTrait};
use crate::CLOCK_FREQUENCY;
use crate::sound::blip::Blip;

pub struct APU {
audio_enabled: bool,
Expand All @@ -24,11 +23,9 @@ pub struct APU {
ch2: CH2,
ch3: CH3,
ch4: CH4,
synth: Synth,
div_one: bool,
freq: f64,
sample_rate: u32,
buffer: Arc<Mutex<Vec<(f32, f32)>>>,
stream: Stream
freq: f64
}

bitflags! {
Expand All @@ -47,15 +44,14 @@ bitflags! {

impl APU {
pub fn new() -> Self {
let host = cpal::default_host();
let device = host.default_output_device().unwrap();
let config = device.default_output_config().unwrap();
let sample_rate = config.sample_rate().0;
let sample_format = config.sample_format();

println!("Initialising Audio Device: {}, at {} Hz ({})", device.name().unwrap(), sample_rate, sample_format);

let buffer = Arc::new(Mutex::new(Vec::new()));
let synth = Synth::new();
// let host = cpal::default_host();
// let device = host.default_output_device().unwrap();
// let config = device.default_output_config().unwrap();
// let sample_rate = config.sample_rate().0;
// let sample_format = config.sample_format();
//
// println!("Initialising Audio Device: {}, at {} Hz ({})", device.name().unwrap(), sample_rate, sample_format);

Self {
audio_enabled: true,
Expand All @@ -66,64 +62,13 @@ impl APU {
left_volume: 0,
right_volume: 0,
panning: Panning::empty(),
ch1: CH1::new(Self::create_blip(sample_rate)),
ch2: CH2::new(Self::create_blip(sample_rate)),
ch3: CH3::new(Self::create_blip(sample_rate)),
ch4: CH4::new(Self::create_blip(sample_rate)),
ch1: CH1::new(),
ch2: CH2::new(),
ch3: CH3::new(),
ch4: CH4::new(),
synth,
div_one: false,
freq: 256.0,
sample_rate,
buffer: buffer.clone(),
stream: match sample_format {
SampleFormat::F32 => {
let buffer_data = buffer.clone();

device.build_output_stream(
&config.config(),
move |data: &mut[f32], _| {
let len = std::cmp::min(data.len() / 2, buffer_data.lock().unwrap().len());
for (i, (data_l, data_r)) in buffer_data.lock().unwrap().drain(..len).enumerate() {
data[i * 2 + 0] = data_l;
data[i * 2 + 1] = data_r;
}
},
move |err| println!("{}", err),
None).unwrap()
}
SampleFormat::F64 => {
let buffer_data = buffer.clone();

device.build_output_stream(
&config.config(),
move |data: &mut[f64], _| {
let len = std::cmp::min(data.len() / 2, buffer_data.lock().unwrap().len());
for (i, (data_l, data_r)) in buffer_data.lock().unwrap().drain(..len).enumerate() {
data[i * 2 + 0] = data_l as f64;
data[i * 2 + 1] = data_r as f64;
}
},
move |err| println!("{}", err),
None).unwrap()
}
format => panic!("Unsupported Output Format {}!", format),
},
}
}

pub fn create_blip(sample_rate: u32) -> Blip {
let mut blip_buf = BlipBuf::new(sample_rate as usize);
blip_buf.set_rates(CLOCK_FREQUENCY, sample_rate);
Blip::new(blip_buf)
}

pub fn play(&mut self, l: &[f32], r: &[f32]) {
assert_eq!(l.len(), r.len());
let mut buffer = self.buffer.lock().unwrap();
for (l, r) in l.iter().zip(r) {
if buffer.len() > self.sample_rate as usize {
return;
}
buffer.push((*l, *r));
}
}

Expand All @@ -150,6 +95,64 @@ impl APU {
self.ch4.cycle();
}

let ch1_vol = {
if self.ch1.dac_enabled {
self.ch1.volume_envelope.volume as f64 / 0xF as f64
} else {
0.0
}
};

let ch1_duty = {
match self.ch1.duty_cycle {
DutyCycle::EIGHTH => 0.125,
DutyCycle::QUARTER => 0.25,
DutyCycle::HALF => 0.5,
DutyCycle::THREE_QUARTERS => 0.75,
_ => 0.0,
}
};

let ch2_vol = {
if self.ch2.dac_enabled {
self.ch2.volume_envelope.volume as f64 / 0xF as f64
} else {
0.0
}
};

let ch2_duty = {
match self.ch2.duty_cycle {
DutyCycle::EIGHTH => 0.125,
DutyCycle::QUARTER => 0.25,
DutyCycle::HALF => 0.5,
DutyCycle::THREE_QUARTERS => 0.75,
_ => 0.0,
}
};

let ch3_vol = {
if self.ch3.dac_enabled {
match self.ch3.output_level {
OutputLevel::MUTE => 0.0,
OutputLevel::QUARTER => 0.25,
OutputLevel::HALF => 0.5,
OutputLevel::MAX => 1.0,
_ => 0.0,
}
} else {
0.0
}
};

let ch4_vol = {
if self.ch4.dac_enabled {
self.ch4.final_volume as f64 / 0xF as f64
} else {
0.0
}
};

// TODO: Amplifier on original hardware NEVER completely mutes non-silent input
let global_l = {
if self.audio_enabled {
Expand All @@ -167,19 +170,31 @@ impl APU {
}
};

let sc1 = self.ch1.blip.data.samples_avail();
let sc2 = self.ch1.blip.data.samples_avail();
let sc3 = self.ch1.blip.data.samples_avail();
let sc4 = self.ch1.blip.data.samples_avail();
self.synth.ch1_freq.set_value(131072.0 / (2048.0 - self.ch1.period as f64));
self.synth.ch1_vol.set_value(ch1_vol);
self.synth.ch1_duty.set_value(ch1_duty);
self.synth.ch1_l.set_value(if self.panning.contains(Panning::CH1_LEFT) { 1.0 } else { 0.0 });
self.synth.ch1_r.set_value(if self.panning.contains(Panning::CH1_RIGHT) { 1.0 } else { 0.0 });

self.synth.ch2_freq.set_value(131072.0 / (2048.0 - self.ch2.period as f64));
self.synth.ch2_vol.set_value(ch2_vol);
self.synth.ch2_duty.set_value(ch2_duty);
self.synth.ch2_l.set_value(if self.panning.contains(Panning::CH2_LEFT) { 1.0 } else { 0.0 });
self.synth.ch2_r.set_value(if self.panning.contains(Panning::CH2_RIGHT) { 1.0 } else { 0.0 });

self.synth.ch3_freq.set_value(65536.0 / (2048.0 - self.ch3.period as f64));
self.synth.ch3_vol.set_value(ch3_vol);
self.synth.ch3_l.set_value(if self.panning.contains(Panning::CH3_LEFT) { 1.0 } else { 0.0 });
self.synth.ch3_r.set_value(if self.panning.contains(Panning::CH3_RIGHT) { 1.0 } else { 0.0 });

self.synth.ch4_freq.set_value(self.ch4.frequency as f64);
self.synth.ch4_vol.set_value(ch4_vol);
self.synth.ch4_l.set_value(if self.panning.contains(Panning::CH4_LEFT) { 1.0 } else { 0.0 });
self.synth.ch4_r.set_value(if self.panning.contains(Panning::CH4_RIGHT) { 1.0 } else { 0.0 });

// Check that all channels
// have equal number of samples
assert_eq!(sc1, sc2);
assert_eq!(sc2, sc3);
assert_eq!(sc3, sc4);
self.synth.global_l.set_value(global_l);
self.synth.global_r.set_value(global_r);

let sample_count = sc1;
let mut sum = 0;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/sound/blip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ impl Blip {
}
}

fn set(&mut self, time: u32, ampl: i32) {
pub(crate) fn set(&mut self, time: u32, ampl: i32) {
self.from = time;
let delta = ampl - self.ampl;
self.ampl = ampl;
Expand Down
18 changes: 7 additions & 11 deletions src/sound/ch1.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::components::memory::Memory;
use crate::sound::apu::DutyCycle;
use crate::sound::blip::Blip;
use crate::sound::length_counter::LengthCounter;
use crate::sound::volume_envelope::VolumeEnvelope;

pub struct CH1 {
pub blip: Blip,
pub dac_enabled: bool,
sweep_pace: u8,
negative_direction: bool,
Expand All @@ -14,13 +12,12 @@ pub struct CH1 {
pub period: u16,
sweep_cycle_count: u32,
length_counter: LengthCounter,
volume_envelope: VolumeEnvelope
pub volume_envelope: VolumeEnvelope
}

impl CH1 {
pub fn new(blip: Blip) -> Self {
pub fn new() -> Self {
Self {
blip,
dac_enabled: false,
sweep_pace: 0,
negative_direction: false,
Expand All @@ -45,6 +42,9 @@ impl CH1 {
}

pub fn cycle(&mut self) {
self.length_counter.cycle();
self.volume_envelope.cycle();

if self.sweep_pace != 0 {
self.sweep_cycle_count += 1;

Expand All @@ -69,10 +69,6 @@ impl CH1 {
}
}
}

self.length_counter.cycle();
self.blip.data.end_frame(4096);
//self.blip.from -= 4096;
}
}

Expand All @@ -90,7 +86,7 @@ impl Memory for CH1 {
0xFF11 => (self.duty_cycle.bits()) << 6 | 0x3F,
// NR12: Volume & Envelope
0xFF12 => {
(self.volume_envelope.volume & 0b0000_1111) << 4
(self.volume_envelope.volume as u8 & 0b0000_1111) << 4
| (self.volume_envelope.positive as u8) << 3
| (self.volume_envelope.positive as u8 & 0b0000_0111)
}
Expand All @@ -117,7 +113,7 @@ impl Memory for CH1 {
}
// NR12: Volume & Envelope
0xFF12 => {
self.volume_envelope.volume = (v & 0b1111_0000) >> 4;
self.volume_envelope.volume = ((v & 0b1111_0000) >> 4) as f32;
self.volume_envelope.positive = ((v & 0b0000_1000) >> 3) != 0;
self.volume_envelope.period = (v & 0b0000_0111) as u16;

Expand Down
14 changes: 5 additions & 9 deletions src/sound/ch2.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
use crate::components::memory::Memory;
use crate::sound::apu::DutyCycle;
use crate::sound::blip::Blip;
use crate::sound::length_counter::LengthCounter;
use crate::sound::volume_envelope::VolumeEnvelope;

pub struct CH2 {
pub blip: Blip,
pub dac_enabled: bool,
pub duty_cycle: DutyCycle,
pub period: u16,
length_counter: LengthCounter,
volume_envelope: VolumeEnvelope
pub volume_envelope: VolumeEnvelope
}

impl CH2 {
pub fn new(blip: Blip) -> Self {
pub fn new() -> Self {
Self {
blip,
dac_enabled: false,
duty_cycle: DutyCycle::EIGHTH,
period: 0,
Expand All @@ -35,8 +32,7 @@ impl CH2 {

pub fn cycle(&mut self) {
self.length_counter.cycle();
self.blip.data.end_frame(4096);
//self.blip.from -= 4096;
self.volume_envelope.cycle();
}
}

Expand All @@ -47,7 +43,7 @@ impl Memory for CH2 {
0xFF16 => (self.duty_cycle.bits()) << 6 | 0x3F,
// NR22: Volume & Envelope
0xFF17 => {
(self.volume_envelope.volume & 0b0000_1111) << 4
(self.volume_envelope.volume as u8 & 0b0000_1111) << 4
| (self.volume_envelope.positive as u8) << 3
| (self.volume_envelope.period as u8 & 0b0000_0111)
}
Expand All @@ -69,7 +65,7 @@ impl Memory for CH2 {
}
// NR22: Volume & Envelope
0xFF17 => {
self.volume_envelope.volume = (v & 0b1111_0000) >> 4;
self.volume_envelope.volume = ((v & 0b1111_0000) >> 4) as f32;
self.volume_envelope.positive = ((v & 0b0000_1000) >> 3) != 0;
self.volume_envelope.period = (v & 0b0000_0111) as u16;

Expand Down
Loading

0 comments on commit f6206a3

Please sign in to comment.