Partial audio implementation
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s

This commit is contained in:
2026-03-14 21:19:16 -05:00
parent 3372559c19
commit b9a30c286a
12 changed files with 1257 additions and 171 deletions

View File

@@ -1,5 +1,13 @@
use std::iter::repeat_n;
use iced::{
Element, Font,
widget::{column, text},
};
use tracing::debug;
pub enum None {}
bitfield::bitfield! {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct DutyVol(u8);
@@ -38,6 +46,8 @@ struct PulseChannel {
cur_time: u16,
cur_length: u8,
cur: u8,
sample: u8,
}
impl PulseChannel {
@@ -50,6 +60,8 @@ impl PulseChannel {
length_timer_high: LengthTimerHigh(0),
cur_time: 0,
cur_length: 0,
cur: 0,
sample: 0,
}
}
fn use_envelope(&self) -> bool {
@@ -61,58 +73,229 @@ impl PulseChannel {
fn timer(&self) -> u16 {
self.timer_low as u16 | ((self.length_timer_high.timer_high() as u16) << 8)
}
fn length(&self) -> u8 {
self.length_timer_high.length()
}
pub fn reset(&mut self) {
// TODO
self.cur_time = self.timer();
self.cur_length = self.length_timer_high.length();
self.cur = 0;
}
pub fn clock(&mut self) {
if self.cur_time == 0 {
self.cur_time = self.timer();
self.cur = (self.cur + 1) % 8;
const DUTY: [[bool; 8]; 4] = [
[false, true, false, false, false, false, false, false],
[false, true, true, false, false, false, false, false],
[false, true, true, true, true, false, false, false],
[true, false, false, true, true, true, true, true],
];
self.sample = if DUTY[self.duty_vol.duty() as usize][self.cur as usize] {
self.volume()
} else {
0x00
};
} else {
self.cur_time -= 1;
}
// TODO
}
pub fn cur_sample(&self) -> u8 {
self.sample
}
pub fn q_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
}
pub fn h_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
self.q_frame_clock();
// if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
// self.cur_length -= 1;
// }
}
pub fn view<T>(&self) -> Element<'_, T> {
text!(
"
Square Channel
"
)
.font(Font::MONOSPACE)
.into()
}
}
bitfield::bitfield! {
pub struct CounterLoad(u8);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct LengthCounter(u8);
impl Debug;
halt, set_halt: 7;
value, set_value: 6, 0;
}
bitfield::bitfield! {
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct LengthLoad(u8);
impl Debug;
load, set_load: 7, 3;
timer_high, set_timer_high: 2, 0;
}
#[derive(Debug, Clone)]
struct TriangleChannel {
enabled: bool,
length: LengthCounter,
timer_low: u8,
length_load: LengthLoad,
reload: bool,
length_counter: u16,
cur: u8,
cur_time: u16,
sample: u8,
}
impl TriangleChannel {
pub fn new() -> Self {
Self {
length: LengthCounter(0),
timer_low: 0,
length_load: LengthLoad(0),
reload: false,
enabled: false,
sample: 0,
cur_time: 0,
cur: 0,
length_counter: 0,
}
}
fn timer(&self) -> u16 {
self.timer_low as u16 | ((self.length_load.timer_high() as u16) << 8)
}
pub fn cur_sample(&self) -> u8 {
self.sample
}
pub fn clock(&mut self) {
if self.length_counter > 0 && self.timer() > 0 {
if self.cur_time == 0 {
self.cur_time = self.timer();
const SAMPLES: [u8; 32] = [
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
];
self.cur = (self.cur + 1) % SAMPLES.len() as u8;
self.sample = SAMPLES[self.cur as usize];
} else {
self.cur_time -= 1;
}
}
}
pub fn q_frame_clock(&mut self) {
if self.reload {
self.length_counter = self.length.value() as u16;
self.reload = self.length.halt();
} else if self.length_counter == 0 {
} else {
self.length_counter -= 1;
}
}
pub fn h_frame_clock(&mut self) {
self.q_frame_clock();
}
pub fn view<T>(&self) -> Element<'_, T> {
text!(
"Triangle Channel
Linear Counter - Reload: {0:>3} ${0:02X}
Linear Counter - Halted: {1}
Period: {2:>3} ${2:04X}
Length Counter - Reload Value: {3:>3} ${3:04X}
Enabled: {4}
Timer: {5:>3} ${5:02X}
Frequency: ???
Sequence Position: {6:>3} ${6:02X}
Length Counter - Counter: {7:>3} ${7:02X}
Linear Counter - Counter: {8:>3} ${8:02X}
Linear Counter - Reload Flag: {9}
Output: {10:>3} ${10:02X}
",
self.length.value(),
self.length.halt(),
self.timer(),
0,
self.enabled,
self.cur_time,
self.cur,
0,
self.length_counter,
self.reload,
self.cur_sample(),
)
.font(Font::MONOSPACE)
.into()
}
}
#[derive(Debug, Clone)]
struct NoiseChannel {
enabled: bool,
sample: u8,
}
impl NoiseChannel {
pub fn new() -> Self {
Self {
enabled: false,
sample: 0,
}
}
pub fn cur_sample(&self) -> u8 {
self.sample
}
pub fn clock(&mut self) {}
pub fn q_frame_clock(&mut self) {}
pub fn h_frame_clock(&mut self) {}
pub fn view<T>(&self) -> Element<'_, T> {
text!("").font(Font::MONOSPACE).into()
}
}
#[derive(Debug, Clone)]
struct DeltaChannel {
enabled: bool,
sample: u8,
}
impl DeltaChannel {
pub fn new() -> Self {
Self {
enabled: false,
sample: 0,
}
}
pub fn cur_sample(&self) -> u8 {
self.sample
}
pub fn clock(&mut self) {}
pub fn q_frame_clock(&mut self) {}
pub fn h_frame_clock(&mut self) {}
pub fn int(&self) -> bool {
false
}
pub fn view<T>(&self) -> Element<'_, T> {
text!("").font(Font::MONOSPACE).into()
}
}
#[derive(Debug, Clone)]
@@ -131,6 +314,8 @@ pub struct APU {
noise: NoiseChannel,
dmc: DeltaChannel,
frame_counter: FrameCounter,
samples: Vec<u8>,
}
impl std::fmt::Debug for APU {
@@ -149,15 +334,17 @@ impl APU {
Self {
pulse_1: PulseChannel::new(),
pulse_2: PulseChannel::new(),
triangle: TriangleChannel { enabled: false },
noise: NoiseChannel { enabled: false },
dmc: DeltaChannel { enabled: false },
triangle: TriangleChannel::new(),
noise: NoiseChannel::new(),
dmc: DeltaChannel::new(),
frame_counter: FrameCounter {
mode_5_step: false,
interrupt_enabled: true,
count: 0,
irq: false,
},
samples: vec![0; 100],
}
}
@@ -201,8 +388,15 @@ impl APU {
self.pulse_2.reset();
}
0x09 => (), // Unused, technically noise channel?
0x08 | 0x0A | 0x0B => {
// TODO: Triangle channel
0x08 => {
self.triangle.length.0 = val;
}
0x0A => {
self.triangle.timer_low = val;
}
0x0B => {
self.triangle.length_load.0 = val;
self.triangle.reload = true;
}
0x0D => (), // Unused, technically noise channel?
0x0C | 0x0E | 0x0F => {
@@ -242,14 +436,41 @@ impl APU {
fn q_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_2.q_frame_clock(); // TODO: clock all
self.pulse_2.q_frame_clock();
self.triangle.q_frame_clock();
self.noise.q_frame_clock();
self.dmc.q_frame_clock();
}
fn h_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_1.h_frame_clock();
self.pulse_2.q_frame_clock();
self.pulse_2.h_frame_clock();
self.triangle.h_frame_clock();
self.noise.h_frame_clock();
self.dmc.h_frame_clock();
}
fn gen_sample(&mut self) {
macro_rules! lut {
($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => {
const $name: [$ty; $len] = {
let mut table = [0; $len];
let mut $n = 0;
while $n < $len {
table[$n] = $expr;
$n += 1;
}
table
};
};
}
lut!(P_LUT: [u8; 32] = |n| (95.52 / (8128.0 / n as f64 + 100.0) * 255.0) as u8);
let pulse_out = P_LUT[(self.pulse_1.cur_sample() + self.pulse_2.cur_sample()) as usize];
lut!(TND_LUT: [u8; 204] = |n| (163.67 / (24329.0 / n as f64 + 100.0) * 255.0) as u8);
let tnd_out = TND_LUT[3 * self.triangle.cur_sample() as usize
+ 2 * self.noise.cur_sample() as usize
+ self.dmc.cur_sample() as usize];
self.samples.push(pulse_out + tnd_out);
}
pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool {
@@ -275,10 +496,27 @@ impl APU {
self.pulse_1.clock();
self.pulse_2.clock();
self.noise.clock();
self.dmc.clock();
}
if ppu_cycle % 3 == 1 {
self.triangle.clock();
}
if ppu_cycle % (6 * 4) == 1 {
self.gen_sample();
}
false
}
pub fn get_frame_samples(&self) -> &[u8] {
// println!("'Frame' of samples: {}", self.samples.len());
&self.samples
}
pub fn reset_frame_samples(&mut self) {
self.samples.clear();
}
pub fn peek_nmi(&self) -> bool {
false
}
@@ -294,4 +532,15 @@ impl APU {
pub fn irq_waiting(&mut self) -> bool {
self.frame_counter.irq
}
pub fn view<'s, T: 's>(&'s self) -> Element<'s, T> {
column![
self.pulse_1.view(),
self.pulse_2.view(),
self.triangle.view(),
self.noise.view(),
self.dmc.view(),
]
.into()
}
}