diff options
| author | Polesznyák Márk <contact@pml68.dev> | 2026-02-21 00:22:30 +0100 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2026-02-21 00:22:30 +0100 |
| commit | 5d2ef703d4d1e998ae1f1e8f79ee0f70c37a8082 (patch) | |
| tree | 8d3ded6b650f292411c4210a9f5eaf0da7f19a0a | |
| parent | chore: build in release mode (diff) | |
| download | hare-chip8-5d2ef703d4d1e998ae1f1e8f79ee0f70c37a8082.tar.gz | |
feat: add audio
| -rw-r--r-- | main.ha | 58 | ||||
| -rw-r--r-- | sdl_audio.ha | 53 |
2 files changed, 93 insertions, 18 deletions
@@ -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; +}; |
