chore: improve APU accuracy + scheduler refactoring
This commit is contained in:
parent
e7777737b3
commit
af2ad6c924
66
src/apu.zig
66
src/apu.zig
|
@ -60,12 +60,12 @@ pub const Apu = struct {
|
||||||
.fs = FrameSequencer.init(),
|
.fs = FrameSequencer.init(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sched.push(.SampleAudio, sched.now() + apu.sampleTicks());
|
sched.push(.SampleAudio, apu.sampleTicks());
|
||||||
sched.push(.{ .ApuChannel = 0 }, sched.now() + SquareWave.ticks); // Channel 1
|
sched.push(.{ .ApuChannel = 0 }, SquareWave.tickInterval); // Channel 1
|
||||||
sched.push(.{ .ApuChannel = 1 }, sched.now() + SquareWave.ticks); // Channel 2
|
sched.push(.{ .ApuChannel = 1 }, SquareWave.tickInterval); // Channel 2
|
||||||
sched.push(.{ .ApuChannel = 2 }, sched.now() + WaveDevice.ticks); // Channel 3
|
sched.push(.{ .ApuChannel = 2 }, WaveDevice.tickInterval); // Channel 3
|
||||||
sched.push(.{ .ApuChannel = 3 }, sched.now() + Noise.ticks); // Channel 4
|
sched.push(.{ .ApuChannel = 3 }, Lfsr.tickInterval); // Channel 4
|
||||||
sched.push(.FrameSequencer, sched.now() + ((1 << 24) / 512));
|
sched.push(.FrameSequencer, ((1 << 24) / 512));
|
||||||
|
|
||||||
return apu;
|
return apu;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,11 @@ pub const Apu = struct {
|
||||||
const ch_left: u4 = self.psg_cnt.ch_left.read();
|
const ch_left: u4 = self.psg_cnt.ch_left.read();
|
||||||
const ch_right: u4 = self.psg_cnt.ch_right.read();
|
const ch_right: u4 = self.psg_cnt.ch_right.read();
|
||||||
|
|
||||||
|
// FIXME: Obscure behaviour?
|
||||||
|
// Apply NR50 Volume Modifications
|
||||||
|
const left_master_vol = (@intToFloat(f32, self.psg_cnt.left_vol.read()) + 1.0) / 7.0;
|
||||||
|
const right_master_vol = (@intToFloat(f32, self.psg_cnt.right_vol.read()) + 1.0) / 7.0;
|
||||||
|
|
||||||
// Apply SOUNDCNT_H Volume Modifications
|
// Apply SOUNDCNT_H Volume Modifications
|
||||||
const gba_vol: f32 = switch (self.dma_cnt.ch_vol.read()) {
|
const gba_vol: f32 = switch (self.dma_cnt.ch_vol.read()) {
|
||||||
0b00 => 0.25,
|
0b00 => 0.25,
|
||||||
|
@ -152,35 +157,27 @@ pub const Apu = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sample Channel 1
|
// Sample Channel 1
|
||||||
const ch1_sample = self.highPass(self.ch1.amplitude(), any_ch_enabled) * gba_vol;
|
const ch1_sample = self.highPass(self.ch1.amplitude(), any_ch_enabled);
|
||||||
const ch1_left = if (ch_left & 1 == 1) ch1_sample else 0;
|
const ch1_left = if (ch_left & 1 == 1) ch1_sample else 0;
|
||||||
const ch1_right = if (ch_right & 1 == 1) ch1_sample else 0;
|
const ch1_right = if (ch_right & 1 == 1) ch1_sample else 0;
|
||||||
|
|
||||||
// Sample Channel 2
|
// Sample Channel 2
|
||||||
const ch2_sample = self.highPass(self.ch2.amplitude(), any_ch_enabled) * gba_vol;
|
const ch2_sample = self.highPass(self.ch2.amplitude(), any_ch_enabled);
|
||||||
const ch2_left = if (ch_left >> 1 & 1 == 1) ch2_sample else 0;
|
const ch2_left = if (ch_left >> 1 & 1 == 1) ch2_sample else 0;
|
||||||
const ch2_right = if (ch_right >> 1 & 1 == 1) ch2_sample else 0;
|
const ch2_right = if (ch_right >> 1 & 1 == 1) ch2_sample else 0;
|
||||||
|
|
||||||
// Sample Channel 3
|
// Sample Channel 3
|
||||||
const ch3_sample = self.highPass(self.ch3.amplitude(), any_ch_enabled) * gba_vol;
|
const ch3_sample = self.highPass(self.ch3.amplitude(), any_ch_enabled);
|
||||||
const ch3_left = if (ch_left >> 2 & 1 == 1) ch3_sample else 0;
|
const ch3_left = if (ch_left >> 2 & 1 == 1) ch3_sample else 0;
|
||||||
const ch3_right = if (ch_right >> 2 & 1 == 1) ch3_sample else 0;
|
const ch3_right = if (ch_right >> 2 & 1 == 1) ch3_sample else 0;
|
||||||
|
|
||||||
// Sample Channel 4
|
// Sample Channel 4
|
||||||
const ch4_sample = self.highPass(self.ch4.amplitude(), any_ch_enabled) * gba_vol;
|
const ch4_sample = self.highPass(self.ch4.amplitude(), any_ch_enabled);
|
||||||
const ch4_left = if (ch_left >> 3 == 1) ch4_sample else 0;
|
const ch4_left = if (ch_left >> 3 == 1) ch4_sample else 0;
|
||||||
const ch4_right = if (ch_right >> 3 == 1) ch4_sample else 0;
|
const ch4_right = if (ch_right >> 3 == 1) ch4_sample else 0;
|
||||||
|
|
||||||
const mixed_left = ch1_left + ch2_left + ch3_left + ch4_left / 4;
|
const psg_left = (ch1_left + ch2_left + ch3_left + ch4_left) * left_master_vol * gba_vol;
|
||||||
const mixed_right = ch1_right + ch2_right + ch3_right + ch4_right / 4;
|
const psg_right = (ch1_right + ch2_right + ch3_right + ch4_right) * right_master_vol * gba_vol;
|
||||||
|
|
||||||
// FIXME: Obscure behaviour?
|
|
||||||
// Apply NR50 Volume Modifications
|
|
||||||
const left_master_vol = (@intToFloat(f32, self.psg_cnt.left_vol.read()) + 1.0) / 7;
|
|
||||||
const right_master_vol = (@intToFloat(f32, self.psg_cnt.right_vol.read()) + 1.0) / 7;
|
|
||||||
|
|
||||||
const psg_left = mixed_left * left_master_vol;
|
|
||||||
const psg_right = mixed_right * right_master_vol;
|
|
||||||
|
|
||||||
// Sample Dma Channels
|
// Sample Dma Channels
|
||||||
const chA_sample = if (self.dma_cnt.chA_vol.read()) self.chA.amplitude() * 4 else self.chA.amplitude() * 2;
|
const chA_sample = if (self.dma_cnt.chA_vol.read()) self.chA.amplitude() * 4 else self.chA.amplitude() * 2;
|
||||||
|
@ -192,8 +189,8 @@ pub const Apu = struct {
|
||||||
const chB_right = if (self.dma_cnt.chB_right.read()) chB_sample else 0;
|
const chB_right = if (self.dma_cnt.chB_right.read()) chB_sample else 0;
|
||||||
|
|
||||||
// Mix all Channels
|
// Mix all Channels
|
||||||
const left = (chA_left + chB_left + psg_left) / 3;
|
const left = (chA_left + chB_left + psg_left) / 6.0;
|
||||||
const right = (chA_right + chB_right + psg_right) / 3;
|
const right = (chA_right + chB_right + psg_right) / 6.0;
|
||||||
|
|
||||||
if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
||||||
log.info("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
log.info("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
||||||
|
@ -210,7 +207,7 @@ pub const Apu = struct {
|
||||||
while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
||||||
|
|
||||||
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
||||||
self.sched.push(.SampleAudio, self.sched.now() + self.sampleTicks() - late);
|
self.sched.push(.SampleAudio, self.sampleTicks() -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampleTicks(self: *const Self) u64 {
|
fn sampleTicks(self: *const Self) u64 {
|
||||||
|
@ -235,7 +232,7 @@ pub const Apu = struct {
|
||||||
1, 3, 5 => {},
|
1, 3, 5 => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sched.push(.FrameSequencer, self.sched.now() + ((1 << 24) / 512) - late);
|
self.sched.push(.FrameSequencer, ((1 << 24) / 512) -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tickLengths(self: *Self) void {
|
fn tickLengths(self: *Self) void {
|
||||||
|
@ -709,7 +706,6 @@ const Wave = struct {
|
||||||
|
|
||||||
const Noise = struct {
|
const Noise = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const ticks = (1 << 24) / (1 << 22);
|
|
||||||
|
|
||||||
/// Write-only
|
/// Write-only
|
||||||
/// NR41
|
/// NR41
|
||||||
|
@ -854,7 +850,7 @@ pub fn DmaSound(comptime kind: DmaSoundKind) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(self: *Self, value: u32) void {
|
pub fn push(self: *Self, value: u32) void {
|
||||||
self.fifo.write(&intToBytes(u32, value)) catch {};
|
self.fifo.write(&intToBytes(u32, value)) catch |e| log.err("{} Error: {}", .{ kind, e });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(self: *const Self) usize {
|
pub fn len(self: *const Self) usize {
|
||||||
|
@ -951,7 +947,7 @@ const EnvelopeDevice = struct {
|
||||||
const WaveDevice = struct {
|
const WaveDevice = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const wave_len = 0x20;
|
const wave_len = 0x20;
|
||||||
const ticks = (1 << 24) / (1 << 22);
|
const tickInterval: u64 = (1 << 24) / (1 << 22);
|
||||||
|
|
||||||
buf: [wave_len]u8,
|
buf: [wave_len]u8,
|
||||||
timer: u16,
|
timer: u16,
|
||||||
|
@ -973,7 +969,7 @@ const WaveDevice = struct {
|
||||||
const timer = (2048 - @as(u64, value)) * 4;
|
const timer = (2048 - @as(u64, value)) * 4;
|
||||||
self.timer = @truncate(u11, timer);
|
self.timer = @truncate(u11, timer);
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = 2 }, self.sched.now() + timer * ticks);
|
self.sched.push(.{ .ApuChannel = 2 }, timer * tickInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleTimerOverflow(self: *Self, cnt_freq: io.Frequency, cnt_sel: io.WaveSelect, late: u64) void {
|
fn handleTimerOverflow(self: *Self, cnt_freq: io.Frequency, cnt_sel: io.WaveSelect, late: u64) void {
|
||||||
|
@ -987,7 +983,7 @@ const WaveDevice = struct {
|
||||||
self.offset = (self.offset + 1) % 0x20; // 0x10 bytes, which contain 2 samples each
|
self.offset = (self.offset + 1) % 0x20; // 0x10 bytes, which contain 2 samples each
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = 2 }, self.sched.now() + timer * ticks - late);
|
self.sched.push(.{ .ApuChannel = 2 }, timer * tickInterval -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(self: *const Self, cnt: io.WaveSelect) u4 {
|
fn sample(self: *const Self, cnt: io.WaveSelect) u4 {
|
||||||
|
@ -1046,7 +1042,7 @@ const WaveDevice = struct {
|
||||||
|
|
||||||
const SquareWave = struct {
|
const SquareWave = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const ticks: u64 = (1 << 24) / (1 << 22);
|
const tickInterval: u64 = (1 << 24) / (1 << 22);
|
||||||
|
|
||||||
pos: u3,
|
pos: u3,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
|
@ -1098,7 +1094,7 @@ const SquareWave = struct {
|
||||||
self.timer = @truncate(u12, timer);
|
self.timer = @truncate(u12, timer);
|
||||||
self.pos +%= 1;
|
self.pos +%= 1;
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = if (kind == .Ch1) 0 else 1 }, self.sched.now() + timer * ticks - late);
|
self.sched.push(.{ .ApuChannel = if (kind == .Ch1) 0 else 1 }, timer * tickInterval -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloadTimer(self: *Self, comptime kind: ChannelKind, value: u11) void {
|
fn reloadTimer(self: *Self, comptime kind: ChannelKind, value: u11) void {
|
||||||
|
@ -1108,7 +1104,7 @@ const SquareWave = struct {
|
||||||
const timer = (tmp & ~@as(u64, 0x3)) | self.timer & 0x3; // Keep the last two bits from the old timer
|
const timer = (tmp & ~@as(u64, 0x3)) | self.timer & 0x3; // Keep the last two bits from the old timer
|
||||||
self.timer = @truncate(u12, timer);
|
self.timer = @truncate(u12, timer);
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = if (kind == .Ch1) 0 else 1 }, self.sched.now() + timer * ticks);
|
self.sched.push(.{ .ApuChannel = if (kind == .Ch1) 0 else 1 }, timer * tickInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(self: *const Self, cnt: io.Duty) u1 {
|
fn sample(self: *const Self, cnt: io.Duty) u1 {
|
||||||
|
@ -1129,7 +1125,7 @@ const SquareWave = struct {
|
||||||
// Linear Feedback Shift Register
|
// Linear Feedback Shift Register
|
||||||
const Lfsr = struct {
|
const Lfsr = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const ticks = (1 << 24) / (1 << 22);
|
const tickInterval: u64 = (1 << 24) / (1 << 22);
|
||||||
|
|
||||||
shift: u15,
|
shift: u15,
|
||||||
timer: u16,
|
timer: u16,
|
||||||
|
@ -1169,7 +1165,7 @@ const Lfsr = struct {
|
||||||
const div = Self.divisor(poly.div_ratio.read());
|
const div = Self.divisor(poly.div_ratio.read());
|
||||||
const timer = @as(u64, div << poly.shift.read());
|
const timer = @as(u64, div << poly.shift.read());
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = 3 }, self.sched.now() + timer * ticks);
|
self.sched.push(.{ .ApuChannel = 3 }, timer * tickInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleTimerOverflow(self: *Self, poly: io.PolyCounter, late: u64) void {
|
fn handleTimerOverflow(self: *Self, poly: io.PolyCounter, late: u64) void {
|
||||||
|
@ -1182,7 +1178,7 @@ const Lfsr = struct {
|
||||||
if (poly.width.read())
|
if (poly.width.read())
|
||||||
self.shift = (self.shift & ~@as(u15, 0x40)) | tmp << 6;
|
self.shift = (self.shift & ~@as(u15, 0x40)) | tmp << 6;
|
||||||
|
|
||||||
self.sched.push(.{ .ApuChannel = 3 }, self.sched.now() + timer * ticks - late);
|
self.sched.push(.{ .ApuChannel = 3 }, timer * tickInterval -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn divisor(code: u3) u16 {
|
fn divisor(code: u3) u16 {
|
||||||
|
|
|
@ -126,7 +126,7 @@ fn Timer(comptime id: u2) type {
|
||||||
const when = (@as(u64, 0x10000) - self._counter) * self.frequency();
|
const when = (@as(u64, 0x10000) - self._counter) * self.frequency();
|
||||||
|
|
||||||
self._start_timestamp = self.sched.now();
|
self._start_timestamp = self.sched.now();
|
||||||
self.sched.push(.{ .TimerOverflow = id }, self.sched.now() + when - late);
|
self.sched.push(.{ .TimerOverflow = id }, when -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frequency(self: *const Self) u16 {
|
fn frequency(self: *const Self) u16 {
|
||||||
|
|
|
@ -269,8 +269,8 @@ pub const Arm7tdmi = struct {
|
||||||
|
|
||||||
pub fn handleDMATransfers(self: *Self) void {
|
pub fn handleDMATransfers(self: *Self) void {
|
||||||
while (self.bus.isDmaRunning()) {
|
while (self.bus.isDmaRunning()) {
|
||||||
if (self.bus.dma[1].step(self)) continue;
|
|
||||||
if (self.bus.dma[0].step(self)) continue;
|
if (self.bus.dma[0].step(self)) continue;
|
||||||
|
if (self.bus.dma[1].step(self)) continue;
|
||||||
if (self.bus.dma[2].step(self)) continue;
|
if (self.bus.dma[2].step(self)) continue;
|
||||||
if (self.bus.dma[3].step(self)) continue;
|
if (self.bus.dma[3].step(self)) continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler) !Self {
|
||||||
// Queue first Hblank
|
// Queue first Hblank
|
||||||
sched.push(.Draw, sched.tick + (240 * 4));
|
sched.push(.Draw, 240 * 4);
|
||||||
|
|
||||||
const framebufs = try alloc.alloc(u8, (framebuf_pitch * height) * 2);
|
const framebufs = try alloc.alloc(u8, (framebuf_pitch * height) * 2);
|
||||||
std.mem.set(u8, framebufs, 0);
|
std.mem.set(u8, framebufs, 0);
|
||||||
|
@ -429,7 +429,7 @@ pub const Ppu = struct {
|
||||||
pollBlankingDma(&cpu.bus, .HBlank);
|
pollBlankingDma(&cpu.bus, .HBlank);
|
||||||
|
|
||||||
self.dispstat.hblank.set();
|
self.dispstat.hblank.set();
|
||||||
self.sched.push(.HBlank, self.sched.now() + (68 * 4) - late);
|
self.sched.push(.HBlank, 68 * 4 -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleHBlankEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void {
|
pub fn handleHBlankEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void {
|
||||||
|
@ -451,7 +451,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
if (scanline < 160) {
|
if (scanline < 160) {
|
||||||
// Transitioning to another Draw
|
// Transitioning to another Draw
|
||||||
self.sched.push(.Draw, self.sched.now() + (240 * 4) - late);
|
self.sched.push(.Draw, 240 * 4 -| late);
|
||||||
} else {
|
} else {
|
||||||
// Transitioning to a Vblank
|
// Transitioning to a Vblank
|
||||||
if (scanline == 160) {
|
if (scanline == 160) {
|
||||||
|
@ -469,7 +469,7 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanline == 227) self.dispstat.vblank.unset();
|
if (scanline == 227) self.dispstat.vblank.unset();
|
||||||
self.sched.push(.VBlank, self.sched.now() + (240 * 4) - late);
|
self.sched.push(.VBlank, 240 * 4 -| late);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) Self {
|
pub fn init(alloc: Allocator) Self {
|
||||||
var sched = Self{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(alloc, {}) };
|
var sched = Self{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(alloc, {}) };
|
||||||
sched.push(.HeatDeath, std.math.maxInt(u64));
|
sched.queue.add(.{ .kind = .HeatDeath, .tick = std.math.maxInt(u64) }) catch unreachable;
|
||||||
|
|
||||||
return sched;
|
return sched;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ pub const Scheduler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(self: *Self, kind: EventKind, end: u64) void {
|
pub fn push(self: *Self, kind: EventKind, end: u64) void {
|
||||||
self.queue.add(.{ .kind = kind, .tick = end }) catch unreachable;
|
self.queue.add(.{ .kind = kind, .tick = self.now() + end }) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nextTimestamp(self: *Self) u64 {
|
pub fn nextTimestamp(self: *Self) u64 {
|
||||||
|
|
Loading…
Reference in New Issue