-
Notifications
You must be signed in to change notification settings - Fork 0
Usage Audio
Mathew Storm edited this page Oct 5, 2025
·
1 revision
The audio system provides simple sound effect playback using SDL2's audio subsystem.
The audio module handles:
- Audio device initialization
- WAV file loading
- Sound effect playback
- Audio clip management
typedef struct {
char filename[MAX_FILENAME_LENGTH];
SDL_AudioSpec spec;
uint32_t length;
uint8_t* buffer;
} aAudioClip_t;int a_InitAudio(void);Initializes the audio device. Called automatically by a_Init().
Returns:
-
0on success -
1on failure
void a_LoadSounds(const char *filename, aAudioClip_t *clip);Loads a WAV audio file into an audio clip structure.
Parameters:
-
filename- Path to the WAV file -
clip- Pointer to audio clip structure to populate
Example:
aAudioClip_t shootSound;
a_LoadSounds("resources/sounds/shoot.wav", &shootSound);void a_PlaySoundEffect(aAudioClip_t *clip);Plays a loaded audio clip.
Parameters:
-
clip- Pointer to the loaded audio clip
Example:
a_PlaySoundEffect(&shootSound);#include "Archimedes.h"
aAudioClip_t jumpSound;
aAudioClip_t coinSound;
aAudioClip_t explosionSound;
void loadAudio(void) {
a_LoadSounds("resources/sounds/jump.wav", &jumpSound);
a_LoadSounds("resources/sounds/coin.wav", &coinSound);
a_LoadSounds("resources/sounds/explosion.wav", &explosionSound);
}
void logic(float dt) {
a_DoInput();
// Play sound on key press
if (app.keyboard[SDL_SCANCODE_SPACE]) {
a_PlaySoundEffect(&jumpSound);
}
}
int main(void) {
a_Init(SCREEN_WIDTH, SCREEN_HEIGHT, "Audio Example");
loadAudio();
app.delegate.logic = logic;
while (app.running) {
a_PrepareScene();
app.delegate.logic(a_GetDeltaTime());
a_PresentScene();
}
a_Quit();
return 0;
}typedef struct {
aAudioClip_t jump;
aAudioClip_t shoot;
aAudioClip_t hit;
aAudioClip_t powerup;
aAudioClip_t gameOver;
} SoundManager;
SoundManager sounds;
void initSounds(void) {
a_LoadSounds("resources/sounds/jump.wav", &sounds.jump);
a_LoadSounds("resources/sounds/shoot.wav", &sounds.shoot);
a_LoadSounds("resources/sounds/hit.wav", &sounds.hit);
a_LoadSounds("resources/sounds/powerup.wav", &sounds.powerup);
a_LoadSounds("resources/sounds/gameover.wav", &sounds.gameOver);
}
// Usage in game
void playerJump(void) {
a_PlaySoundEffect(&sounds.jump);
// ... jump logic ...
}
void collectPowerup(void) {
a_PlaySoundEffect(&sounds.powerup);
// ... powerup logic ...
}typedef struct {
aAudioClip_t clip;
float cooldown;
float cooldownTimer;
} CooldownSound;
CooldownSound shootSound;
void initCooldownSound(void) {
a_LoadSounds("resources/sounds/shoot.wav", &shootSound.clip);
shootSound.cooldown = 0.2f; // 0.2 seconds between shots
shootSound.cooldownTimer = 0.0f;
}
void updateCooldownSound(CooldownSound* snd, float dt) {
if (snd->cooldownTimer > 0.0f) {
snd->cooldownTimer -= dt;
}
}
int playCooldownSound(CooldownSound* snd) {
if (snd->cooldownTimer <= 0.0f) {
a_PlaySoundEffect(&snd->clip);
snd->cooldownTimer = snd->cooldown;
return 1; // Played
}
return 0; // On cooldown
}
// In game loop
void logic(float dt) {
updateCooldownSound(&shootSound, a_GetDeltaTime());
if (app.keyboard[SDL_SCANCODE_SPACE]) {
if (playCooldownSound(&shootSound)) {
// Shoot bullet
}
}
}#define NUM_FOOTSTEP_SOUNDS 4
typedef struct {
aAudioClip_t sounds[NUM_FOOTSTEP_SOUNDS];
int numSounds;
} RandomSoundBank;
RandomSoundBank footsteps;
void initRandomSounds(void) {
a_LoadSounds("resources/sounds/footstep1.wav", &footsteps.sounds[0]);
a_LoadSounds("resources/sounds/footstep2.wav", &footsteps.sounds[1]);
a_LoadSounds("resources/sounds/footstep3.wav", &footsteps.sounds[2]);
a_LoadSounds("resources/sounds/footstep4.wav", &footsteps.sounds[3]);
footsteps.numSounds = 4;
}
void playRandomSound(RandomSoundBank* bank) {
int index = rand() % bank->numSounds;
a_PlaySoundEffect(&bank->sounds[index]);
}
// Usage
playRandomSound(&footsteps); // Plays random footstep soundtypedef enum {
EVENT_PLAYER_JUMP,
EVENT_ENEMY_HIT,
EVENT_COIN_COLLECT,
EVENT_LEVEL_COMPLETE,
EVENT_MAX
} GameEvent;
aAudioClip_t eventSounds[EVENT_MAX];
void initEventSounds(void) {
a_LoadSounds("resources/sounds/jump.wav", &eventSounds[EVENT_PLAYER_JUMP]);
a_LoadSounds("resources/sounds/hit.wav", &eventSounds[EVENT_ENEMY_HIT]);
a_LoadSounds("resources/sounds/coin.wav", &eventSounds[EVENT_COIN_COLLECT]);
a_LoadSounds("resources/sounds/complete.wav", &eventSounds[EVENT_LEVEL_COMPLETE]);
}
void triggerGameEvent(GameEvent event) {
if (event >= 0 && event < EVENT_MAX) {
a_PlaySoundEffect(&eventSounds[event]);
}
}
// Usage
triggerGameEvent(EVENT_COIN_COLLECT);
triggerGameEvent(EVENT_PLAYER_JUMP);#include "Archimedes.h"
#include <stdlib.h>
typedef struct {
aAudioClip_t jump;
aAudioClip_t coin;
aAudioClip_t hurt;
} GameSounds;
GameSounds sounds;
int score = 0;
int coins[10][2]; // x, y positions
void initGame(void) {
// Load sounds
a_LoadSounds("resources/sounds/jump.wav", &sounds.jump);
a_LoadSounds("resources/sounds/coin.wav", &sounds.coin);
a_LoadSounds("resources/sounds/hurt.wav", &sounds.hurt);
// Initialize coins
for (int i = 0; i < 10; i++) {
coins[i][0] = rand() % SCREEN_WIDTH;
coins[i][1] = rand() % SCREEN_HEIGHT;
}
}
void logic(float dt) {
a_DoInput();
// Jump sound
static int spaceWasPressed = 0;
if (app.keyboard[SDL_SCANCODE_SPACE] && !spaceWasPressed) {
a_PlaySoundEffect(&sounds.jump);
spaceWasPressed = 1;
} else if (!app.keyboard[SDL_SCANCODE_SPACE]) {
spaceWasPressed = 0;
}
// Coin collection (simplified)
if (app.mouse.pressed) {
for (int i = 0; i < 10; i++) {
int dx = app.mouse.x - coins[i][0];
int dy = app.mouse.y - coins[i][1];
if (dx*dx + dy*dy < 400) { // 20px radius
a_PlaySoundEffect(&sounds.coin);
score += 10;
coins[i][0] = -100; // Hide coin
}
}
}
}
void render(float dt) {
// Draw coins
for (int i = 0; i < 10; i++) {
if (coins[i][0] >= 0) {
a_DrawFilledCircle(coins[i][0], coins[i][1], 10, yellow);
}
}
// Draw score
char scoreText[64];
snprintf(scoreText, sizeof(scoreText), "Score: %d", score);
a_DrawText(scoreText, 10, 10, 255, 255, 255, FONT_GAME, TEXT_ALIGN_LEFT, 0);
}
int main(void) {
a_Init(SCREEN_WIDTH, SCREEN_HEIGHT, "Audio Example");
initGame();
app.delegate.logic = logic;
app.delegate.draw = render;
while (app.running) {
a_PrepareScene();
app.delegate.logic(a_GetDeltaTime());
app.delegate.draw(a_GetDeltaTime());
a_PresentScene();
}
a_Quit();
return 0;
}SDL_AudioDeviceID app.deviceID; // Initialized by a_InitAudio()The audio device is automatically opened and started during initialization.
Archimedes audio system uses SDL_LoadWAV() which supports:
- WAV format (recommended)
- Uncompressed PCM audio
For other formats (MP3, OGG), you would need additional SDL_mixer integration.
- Sounds are loaded into memory when
a_LoadSounds()is called - Sound playback uses SDL's audio queue system
- Multiple sounds can play simultaneously
- Audio clips remain in memory until program exit
- No automatic cleanup - manage audio clip lifetimes manually if needed
// Check if audio initialization succeeded
if (a_InitAudio() != 0) {
printf("Audio initialization failed!\n");
}
// Check if sound loaded successfully
a_LoadSounds("missing.wav", &clip);
// Check SDL_GetError() if no sound playsToo many simultaneous sounds can cause clipping. Limit concurrent sounds or use volume mixing.
SDL_QueueAudio() may have slight delay. For real-time audio, consider using callbacks (advanced).
- No built-in volume control (would need manual implementation)
- No pitch shifting or audio effects
- No music playback (use SDL_mixer for music)
- No 3D positional audio
- Sound clips must be manually managed (no automatic cleanup)
- Preload all sounds at startup to avoid loading delays during gameplay
- Use cooldowns to prevent sound spam from repeated actions
- Check return values to handle loading failures gracefully
- Keep sound files small - use short clips for sound effects
- Use consistent sample rates (44100 Hz recommended) for all audio files
- Audio device is opened automatically by
a_Init() - Audio playback starts immediately (device is unpaused)
- The framework uses SDL's simple audio API, not SDL_mixer
- For more advanced audio features (music, mixing, effects), consider integrating SDL_mixer