aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPolesznyák Márk <contact@pml68.dev>2026-02-21 00:22:30 +0100
committerPolesznyák Márk <contact@pml68.dev>2026-02-21 00:22:30 +0100
commit5d2ef703d4d1e998ae1f1e8f79ee0f70c37a8082 (patch)
tree8d3ded6b650f292411c4210a9f5eaf0da7f19a0a
parentchore: build in release mode (diff)
downloadhare-chip8-5d2ef703d4d1e998ae1f1e8f79ee0f70c37a8082.tar.gz
feat: add audio
-rw-r--r--main.ha58
-rw-r--r--sdl_audio.ha53
2 files changed, 93 insertions, 18 deletions
diff --git a/main.ha b/main.ha
index 8051da4..e3e9332 100644
--- a/main.ha
+++ b/main.ha
@@ -9,6 +9,7 @@ use sdl2;
def SCALE: int = 20;
def CLOCKSPEED: int = 600;
+def VOLUME: i16 = 3000;
def SCREEN_WIDTH: size = 64;
def SCREEN_HEIGHT: size = 32;
@@ -70,20 +71,6 @@ fn chip8() state = {
return state;
};
-fn reset(state: *state) void = {
- state.display = [false...];
- state.keys = [false...];
- state.stack = [0...];
- state.SP = 0;
- state.PC = START_ADDR;
- state.V = [0...];
- state.I = 0;
- state.DT = 0;
- state.ST = 0;
-
- state.mem[..FONT_SIZE] = FONT[..];
-};
-
fn instruction(opcode: u16) inst = inst {
op = opcode,
X = (opcode >> 8): u8 & 0x0F,
@@ -365,14 +352,15 @@ fn execute_instruction(state: *state, inst: inst, random: *random::random) void
};
};
-fn tick_timers(state: *state) void = {
+fn tick_timers(state: *state, audio_device: SDL_AudioDeviceID) void = {
if (state.DT > 0)
state.DT -= 1;
if (state.ST > 0) {
- // BEEP
-
state.ST -= 1;
+ SDL_PauseAudioDevice(audio_device, 0);
+ } else {
+ SDL_PauseAudioDevice(audio_device, 1);
};
};
@@ -415,6 +403,22 @@ fn update_screen(state: *state, renderer: *sdl2::SDL_Renderer) (void | sdl2::err
sdl2::SDL_RenderPresent(renderer);
};
+fn audio_callback(userdata: nullable *opaque, stream: *u8, len_: int) void = {
+ let stream: []i16 = *(&types::slice {
+ data = stream: *[*]i16,
+ length = len_: size / 2,
+ capacity = 0
+ }: *[]i16);
+
+ let sample_index = 0;
+ const half_square_wave_period = 44100 / 440 / 2;
+
+ for (let i = 0; i < len_ / 2; i += 1) {
+ stream[i] = if ((sample_index / half_square_wave_period) % 2 == 0) -VOLUME else VOLUME;
+ sample_index += 1;
+ };
+};
+
fn run() (void | fs::error | io::error | sdl2::error) = {
if (len(os::args) < 2) {
fmt::fatalf("Usage: {} <rom>", os::args[0]);
@@ -440,6 +444,24 @@ fn run() (void | fs::error | io::error | sdl2::error) = {
let renderer = sdl2::SDL_CreateRenderer(window, -1, sdl2::SDL_RendererFlags::ACCELERATED)?;
defer sdl2::SDL_DestroyRenderer(renderer);
+ const want_spec = SDL_AudioSpec {
+ freq = 44100,
+ format = AUDIO_S16LSB,
+ channels = 1,
+ samples = 512,
+ callback = &audio_callback,
+ userdata = null,
+ ...
+ };
+
+ let have_spec = SDL_AudioSpec {
+ callback = &audio_callback,
+ ...
+ };
+
+ let dev = SDL_OpenAudioDevice(null, 0, &want_spec, &have_spec, 0)?;
+ defer SDL_CloseAudioDevice(dev);
+
sdl2::SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF)?;
sdl2::SDL_RenderClear(renderer)?;
@@ -551,7 +573,7 @@ fn run() (void | fs::error | io::error | sdl2::error) = {
chip8.draw = false;
};
- tick_timers(&chip8);
+ tick_timers(&chip8, dev);
};
};
diff --git a/sdl_audio.ha b/sdl_audio.ha
new file mode 100644
index 0000000..545668c
--- /dev/null
+++ b/sdl_audio.ha
@@ -0,0 +1,53 @@
+use sdl2;
+use types::c;
+
+type SDL_AudioFormat = u16;
+
+def AUDIO_U8: SDL_AudioFormat = 0x0008;
+def AUDIO_S8: SDL_AudioFormat = 0x8008;
+def AUDIO_U16LSB: SDL_AudioFormat = 0x0010;
+def AUDIO_S16LSB: SDL_AudioFormat = 0x8010;
+def AUDIO_U16MSB: SDL_AudioFormat = 0x1010;
+def AUDIO_S16MSB: SDL_AudioFormat = 0x9010;
+def AUDIO_U16: SDL_AudioFormat = AUDIO_U16LSB;
+def AUDIO_S16: SDL_AudioFormat = AUDIO_S16LSB;
+def AUDIO_S32LSB: SDL_AudioFormat = 0x8020;
+def AUDIO_S32MSB: SDL_AudioFormat = 0x9020;
+def AUDIO_S32: SDL_AudioFormat = AUDIO_S32LSB;
+def AUDIO_F32LSB: SDL_AudioFormat = 0x8120;
+def AUDIO_F32MSB: SDL_AudioFormat = 0x9120;
+def AUDIO_F32: SDL_AudioFormat = AUDIO_F32LSB;
+
+type SDL_AudioCallback = *fn(userdata: nullable *opaque, stream: *u8, len_: int) void;
+
+type SDL_AudioSpec = struct {
+ freq: int,
+ format: SDL_AudioFormat,
+ channels: u8,
+ silence: u8,
+ samples: u16,
+ padding: u16,
+ size_: u32,
+ callback: SDL_AudioCallback,
+ userdata: nullable *opaque
+};
+
+type SDL_AudioDeviceID = u32;
+
+@symbol("SDL_OpenAudioDevice") fn _SDL_OpenAudioDevice(device: nullable *c::char, iscapture: int, desired: const *SDL_AudioSpec, obtained: *SDL_AudioSpec, allowed_changes: int) SDL_AudioDeviceID;
+
+@symbol("SDL_CloseAudioDevice") fn SDL_CloseAudioDevice(dev: SDL_AudioDeviceID) void;
+
+@symbol("SDL_PauseAudioDevice") fn SDL_PauseAudioDevice(dev: SDL_AudioDeviceID, pause_on: int) void;
+
+@symbol("SDL_GetError") fn SDL_GetError() const *c::char;
+
+fn SDL_OpenAudioDevice(device: nullable *c::char, iscapture: int, desired: const *SDL_AudioSpec, obtained: *SDL_AudioSpec, allowed_changes: int) (SDL_AudioDeviceID | sdl2::error) = {
+ const ret = _SDL_OpenAudioDevice(device, iscapture, desired, obtained, allowed_changes);
+
+ if (ret == 0) {
+ return c::tostr(SDL_GetError()): sdl2::error;
+ };
+
+ return ret;
+};