chore: redo apu sampling
This commit is contained in:
parent
a2d2a84850
commit
7f32073537
182
src/apu.zig
182
src/apu.zig
|
@ -53,7 +53,7 @@ pub const Apu = struct {
|
||||||
.bias = .{ .raw = 0x0200 },
|
.bias = .{ .raw = 0x0200 },
|
||||||
|
|
||||||
.sampling_cycle = 0b00,
|
.sampling_cycle = 0b00,
|
||||||
.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_F32, 2, 1 << 15, SDL.AUDIO_F32, 2, host_sample_rate) orelse unreachable,
|
.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_S16, 2, 1 << 15, SDL.AUDIO_S16, 2, host_sample_rate) orelse unreachable,
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
|
|
||||||
.capacitor = 0,
|
.capacitor = 0,
|
||||||
|
@ -133,64 +133,53 @@ pub const Apu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sampleAudio(self: *Self, late: u64) void {
|
pub fn sampleAudio(self: *Self, late: u64) void {
|
||||||
// zig fmt: off
|
var left: i16 = 0;
|
||||||
const any_ch_enabled = self.ch1.enabled
|
var right: i16 = 0;
|
||||||
or self.ch2.enabled
|
|
||||||
or self.ch3.enabled
|
|
||||||
or self.ch4.enabled;
|
|
||||||
// zig fmt: on
|
|
||||||
|
|
||||||
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: i16 = switch (self.dma_cnt.ch_vol.read()) {
|
||||||
0b00 => 0.25,
|
0b00 => 4,
|
||||||
0b01 => 0.5,
|
0b01 => 2,
|
||||||
0b10 => 0.75,
|
else => 1,
|
||||||
0b11 => 0.0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sample Channel 1
|
left += if (ch_left & 1 == 1) self.ch1.amplitude() else 0;
|
||||||
const ch1_sample = self.highPass(self.ch1.amplitude(), any_ch_enabled);
|
left += if (ch_left >> 1 & 1 == 1) self.ch2.amplitude() else 0;
|
||||||
const ch1_left = if (ch_left & 1 == 1) ch1_sample else 0;
|
left += if (ch_left >> 2 & 1 == 1) self.ch3.amplitude() else 0;
|
||||||
const ch1_right = if (ch_right & 1 == 1) ch1_sample else 0;
|
left += if (ch_left >> 3 == 1) self.ch4.amplitude() else 0;
|
||||||
|
|
||||||
// Sample Channel 2
|
right += if (ch_right & 1 == 1) self.ch1.amplitude() else 0;
|
||||||
const ch2_sample = self.highPass(self.ch2.amplitude(), any_ch_enabled);
|
right += if (ch_right >> 1 & 1 == 1) self.ch2.amplitude() else 0;
|
||||||
const ch2_left = if (ch_left >> 1 & 1 == 1) ch2_sample else 0;
|
right += if (ch_right >> 2 & 1 == 1) self.ch3.amplitude() else 0;
|
||||||
const ch2_right = if (ch_right >> 1 & 1 == 1) ch2_sample else 0;
|
right += if (ch_right >> 3 == 1) self.ch4.amplitude() else 0;
|
||||||
|
|
||||||
// Sample Channel 3
|
left *= 1 + @intCast(i16, self.psg_cnt.left_vol.read());
|
||||||
const ch3_sample = self.highPass(self.ch3.amplitude(), any_ch_enabled);
|
right *= 1 + @intCast(i16, self.psg_cnt.right_vol.read());
|
||||||
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;
|
|
||||||
|
|
||||||
// Sample Channel 4
|
left = @divTrunc(left, gba_vol);
|
||||||
const ch4_sample = self.highPass(self.ch4.amplitude(), any_ch_enabled);
|
right = @divFloor(right, gba_vol);
|
||||||
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 psg_left = (ch1_left + ch2_left + ch3_left + ch4_left) * left_master_vol * gba_vol;
|
// const chA_sample = self.chA.amplitude() >> if (self.dma_cnt.chA_vol.read()) @as(u1, 1) else 0;
|
||||||
const psg_right = (ch1_right + ch2_right + ch3_right + ch4_right) * right_master_vol * gba_vol;
|
// left += if (self.dma_cnt.chA_left.read()) chA_sample else 0;
|
||||||
|
// right += if (self.dma_cnt.chA_right.read()) chA_sample else 0;
|
||||||
|
|
||||||
// Sample Dma Channels
|
// const chB_sample = self.chB.amplitude() >> if (self.dma_cnt.chB_vol.read()) @as(u1, 1) else 0;
|
||||||
const chA_sample = if (self.dma_cnt.chA_vol.read()) self.chA.amplitude() * 4 else self.chA.amplitude() * 2;
|
// left += if (self.dma_cnt.chB_left.read()) chB_sample else 0;
|
||||||
const chA_left = if (self.dma_cnt.chA_left.read()) chA_sample else 0;
|
// right += if (self.dma_cnt.chB_right.read()) chB_sample else 0;
|
||||||
const chA_right = if (self.dma_cnt.chA_right.read()) chA_sample else 0;
|
|
||||||
|
|
||||||
const chB_sample = if (self.dma_cnt.chB_vol.read()) self.chB.amplitude() * 4 else self.chB.amplitude() * 2;
|
// Add SOUNDBIAS
|
||||||
const chB_left = if (self.dma_cnt.chB_left.read()) chB_sample else 0;
|
const bias = @intCast(i16, self.bias.level.read());
|
||||||
const chB_right = if (self.dma_cnt.chB_right.read()) chB_sample else 0;
|
left += bias;
|
||||||
|
right += bias;
|
||||||
|
|
||||||
// Mix all Channels
|
left = std.math.clamp(left, -1024, 1023);
|
||||||
const left = (chA_left + chB_left + psg_left) / 6.0;
|
right = std.math.clamp(right, -1024, 1023);
|
||||||
const right = (chA_right + chB_right + psg_right) / 6.0;
|
|
||||||
|
left = (left << 5) | (left >> 6);
|
||||||
|
right = (right << 5) | (right >> 6);
|
||||||
|
|
||||||
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() });
|
||||||
|
@ -201,15 +190,92 @@ pub const Apu = struct {
|
||||||
defer SDL.SDL_FreeAudioStream(old);
|
defer SDL.SDL_FreeAudioStream(old);
|
||||||
|
|
||||||
self.sampling_cycle = self.bias.sampling_cycle.read();
|
self.sampling_cycle = self.bias.sampling_cycle.read();
|
||||||
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_F32, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_F32, 2, host_sample_rate) orelse unreachable;
|
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_S16, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_S16, 2, host_sample_rate) orelse unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(i16) * 2 * 0x800)) {}
|
||||||
|
|
||||||
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]i16{ left, right }, 2 * @sizeOf(i16));
|
||||||
self.sched.push(.SampleAudio, self.sampleTicks() -| late);
|
self.sched.push(.SampleAudio, self.sampleTicks() -| late);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub fn sampleAudio(self: *Self, late: u64) void {
|
||||||
|
// // zig fmt: off
|
||||||
|
// const any_ch_enabled = self.ch1.enabled
|
||||||
|
// or self.ch2.enabled
|
||||||
|
// or self.ch3.enabled
|
||||||
|
// or self.ch4.enabled;
|
||||||
|
// // zig fmt: on
|
||||||
|
|
||||||
|
// const ch_left: u4 = self.psg_cnt.ch_left.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
|
||||||
|
// const gba_vol: f32 = switch (self.dma_cnt.ch_vol.read()) {
|
||||||
|
// 0b00 => 0.25,
|
||||||
|
// 0b01 => 0.5,
|
||||||
|
// else => 1.0,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Sample Channel 1
|
||||||
|
// 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_right = if (ch_right & 1 == 1) ch1_sample else 0;
|
||||||
|
|
||||||
|
// // Sample Channel 2
|
||||||
|
// 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_right = if (ch_right >> 1 & 1 == 1) ch2_sample else 0;
|
||||||
|
|
||||||
|
// // Sample Channel 3
|
||||||
|
// 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_right = if (ch_right >> 2 & 1 == 1) ch3_sample else 0;
|
||||||
|
|
||||||
|
// // Sample Channel 4
|
||||||
|
// 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_right = if (ch_right >> 3 == 1) ch4_sample else 0;
|
||||||
|
|
||||||
|
// const psg_left = (ch1_left + ch2_left + ch3_left + ch4_left) * left_master_vol * gba_vol;
|
||||||
|
// const psg_right = (ch1_right + ch2_right + ch3_right + ch4_right) * right_master_vol * gba_vol;
|
||||||
|
|
||||||
|
// // Sample Dma Channels
|
||||||
|
// const chA_sample = if (self.dma_cnt.chA_vol.read()) self.chA.amplitude() * 4 else self.chA.amplitude() * 2;
|
||||||
|
// const chA_left = if (self.dma_cnt.chA_left.read()) chA_sample else 0;
|
||||||
|
// const chA_right = if (self.dma_cnt.chA_right.read()) chA_sample else 0;
|
||||||
|
|
||||||
|
// const chB_sample = if (self.dma_cnt.chB_vol.read()) self.chB.amplitude() * 4 else self.chB.amplitude() * 2;
|
||||||
|
// const chB_left = if (self.dma_cnt.chB_left.read()) chB_sample else 0;
|
||||||
|
// const chB_right = if (self.dma_cnt.chB_right.read()) chB_sample else 0;
|
||||||
|
|
||||||
|
// // Mix all Channels
|
||||||
|
// const left = (chA_left + chB_left + psg_left) / 6.0;
|
||||||
|
// const right = (chA_right + chB_right + psg_right) / 6.0;
|
||||||
|
|
||||||
|
// if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
||||||
|
// log.info("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
||||||
|
|
||||||
|
// // Sample Rate Changed, Create a new Resampler since i can't figure out how to change
|
||||||
|
// // the parameters of the old one
|
||||||
|
// const old = self.stream;
|
||||||
|
// defer SDL.SDL_FreeAudioStream(old);
|
||||||
|
|
||||||
|
// self.sampling_cycle = self.bias.sampling_cycle.read();
|
||||||
|
// self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_F32, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_F32, 2, host_sample_rate) orelse unreachable;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
||||||
|
|
||||||
|
// _ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
||||||
|
// self.sched.push(.SampleAudio, self.sampleTicks() -| late);
|
||||||
|
// }
|
||||||
|
|
||||||
fn sampleTicks(self: *const Self) u64 {
|
fn sampleTicks(self: *const Self) u64 {
|
||||||
return (1 << 24) / self.sampleRate();
|
return (1 << 24) / self.sampleRate();
|
||||||
}
|
}
|
||||||
|
@ -392,8 +458,8 @@ const ToneSweep = struct {
|
||||||
self.sample = if (self.enabled) self.square.sample(self.duty) * self.env_dev.vol else 0;
|
self.sample = if (self.enabled) self.square.sample(self.duty) * self.env_dev.vol else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amplitude(self: *const Self) f32 {
|
fn amplitude(self: *const Self) i16 {
|
||||||
return (@intToFloat(f32, self.sample) / 7.5) - 1.0;
|
return @intCast(i16, self.sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NR11, NR12
|
/// NR11, NR12
|
||||||
|
@ -526,8 +592,8 @@ const Tone = struct {
|
||||||
self.sample = if (self.enabled) self.square.sample(self.duty) * self.env_dev.vol else 0;
|
self.sample = if (self.enabled) self.square.sample(self.duty) * self.env_dev.vol else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amplitude(self: *const Self) f32 {
|
fn amplitude(self: *const Self) i16 {
|
||||||
return (@intToFloat(f32, self.sample) / 7.5) - 1.0;
|
return @intCast(i16, self.sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NR21, NR22
|
/// NR21, NR22
|
||||||
|
@ -699,8 +765,8 @@ const Wave = struct {
|
||||||
self.sample = if (self.enabled) self.wave_dev.sample(self.select) >> self.wave_dev.shift(self.vol) else 0;
|
self.sample = if (self.enabled) self.wave_dev.sample(self.select) >> self.wave_dev.shift(self.vol) else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amplitude(self: *const Self) f32 {
|
fn amplitude(self: *const Self) i16 {
|
||||||
return (@intToFloat(f32, self.sample) / 7.5) - 1.0;
|
return @intCast(i16, self.sample);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -824,8 +890,8 @@ const Noise = struct {
|
||||||
self.sample = if (self.enabled) self.lfsr.sample() * self.env_dev.vol else 0;
|
self.sample = if (self.enabled) self.lfsr.sample() * self.env_dev.vol else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn amplitude(self: *const Self) f32 {
|
fn amplitude(self: *const Self) i16 {
|
||||||
return (@intToFloat(f32, self.sample) / 7.5) - 1.0;
|
return @intCast(i16, self.sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isDacEnabled(self: *const Self) bool {
|
fn isDacEnabled(self: *const Self) bool {
|
||||||
|
@ -861,8 +927,8 @@ pub fn DmaSound(comptime kind: DmaSoundKind) type {
|
||||||
if (self.fifo.readItem()) |sample| self.sample = @bitCast(i8, sample);
|
if (self.fifo.readItem()) |sample| self.sample = @bitCast(i8, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn amplitude(self: *const Self) f32 {
|
pub fn amplitude(self: *const Self) i16 {
|
||||||
return @intToFloat(f32, self.sample) / 127.5 - (1 / 255);
|
return @as(i16,self.sample);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ fn initAudio(apu: *Apu) SDL.SDL_AudioDeviceID {
|
||||||
var have: SDL.SDL_AudioSpec = undefined;
|
var have: SDL.SDL_AudioSpec = undefined;
|
||||||
var want: SDL.SDL_AudioSpec = .{
|
var want: SDL.SDL_AudioSpec = .{
|
||||||
.freq = sample_rate,
|
.freq = sample_rate,
|
||||||
.format = SDL.AUDIO_F32,
|
.format = SDL.AUDIO_S16,
|
||||||
.channels = 2,
|
.channels = 2,
|
||||||
.samples = 0x100,
|
.samples = 0x100,
|
||||||
.callback = audioCallback,
|
.callback = audioCallback,
|
||||||
|
|
Loading…
Reference in New Issue