From 21aca01cba3e10a493cde28e22ac8c31efea6130 Mon Sep 17 00:00:00 2001 From: Quinn Date: Fri, 27 Jun 2025 12:39:04 +0200 Subject: [PATCH] rework audio system to be able to call it from anywhere, rather than having to have access to the audio data. --- src/io/audio.c | 136 ++++++++++++++++++++++++------------------------ src/io/audio.h | 31 +++++++---- src/io/window.c | 11 ++-- 3 files changed, 95 insertions(+), 83 deletions(-) diff --git a/src/io/audio.c b/src/io/audio.c index ddcdf54..5150dd4 100644 --- a/src/io/audio.c +++ b/src/io/audio.c @@ -11,22 +11,28 @@ #include "../error.h" #include "../util/compat.h" +#include "../util/types.h" struct audioplayer { - uint8_t const* buf; + u8 const* buf; int len; }; -struct audiodevice { +static struct audiodevice { struct audioplayer audio[AUDIO_MAX]; - unsigned id; - int freq; - SDL_AudioFormat fmt; - uint8_t ch; // audio channels + u32 id; + SDL_AudioSpec spec; } dev; +struct audiodata audio_dat[AUDIO_ID_COUNT] = {0}; // contains pointers to audio buffers. + +static char const* const audio_path[AUDIO_ID_COUNT] = { + "korobeiniki.wav", + "place.wav", +}; + /* mixes the audio output stream, using the different audio as sources */ -static void audiomixer(void* const userdata, uint8_t* const stream, int const len) { +static void audiomixer(void* const userdata, u8* const stream, int const len) { (void)userdata; memset(stream, 0, len); // clear the playing audio @@ -34,7 +40,7 @@ static void audiomixer(void* const userdata, uint8_t* const stream, int const le for (unsigned i = 0; i < AUDIO_MAX; i++) { if (dev.audio[i].len > 0) { unsigned mixlen = SDL_min(len, dev.audio[i].len); - SDL_MixAudioFormat(stream, dev.audio[i].buf, dev.fmt, mixlen, SDL_MIX_MAXVOLUME); + SDL_MixAudioFormat(stream, dev.audio[i].buf, dev.spec.format, mixlen, SDL_MIX_MAXVOLUME); dev.audio[i].len -= mixlen; dev.audio[i].buf += mixlen; } @@ -45,12 +51,12 @@ static void audiomixer(void* const userdata, uint8_t* const stream, int const le * `len` is a pointer to the current size, the new size will be written to this location. * returns the pointer to the audio buffer to use, or NULL, when something went wrong. * NULL will never be returned after the conversion */ -static uint8_t* audio_cvt(SDL_AudioSpec const* spec, uint8_t* bufptr, unsigned* len) { +static u8* audio_cvt(SDL_AudioSpec const* spec, u8* bufptr, unsigned* len) { if (!bufptr) return NULL; // init the converter SDL_AudioCVT cvt; - if (SDL_BuildAudioCVT(&cvt, spec->format, spec->channels, spec->freq, dev.fmt, dev.ch, dev.freq) < 0) { + if (SDL_BuildAudioCVT(&cvt, spec->format, spec->channels, spec->freq, dev.spec.format, dev.spec.channels, dev.spec.freq) < 0) { error("could not build the audio converter! SDL Error: %s", SDL_GetError()); return NULL; } @@ -78,72 +84,68 @@ static uint8_t* audio_cvt(SDL_AudioSpec const* spec, uint8_t* bufptr, unsigned* return bufptr ? bufptr : cvt.buf; } -int audio_device_init(int freq, SDL_AudioFormat fmt, uint8_t channels, uint16_t samples) { - // define the audio specification - SDL_AudioSpec spec = {freq, fmt, channels, 0, samples, 0, 0, NULL, NULL}; - spec.callback = audiomixer; - spec.userdata = NULL; - - unsigned id = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); - - // create the audio device - dev = (struct audiodevice){ - {0}, - id, - freq, - fmt, - channels, - }; - - if (id) { - // default state of the device is paused, so we unpause it here - SDL_PauseAudioDevice(id, 0); - return 0; - } else return 1; +/* computes the time in milliseconds of the audio fragment by dividing + * the audio byte length by the format's bit size, which is divided by + * the audio device's channels and frequency */ +static inline u32 audio_btoms(u32 len) { + return (((1000 * len) / (SDL_AUDIO_BITSIZE(dev.spec.format) / 8)) / dev.spec.channels / dev.spec.freq); } -void audio_device_free(void) { - SDL_CloseAudioDevice(dev.id); - dev = (struct audiodevice){0}; -} - -void audio_play(audiodata const* audio) { - if (!dev.id) return; - if (!audio->buf) return; - - unsigned i = 0; - while (i < AUDIO_MAX && dev.audio[i].len > 0) i++; - if (i < AUDIO_MAX) - dev.audio[i] = (struct audioplayer){audio->buf, audio->len}; -} - -audiodata audio_wav_load(char const* fpath) { - debug("loading audio file '%s'...", fpath); - - if (faccess(fpath, FA_R)) { - error("%s:%u audio file either isn't readable or doesn't exist. path: '%s'!", __FILE_NAME__, __LINE__, fpath); - return (struct audiodata){0}; +/* loads a `struct audiodata` from `fpat` to `out`. */ +static void audio_wav_load(char const* restrict fpat, struct audiodata* restrict out) { + debug("loading audio file '%s'...", fpat); + if (faccess(fpat, FA_R)) { + error("audio file either isn't readable or doesn't exist. path: '%s'!", fpat); + return; } // load the audio + u32 len; + u8 *ptr, *tmp; SDL_AudioSpec spec; - audiodata audio; - SDL_LoadWAV(fpath, &spec, &audio.buf, &audio.len); - if (!audio.buf) return audio; + SDL_LoadWAV(fpat, &spec, &ptr, &len); // convert the audio data to the format reflecting dev - void* ptr = audio_cvt(&spec, audio.buf, &audio.len); - if (!ptr) free(audio.buf); // free the buffer if NULL was returned; failure - audio.buf = ptr; + tmp = audio_cvt(&spec, ptr, &len); + if (!tmp) free(ptr); // free the buffer if NULL was returned; failure - // calculate the time in milliseconds of the audio fragment - // by dividing the audio bytelength by the format's bitsize, by the audio device's channels and the audio device's frequency - audio.ms = (((1000 * audio.len) / (SDL_AUDIO_BITSIZE(dev.fmt) / 8)) / dev.ch / dev.freq); - - return audio; + *out = (struct audiodata){tmp, len, audio_btoms(len)}; } -void audio_wav_unload(audiodata* audio) { - free(audio->buf); - *audio = (audiodata){0}; // zero out all audio data +/* loads the audio data into the buffer */ +static inline void audio_load(void) { + for (size_t i = 0; i < AUDIO_ID_COUNT; i++) + audio_wav_load(audio_path[i], &audio_dat[i]); +} + +void audio_play(u32 audio_id) { + if (!dev.id) return; + size_t i = 0; + while (i < AUDIO_MAX && dev.audio[i].len > 0) i++; + if (i >= AUDIO_MAX) return; + + dev.audio[i] = (struct audioplayer){ + audio_dat[audio_id].buf, + audio_dat[audio_id].len, + }; +} + +int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples) { + // initialise the audio device + specification + SDL_AudioSpec spec = {freq, fmt, ch, 0, samples, 0, 0, audiomixer, NULL}; + unsigned id = SDL_OpenAudioDevice(NULL, 0, &spec, &spec, 0); + dev = (struct audiodevice){{0}, id, spec}; + + if (!id) return 1; + SDL_PauseAudioDevice(id, 0); + audio_load(); + return 0; +} + +void audio_free(void) { + SDL_CloseAudioDevice(dev.id); + dev = (struct audiodevice){0}; + + for (size_t i = 0; i < AUDIO_ID_COUNT; i++) + free((void*)audio_dat[i].buf); } diff --git a/src/io/audio.h b/src/io/audio.h index 2c34d69..72d54f5 100644 --- a/src/io/audio.h +++ b/src/io/audio.h @@ -3,18 +3,31 @@ #include #include +#include "../util/types.h" + #define AUDIO_MAX 4 // maximum number of sound effects that are allowed to play at once struct audiodata { - uint8_t* buf; // pointer to the audio buffer - uint32_t len; // length in bytes of the audio buffer - uint32_t ms; // length in miliseconds of the audio buffer + u8 const* buf; // pointer to the audio buffer + u32 len; // length in bytes of the audio buffer + u32 ms; // length in miliseconds of the audio buffer }; -typedef struct audiodata audiodata; -int audio_device_init(int, SDL_AudioFormat, uint8_t, uint16_t); -void audio_device_free(void); -void audio_play(audiodata const*); +enum audio_id { + AUDIO_ID_MUSIC, + AUDIO_ID_PLACE, -audiodata audio_wav_load(char const*); -void audio_wav_unload(audiodata*); + // leave at end, will contain count + AUDIO_ID_COUNT, +}; + +extern struct audiodata audio_dat[AUDIO_ID_COUNT]; + +/* loads the audio to be played, unless `AUDIO_MAX` has been reached, then this call will fail */ +void audio_play(u32); + +/* initialises the audio device, then loads the audio data */ +int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples); + +/* frees up resources held by the audio device and audio buffers */ +void audio_free(void); diff --git a/src/io/window.c b/src/io/window.c index 9138686..d84901e 100644 --- a/src/io/window.c +++ b/src/io/window.c @@ -17,7 +17,6 @@ static SDL_Window* win = NULL; static bool close = false; -static audiodata music; void window_init(struct gamedata const* gdat) { assert(!win && !close); @@ -31,8 +30,7 @@ void window_init(struct gamedata const* gdat) { fatal(ERROR_SDL_RENDERING_INIT, "Window failed to be created! SDL Error: %s", SDL_GetError()); render_init(win, gdat); - audio_device_init(32000, AUDIO_S16, 1, 4096); - music = audio_wav_load("korobeiniki.wav"); + audio_init(32000, AUDIO_S16, 1, 4096); } void window_free(void) { @@ -42,8 +40,7 @@ void window_free(void) { win = NULL; close = false; - audio_wav_unload(&music); - audio_device_free(); + audio_free(); } void window_open(void) { @@ -53,8 +50,8 @@ void window_open(void) { render_update(); static time_t timeout = 0; - if (time_poll(time, music.ms, &timeout)) - audio_play(&music); + if (time_poll(time, audio_dat[AUDIO_ID_MUSIC].ms, &timeout)) + audio_play(AUDIO_ID_MUSIC); } }