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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user