Skip to content

Commit

Permalink
audio code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
floooh committed Aug 3, 2024
1 parent 3336545 commit 33d6ce3
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 292 deletions.
14 changes: 1 addition & 13 deletions src/chips/ay3891.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
const bitutils = @import("common").bitutils;
const mask = bitutils.mask;
const maskm = bitutils.maskm;
const filter = @import("common").filter;

/// map chip pin names to bit positions
///
Expand Down Expand Up @@ -46,8 +45,6 @@ pub const TypeConfig = struct {
model: Model = .AY38910,
pins: Pins,
bus: type,
dcadjust_buf_len: u32 = 128,
enable_lowpass_filter: bool = true,
};

pub fn Type(comptime cfg: TypeConfig) type {
Expand All @@ -58,7 +55,6 @@ pub fn Type(comptime cfg: TypeConfig) type {
pub const Options = struct {
tick_hz: u32, // frequency at which the tick function will be called
sound_hz: u32, // host sound frequency (number of samples per second)
volume: f32 = 1.0, // output volume (0..1)
chip_select: u4 = 0, // optional chip-select
};

Expand Down Expand Up @@ -176,14 +172,8 @@ pub fn Type(comptime cfg: TypeConfig) type {
pub const Sample = struct {
period: i32 = 0,
counter: i32 = 0,
volume: f32 = 0.0,
out: f32 = 0.0, // output sample value
ready: bool = false, // true if a new sample value is ready
filter: filter.Type(.{
.enable_dcadjust = true,
.enable_lowpass_filter = cfg.enable_lowpass_filter,
.dcadjust_buf_len = cfg.dcadjust_buf_len,
}) = .{},
};

tick_count: u32 = 0, // tick counter for internal clock division
Expand Down Expand Up @@ -236,7 +226,6 @@ pub fn Type(comptime cfg: TypeConfig) type {
.sample = .{
.period = sample_period,
.counter = sample_period,
.volume = opts.volume,
},
};
self.updateValues();
Expand All @@ -246,7 +235,6 @@ pub fn Type(comptime cfg: TypeConfig) type {

pub fn reset(self: *Self) void {
self.active = false;
self.sample.filter.reset();
self.addr = 0;
self.tick_count = 0;
for (&self.regs) |r| {
Expand Down Expand Up @@ -349,7 +337,7 @@ pub fn Type(comptime cfg: TypeConfig) type {
}
}
}
self.sample.out = self.sample.filter.put(sm) * self.sample.volume;
self.sample.out = sm;
self.sample.ready = true;
} else {
self.sample.ready = false;
Expand Down
66 changes: 66 additions & 0 deletions src/common/Beeper.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const Self = @This();

pub const Options = struct {
tick_hz: u32,
sound_hz: u32,
};

const OVERSAMPLE_PERIOD = 16; // anti-aliasing oversampling
const OVERSAMPLE_MUL: f32 = 1.0 / @as(f32, @floatFromInt(OVERSAMPLE_PERIOD));
const FIXEDPOINT_SCALE = 256; // error-reduction for sample period counter

state: u1 = 0,
period: i32 = 0,
counter: i32 = 0,
oversample_counter: u8 = OVERSAMPLE_PERIOD,
oversample_accum: f32 = 0.0, // oversampling accumulator for anti-aliasing
volume: f32 = 1.0,
sample: struct {
ready: bool = false,
out: f32 = 0.0,
} = .{},

pub fn init(opts: Options) Self {
const period: i32 = @intCast((opts.tick_hz * FIXEDPOINT_SCALE) / (opts.sound_hz * OVERSAMPLE_PERIOD));
return .{
.period = period,
.counter = period,
.oversample_counter = OVERSAMPLE_PERIOD,
};
}

pub fn reset(self: *Self) void {
self.state = 0;
self.counter = self.period;
self.sample = .{};
}

pub fn setVolume(self: *Self, v: f32) void {
self.volume = v;
}

pub fn toggle(self: *Self) void {
self.state ^= 1;
}

pub fn set(self: *Self, on: bool) void {
self.state = if (on) 1 else 0;
}

pub fn tick(self: *Self) bool {
self.counter -= FIXEDPOINT_SCALE;
if (self.counter <= 0) {
self.counter += self.period;
self.oversample_accum += @as(f32, @floatFromInt(self.state)) * self.volume;
self.oversample_counter -= 1;
if (self.oversample_counter == 0) {
// new sample ready
self.oversample_counter = OVERSAMPLE_PERIOD;
const s = self.oversample_accum * OVERSAMPLE_MUL;
self.sample.out = s;
self.oversample_accum = 0;
return true;
}
}
return false;
}
105 changes: 105 additions & 0 deletions src/common/audio.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//! support code to bind emulator audio output to the host audio system
const std = @import("std");
const assert = std.debug.assert;

/// comptime type configuration
pub const TypeConfig = struct {
/// number of generated samples before audio callback is called
num_samples: u32 = 128,
/// number of voices in the input sample
num_voices: u32,
/// enable/disable DC-adjustment
dcadjust_enable: bool = true,
/// enable/disable lowpass filter
lowpass_filter_enable: bool = true,
/// dc-adjustment buffer size
dcadjust_buf_len: u32 = 128,
};

pub fn Type(cfg: TypeConfig) type {
assert(std.math.isPowerOfTwo(cfg.num_samples));
assert(std.math.isPowerOfTwo(cfg.dcadjust_buf_len));
assert(cfg.num_voices > 0);

return struct {
const Self = @This();

const voice_volume: f32 = 1.0 / @as(f32, @floatFromInt(cfg.num_voices));

/// called when intermediate sample buffer is full
pub const Callback = ?*const fn (samples: []f32) void;

/// runtime audio options
pub const Options = struct {
/// host audio frequency in Hz
sample_rate: i32,
/// output volume modulator (0..1)
volume: f32 = 0.75,
/// called when new chunk of audio data is ready
callback: Callback,
};

volume: f32,
callback: Callback,
pos: u32 = 0,
filter: struct {
lopass: [2]f32 = [_]f32{0} ** 2,
dcadj: struct {
sum: f32 = 0,
pos: u32 = 0,
buf: [cfg.dcadjust_buf_len]f32 = [_]f32{0} ** cfg.dcadjust_buf_len,
} = .{},
} = .{},
buf: [cfg.num_samples]f32,

pub fn init(opts: Options) Self {
return .{
.volume = opts.volume,
.callback = opts.callback,
.buf = std.mem.zeroes([cfg.num_samples]f32),
};
}

pub fn reset(self: *Self) void {
self.pos = 0;
self.buf = std.mem.zeroes(@TypeOf(self.buf));
self.filter = .{};
}

pub fn put(self: *Self, sample: f32) void {
var s = sample * self.volume * voice_volume;
if (cfg.dcadjust_enable) {
s = self.dcAdjust(s);
}
if (cfg.lowpass_filter_enable) {
s = self.lowPassFilter(s);
}
s = std.math.clamp(s, -1.0, 1.0);
self.buf[self.pos] = s;
self.pos += 1;
if (self.pos == cfg.num_samples) {
if (self.callback) |cb| {
cb(&self.buf);
}
self.pos = 0;
}
}

fn dcAdjust(self: *Self, in: f32) f32 {
const pos = self.filter.dcadj.pos;
self.filter.dcadj.sum -= self.filter.dcadj.buf[pos];
self.filter.dcadj.sum += in;
self.filter.dcadj.buf[pos] = in;
self.filter.dcadj.pos = (pos + 1) & (cfg.dcadjust_buf_len - 1);
const div: f32 = @floatFromInt(cfg.dcadjust_buf_len);
return in - (self.filter.dcadj.sum / div);
}

fn lowPassFilter(self: *Self, in: f32) f32 {
const out = self.filter.lopass[0] * 0.25 + self.filter.lopass[1] * 0.5 + in * 0.25;
self.filter.lopass[0] = self.filter.lopass[1];
self.filter.lopass[1] = in;
return out;
}
};
}
86 changes: 0 additions & 86 deletions src/common/beeper.zig

This file was deleted.

4 changes: 2 additions & 2 deletions src/common/common.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub const bitutils = @import("bitutils.zig");
pub const memory = @import("memory.zig");
pub const glue = @import("glue.zig");
pub const clock = @import("clock.zig");
pub const filter = @import("filter.zig");
pub const utils = @import("utils.zig");
pub const keybuf = @import("keybuf.zig");
pub const beeper = @import("beeper.zig");
pub const audio = @import("audio.zig");
pub const Beeper = @import("Beeper.zig");
57 changes: 0 additions & 57 deletions src/common/filter.zig

This file was deleted.

Loading

0 comments on commit 33d6ce3

Please sign in to comment.