summaryrefslogtreecommitdiff
path: root/src/audio/audio.cc
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-04 13:08:13 +0100
committerskal <pascal.massimino@gmail.com>2026-02-04 13:08:13 +0100
commitc4888bea71326f7a69e8214af0d9c2a62a60b887 (patch)
treedd1e71be51893414c590db8a7be578ddfd6232f0 /src/audio/audio.cc
parentf64f842bb0dabd89308e2378e56358bc8abdd653 (diff)
feat(audio): Implement audio backend abstraction (Task #51.1)
Created interface-based audio backend system to enable testing without hardware. This is the foundation for robust tracker timing verification. Changes: - Created AudioBackend interface with init/start/shutdown methods - Added test-only hooks: on_voice_triggered() and on_frames_rendered() - Moved miniaudio implementation to MiniaudioBackend class - Refactored audio.cc to use backend abstraction with auto-fallback - Added time tracking to synth.cc (elapsed time from rendered frames) - Created test_audio_backend.cc to verify backend injection works - Fixed audio test linking to include util/procedural dependencies All test infrastructure guarded by #if !defined(STRIP_ALL) for zero size impact on final build. Production path unchanged, 100% backward compatible. All 13 tests pass. handoff(Claude): Task #51.1 complete, audio backend abstraction ready Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/audio/audio.cc')
-rw-r--r--src/audio/audio.cc69
1 files changed, 44 insertions, 25 deletions
diff --git a/src/audio/audio.cc b/src/audio/audio.cc
index d08a2fa..6ee9782 100644
--- a/src/audio/audio.cc
+++ b/src/audio/audio.cc
@@ -1,16 +1,35 @@
// This file is part of the 64k demo project.
// It manages the low-level audio device and high-level audio state.
-// Implementation uses miniaudio for cross-platform support.
+// Now uses backend abstraction for testability.
#include "audio.h"
+#include "audio_backend.h"
+#include "miniaudio_backend.h"
+#include "synth.h"
#include "util/asset_manager.h"
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
-#include "synth.h"
#include <stdio.h>
+// Global backend pointer for audio abstraction
+static AudioBackend* g_audio_backend = nullptr;
+static MiniaudioBackend g_default_backend;
+static bool g_using_default_backend = false;
+
+#if !defined(STRIP_ALL)
+// Allow tests to inject a custom backend
+void audio_set_backend(AudioBackend* backend) {
+ g_audio_backend = backend;
+}
+
+// Get current backend (for tests)
+AudioBackend* audio_get_backend() {
+ return g_audio_backend;
+}
+#endif /* !defined(STRIP_ALL) */
+
int register_spec_asset(AssetId id) {
size_t size;
const uint8_t* data = GetAsset(id, &size);
@@ -28,36 +47,24 @@ int register_spec_asset(AssetId id) {
return synth_register_spectrogram(&spec);
}
-static ma_device g_device;
-
-void audio_data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
- ma_uint32 frameCount) {
- (void)pInput;
- float* fOutput = (float*)pOutput;
- synth_render(fOutput, (int)frameCount);
-}
-
void audio_init() {
synth_init();
- ma_device_config config = ma_device_config_init(ma_device_type_playback);
- config.playback.format = ma_format_f32;
- config.playback.channels = 2;
- config.sampleRate = 32000;
- config.dataCallback = audio_data_callback;
-
- if (ma_device_init(NULL, &config, &g_device) != MA_SUCCESS) {
- printf("Failed to open playback device.\n");
- return;
+ // Use default backend if none set
+ if (g_audio_backend == nullptr) {
+ g_audio_backend = &g_default_backend;
+ g_using_default_backend = true;
}
+
+ g_audio_backend->init();
}
void audio_start() {
- if (ma_device_start(&g_device) != MA_SUCCESS) {
- printf("Failed to start playback device.\n");
- ma_device_uninit(&g_device);
+ if (g_audio_backend == nullptr) {
+ printf("Cannot start: audio not initialized.\n");
return;
}
+ g_audio_backend->start();
}
#if !defined(STRIP_ALL)
@@ -72,6 +79,11 @@ void audio_render_silent(float duration_sec) {
(total_frames > chunk_size) ? chunk_size : total_frames;
synth_render(buffer, frames_to_render);
total_frames -= frames_to_render;
+
+ // Notify backend of frames rendered (for mock tracking)
+ if (g_audio_backend != nullptr) {
+ g_audio_backend->on_frames_rendered(frames_to_render);
+ }
}
}
#endif /* !defined(STRIP_ALL) */
@@ -80,7 +92,14 @@ void audio_update() {
}
void audio_shutdown() {
- ma_device_stop(&g_device);
- ma_device_uninit(&g_device);
+ if (g_audio_backend != nullptr) {
+ g_audio_backend->shutdown();
+ }
synth_shutdown();
+
+ // Clear backend pointer if using default
+ if (g_using_default_backend) {
+ g_audio_backend = nullptr;
+ g_using_default_backend = false;
+ }
}