Add audio subsystem skeleton with a working Linux PCM playback path

Cross-platform audiostream/audio API with per-platform hooks for
PSP/Dolphin/Linux. Linux is fully wired end-to-end through SDL2
(device open, volume-mixed queueing, finish detection via
SDL_GetQueuedAudioSize) and verified playing a generated test tone
in engine.c. PSP and Dolphin hooks are stubbed for now.
This commit is contained in:
2026-08-31 10:24:02 -05:00
parent fcf0de72af
commit 8ea370a1d1
31 changed files with 1022 additions and 1 deletions
+1
View File
@@ -11,6 +11,7 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
# Subdirs
add_subdirectory(asset)
add_subdirectory(audio)
add_subdirectory(log)
add_subdirectory(input)
if(DUSK_NETWORK)
+11
View File
@@ -0,0 +1,11 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
audiolinux.c
audiostreamlinux.c
)
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "audiolinux.h"
#include <SDL2/SDL.h>
errorret_t audioLinuxInit() {
if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
errorThrow("Failed to initialize SDL2 audio subsystem: %s", SDL_GetError());
}
errorOk();
}
errorret_t audioLinuxUpdate() {
errorOk();
}
errorret_t audioLinuxDispose() {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
errorOk();
}
+30
View File
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
/**
* Initializes the Linux-specific audio subsystem state.
*
* @return Error state if any.
*/
errorret_t audioLinuxInit();
/**
* Updates the Linux-specific audio subsystem state. Called once per frame.
*
* @return Error state if any.
*/
errorret_t audioLinuxUpdate();
/**
* Disposes the Linux-specific audio subsystem state.
*
* @return Error state if any.
*/
errorret_t audioLinuxDispose();
+13
View File
@@ -0,0 +1,13 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "audiolinux.h"
#define audioPlatformInit audioLinuxInit
#define audioPlatformUpdate audioLinuxUpdate
#define audioPlatformDispose audioLinuxDispose
+67
View File
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "audiostreamlinux.h"
#include "audio/audiostream.h"
#include "assert/assert.h"
#include "util/memory.h"
errorret_t audioStreamLinuxInit(audiostream_t *stream) {
assertNotNull(stream, "Stream cannot be NULL.");
SDL_AudioSpec desired;
memoryZero(&desired, sizeof(SDL_AudioSpec));
desired.freq = (int) stream->pcm.sampleRate;
desired.format = AUDIO_S16SYS;
desired.channels = stream->pcm.channels;
desired.samples = 4096;
stream->platform.device = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0);
if(stream->platform.device == 0) {
errorThrow("Failed to open SDL2 audio device: %s", SDL_GetError());
}
errorOk();
}
errorret_t audioStreamLinuxDispose(audiostream_t *stream) {
assertNotNull(stream, "Stream cannot be NULL.");
SDL_CloseAudioDevice(stream->platform.device);
errorOk();
}
errorret_t audioStreamLinuxBuffer(audiostream_t *stream) {
assertNotNull(stream, "Stream cannot be NULL.");
uint8_t *mixed = memoryAllocate(stream->dataSize);
memoryZero(mixed, stream->dataSize);
SDL_MixAudioFormat(
mixed, stream->data, AUDIO_S16SYS, (Uint32) stream->dataSize,
(stream->volume * SDL_MIX_MAXVOLUME) / 0xFF
);
int queued = SDL_QueueAudio(
stream->platform.device, mixed, (Uint32) stream->dataSize
);
memoryFree(mixed);
if(queued != 0) {
errorThrow("Failed to queue SDL2 audio data: %s", SDL_GetError());
}
SDL_PauseAudioDevice(stream->platform.device, 0);
errorOk();
}
bool_t audioStreamLinuxIsFinished(audiostream_t *stream) {
assertNotNull(stream, "Stream cannot be NULL.");
return SDL_GetQueuedAudioSize(stream->platform.device) == 0;
}
+52
View File
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include <SDL2/SDL.h>
typedef struct audiostream_s audiostream_t;
typedef struct {
SDL_AudioDeviceID device;
} audiostreamlinux_t;
/**
* Initializes the Linux-specific playback state of an audio stream by
* opening an SDL2 audio device matching the stream's PCM format.
*
* @param stream The audio stream to initialize.
* @return Error state if any.
*/
errorret_t audioStreamLinuxInit(audiostream_t *stream);
/**
* Disposes the Linux-specific playback state of an audio stream, closing
* its SDL2 audio device.
*
* @param stream The audio stream to dispose.
* @return Error state if any.
*/
errorret_t audioStreamLinuxDispose(audiostream_t *stream);
/**
* Sends the stream's currently staged PCM data (stream->data) to its SDL2
* audio device and starts/resumes playback.
*
* @param stream The audio stream to output.
* @return Error state if any.
*/
errorret_t audioStreamLinuxBuffer(audiostream_t *stream);
/**
* Checks whether the stream's SDL2 audio device has finished playing all
* queued data.
*
* @param stream The audio stream to check.
* @return true if playback has finished, false otherwise.
*/
bool_t audioStreamLinuxIsFinished(audiostream_t *stream);
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "audiostreamlinux.h"
typedef audiostreamlinux_t audiostreamplatform_t;
#define audioStreamPlatformInit audioStreamLinuxInit
#define audioStreamPlatformDispose audioStreamLinuxDispose
#define audioStreamPlatformBuffer audioStreamLinuxBuffer
#define audioStreamPlatformIsFinished audioStreamLinuxIsFinished