diff --git a/build.zig b/build.zig index cdcbd5e..2a6487c 100644 --- a/build.zig +++ b/build.zig @@ -91,8 +91,8 @@ pub fn build(b: *Build) void { } inline for (.{ - "neopixels", - "song", + // "neopixels", + // "song", }) |name| { const mvp = add_cart(&dep, b, .{ .name = std.fmt.comptimePrint("badge.demo.{s}", .{name}), @@ -144,7 +144,7 @@ pub const Cart = struct { pub fn install_with_watcher(c: *const Cart, d: *Build.Dependency, b: *Build, opt: CartWatcherOptions) *Build.Step.Run { if (opt.build_firmware) { - // c.mz.install_firmware(b, c.fw, .{ .format = .{ .uf2 = .SAMD51 } }); + c.mz.install_firmware(b, c.fw, .{ .format = .{ .uf2 = .SAMD51 } }); } const install_artifact_step = b.addInstallArtifact(c.wasm, .{}); b.getInstallStep().dependOn(&install_artifact_step.step); diff --git a/simulator/src/apu-worklet.ts b/simulator/src/apu-worklet.ts index ab78b3e..253c4cb 100644 --- a/simulator/src/apu-worklet.ts +++ b/simulator/src/apu-worklet.ts @@ -11,6 +11,7 @@ class APUProcessor extends AudioWorkletProcessor { if (this.port != null) { this.port.onmessage = (event: MessageEvent<"reset" | {left: number[], right: number[]}>) => { if (event.data === "reset") { + console.log("Reset"); this.samplesLeft = []; this.samplesRight = []; } else { @@ -33,6 +34,8 @@ class APUProcessor extends AudioWorkletProcessor { outputRight[index] = pcmRight[index] / 32767; } + console.log(pcmLeft.length); + return true; } } diff --git a/simulator/src/runtime.ts b/simulator/src/runtime.ts index 779cc25..d18d908 100644 --- a/simulator/src/runtime.ts +++ b/simulator/src/runtime.ts @@ -240,15 +240,16 @@ export class Runtime { // TODO: should this be called via a message from the worklet maybe? let audio_function = this.wasm!.exports["audio"] as any; - // if (typeof audio_function === "function") { - // this.bluescreenOnError(audio_function); - // } - - if (audio_function(constants.ADDR_AUDIO_BUFFER)) { - this.apu.send( - [...new Int16Array(this.memory.buffer.slice(constants.ADDR_AUDIO_BUFFER)).slice(0, 512)], - [...new Int16Array(this.memory.buffer.slice(constants.ADDR_AUDIO_BUFFER + 512 * 2)).slice(0, 512)], - ); + + if (typeof update_function === "function") { + // this is terrible + for (let i = 0; i < 2; i++) { + this.bluescreenOnError(audio_function); + this.apu.send( + [...new Int16Array(this.memory.buffer.slice(constants.ADDR_AUDIO_BUFFER)).slice(0, 512)], + [...new Int16Array(this.memory.buffer.slice(constants.ADDR_AUDIO_BUFFER + 512 * 2)).slice(0, 512)], + ); + } } } diff --git a/src/badge.zig b/src/badge.zig index 1eea34b..1b7d651 100644 --- a/src/badge.zig +++ b/src/badge.zig @@ -216,7 +216,7 @@ pub fn main() !void { }); timer.init(); - audio.init(); + audio.init(&cart.call_audio); // Light sensor adc microzig.board.A6_LIGHT.set_mux(.B); diff --git a/src/badge/cart.zig b/src/badge/cart.zig index c354a83..1e9ba2c 100644 --- a/src/badge/cart.zig +++ b/src/badge/cart.zig @@ -17,6 +17,8 @@ const libcart = struct { extern fn start() void; extern fn update() void; + extern fn audio() void; + export fn __return_thunk__() linksection(".text.cart") noreturn { asm volatile (" svc #12"); unreachable; @@ -123,7 +125,7 @@ pub const HSRAM = struct { }; pub fn start() void { - @memset(@as(*[0xA020]u8, @ptrFromInt(0x20000000)), 0); + @memset(@as(*[0xA820]u8, @ptrFromInt(0x20000000)), 0); // fill .bss with zeroes { @@ -188,6 +190,10 @@ fn call(func: *const fn () callconv(.C) void) void { }); } +pub fn call_audio() void { + call(libcart.audio); +} + fn User(comptime T: type) type { return extern struct { const Self = @This(); diff --git a/src/badge/feature_test.zig b/src/badge/feature_test.zig index b23aad7..fa04fe5 100644 --- a/src/badge/feature_test.zig +++ b/src/badge/feature_test.zig @@ -155,17 +155,15 @@ export fn update() void { }); } -const raw_data = std.mem.bytesAsSlice(i16, @embedFile("pepsi.raw")); +var wave_t: f32 = 0; -var audio_offset: usize = 0; +export fn audio() void { + for (&cart.audio_buffer[0], &cart.audio_buffer[1]) |*l, *r| { + const val: i16 = @intFromFloat(@sin(wave_t) * std.math.maxInt(i16)); -export fn audio(buffer: *volatile [2][512]i16) bool { - if (audio_offset >= raw_data.len) return false; + l.* = val; + r.* = val; - @memcpy(&buffer[0], raw_data[audio_offset..][0..512]); - @memcpy(&buffer[1], raw_data[audio_offset..][0..512]); - - audio_offset += 512; - - return true; + wave_t += 0.1; + } } diff --git a/src/board/audio.zig b/src/board/audio.zig index cd532ff..38dac8f 100644 --- a/src/board/audio.zig +++ b/src/board/audio.zig @@ -1,28 +1,9 @@ -pub const sample_buffer: *volatile [2][512]u16 = &sample_buffer_storage; +pub const sample_buffer: *volatile [2][512]i16 = @ptrFromInt(0x20000000 + 0xa020); +var call_audio: *const fn () void = undefined; -pub const Channel = struct { - duty: u32, - phase: u32, - phase_step: u31, - phase_step_step: i32, - - duration: u31, - attack_duration: u31, - decay_duration: u31, - sustain_duration: u31, - release_duration: u31, - - volume: u31, - volume_step: i32, - peak_volume: u31, - sustain_volume: u31, - attack_volume_step: i32, - decay_volume_step: i32, - release_volume_step: i32, -}; - -pub fn init() void { +pub fn init(call_audio_fn: *const fn () void) void { @setCold(true); + call_audio = call_audio_fn; board.A0_SPKR.set_dir(.out); board.A1_VCC.set_dir(.in); @@ -204,89 +185,20 @@ pub fn init() void { } pub fn mix() callconv(.C) void { - var local_channels = channels.*; var speaker_enable: port.Level = .low; + + call_audio(); + for (&sample_buffer[ (dma.get_audio_part() + sample_buffer.len - 1) % sample_buffer.len - ]) |*out_sample| { - var sample: i32 = 0; - inline for (&local_channels) |*channel| { - if (channel.duty > 0) { - // generate sample; - if (channel.phase < channel.duty) { - sample += channel.volume; - } else { - sample -= channel.volume; - } - // update - channel.phase +%= channel.phase_step; - channel.phase_step = @intCast(channel.phase_step + channel.phase_step_step); - if (channel.duration > 0) { - channel.duration -= 1; - channel.volume = @intCast(channel.volume + channel.volume_step); - } else if (channel.attack_duration > 0) { - channel.duration = channel.attack_duration; - channel.attack_duration = 0; - channel.volume = 0; - channel.volume_step = channel.attack_volume_step; - } else if (channel.decay_duration > 0) { - channel.duration = channel.decay_duration; - channel.decay_duration = 0; - channel.volume = channel.peak_volume; - channel.volume_step = channel.decay_volume_step; - } else if (channel.sustain_duration > 0) { - channel.duration = channel.sustain_duration; - channel.sustain_duration = 0; - channel.volume = channel.sustain_volume; - channel.volume_step = 0; - } else if (channel.release_duration > 0) { - channel.duration = channel.release_duration; - channel.release_duration = 0; - channel.volume = channel.sustain_volume; - channel.volume_step = channel.release_volume_step; - } else { - channel.duty = 0; - } - } - } + ]) |sample| { if (sample != 0) speaker_enable = .high; - out_sample.* = @intCast((sample >> 16) - std.math.minInt(i16)); } - channels.* = local_channels; + board.SPKR_EN.write(speaker_enable); dma.ack_audio(); } -pub fn set_channel(channel: usize, state: Channel) void { - NVIC.ICER[32 / 32].write(.{ .CLRENA = 1 << 32 % 32 }); - channels[channel] = state; - NVIC.ISER[32 / 32].write(.{ .SETENA = 1 << 32 % 32 }); -} - -var channels_storage: [4]Channel = .{.{ - .duty = 0, - .phase = 0, - .phase_step = 0, - .phase_step_step = 0, - - .duration = 0, - .attack_duration = 0, - .decay_duration = 0, - .sustain_duration = 0, - .release_duration = 0, - - .volume = 0, - .volume_step = 0, - .peak_volume = 0, - .sustain_volume = 0, - .attack_volume_step = 0, - .decay_volume_step = 0, - .release_volume_step = 0, -}} ** 4; -const channels: *volatile [4]Channel = &channels_storage; - -var sample_buffer_storage: [2][512]u16 = .{.{0} ** 512} ** 2; - const board = @import("../board.zig"); const std = @import("std"); const microzig = @import("microzig"); diff --git a/src/cart/api.zig b/src/cart/api.zig index 54d4ed7..5cf2169 100644 --- a/src/cart/api.zig +++ b/src/cart/api.zig @@ -86,6 +86,7 @@ pub const neopixels: *[5]NeopixelColor = @ptrFromInt(base + 0x08); pub const red_led: *bool = @ptrFromInt(base + 0x1c); pub const battery_level: *u12 = @ptrFromInt(base + 0x1e); pub const framebuffer: *volatile [screen_width][screen_height]Pixel = @ptrFromInt(base + 0x20); +pub const audio_buffer: *volatile [2][512]i16 = @ptrFromInt(base + 0xa020); pub const BlitOptions = struct { pub const Flags = packed struct(u32) {