Userland audio API and libc support
Goal
Provide userland API in libc for audio playback, compatible with Doom's audio expectations and standard OSS/ALSA patterns.
Context
Part of #33 (Audio subsystem). This layer wraps /dev/dsp device interface (#384) into convenient C library functions that Doom and other applications can use.
Scope
libc Audio API
Core Functions
// Open audio device
int audio_open(const char *device, int flags);
int audio_close(int fd);
// Configure audio format
int audio_set_format(int fd, int format); // AFMT_U8, AFMT_S16_LE
int audio_set_channels(int fd, int channels); // 1=mono, 2=stereo
int audio_set_rate(int fd, int rate); // 11025, 22050, 44100, 48000
// Playback
ssize_t audio_write(int fd, const void *buf, size_t count);
int audio_drain(int fd); // Block until all samples played
int audio_reset(int fd); // Stop immediately and flush
// Query
int audio_get_ospace(int fd, struct audio_buf_info *info);
Helper Wrappers
// Simplified API for common cases
int audio_init(int rate, int channels, int format);
int audio_play_samples(const void *samples, size_t count);
void audio_shutdown(void);
Doom Integration Shims
Doom expects these functions (from doomgeneric or i_sound.c):
void I_InitSound(void);
void I_ShutdownSound(void);
void I_SubmitSound(void); // Called periodically to feed mixer
void I_UpdateSound(void); // Update music position
Map these to libc audio API:
-
I_InitSound()→audio_init(11025, 2, AFMT_S16_LE) -
I_SubmitSound()→audio_write()with mixed samples -
I_ShutdownSound()→audio_shutdown()
Host Test Harness
For MENIOS_HOST_TEST=1 builds, forward to host audio:
- Use OSS
/dev/dspon FreeBSD/Linux - Use SDL2 audio on macOS (via
SDL_OpenAudio) - Mock implementation for CI environments
Headers
<sys/soundcard.h> (OSS compatibility)
#define SNDCTL_DSP_SETFMT _SIOWR('P', 5, int)
#define SNDCTL_DSP_CHANNELS _SIOWR('P', 6, int)
#define SNDCTL_DSP_SPEED _SIOWR('P', 2, int)
#define SNDCTL_DSP_GETOSPACE _SIOR('P', 12, struct audio_buf_info)
#define SNDCTL_DSP_RESET _SIO('P', 0)
#define SNDCTL_DSP_SYNC _SIO('P', 1)
#define AFMT_U8 0x00000008
#define AFMT_S16_LE 0x00000010
<audio.h> (meniOS-specific)
#define AUDIO_DEVICE "/dev/dsp"
struct audio_buf_info {
int fragments;
int fragstotal;
int fragsize;
int bytes; // Available bytes for writing
};
int audio_open(const char *device, int flags);
// ... (prototypes above)
Implementation Phases
Phase 1: Basic API (3-4 days)
- Implement
audio_open/close/write - Add format/rate/channel configuration wrappers
- Error handling and errno mapping
Phase 2: Advanced Features (2-3 days)
- Implement
audio_drain/reset/get_ospace - Non-blocking mode support
- Buffer management helpers
Phase 3: Doom Integration (2-3 days)
- Implement
I_InitSound/SubmitSound/ShutdownSound - Map Doom's internal mixer output to
audio_write - Test with Doom's sound effects and music
Phase 4: Host Test Harness (2-3 days)
- SDL2 audio backend for macOS testing
- OSS passthrough for Linux/FreeBSD
- Mock backend for CI
Definition of Done
- [ ]
audio_open/close/writeimplemented in libc - [ ] Format/rate/channel configuration functions working
- [ ]
<sys/soundcard.h>header with OSS defines - [ ]
<audio.h>header with meniOS API - [ ] Host test harness forwards to SDL2/OSS
- [ ] Doom integration functions (
I_*Sound) implemented - [ ] Tested with tone generator (#386)
- [ ] Doom sound effects play correctly
- [ ] Documentation in
docs/api/audio.md
Dependencies
- #193 - Minimal libc ✅ (CLOSED)
- #384 - Audio syscalls and /dev/dsp (CRITICAL)
- #96 - File descriptor management ✅ (CLOSED)
- #300 - Doom port layer ✅ (CLOSED - for I_* function stubs)
Risks
- API mismatch: Doom expects specific OSS semantics
- Buffer sizing: Incorrect fragment size causes latency or underruns
- Host test complexity: SDL2 dependency for macOS testing
- Endianness: S16_LE vs S16_BE on different architectures
Timeline
Estimated effort: 1.5-2 weeks (9-13 days)
Testing Strategy
- Unit tests: Mock /dev/dsp, verify ioctl calls
- Integration tests: Real kernel, tone generator
- Doom integration: Load Doom, verify sound effects play
- Host tests: Verify SDL2 backend on macOS
Example Usage
// Simple tone generator
#include <audio.h>
#include <math.h>
int main(void) {
int fd = audio_init(22050, 1, AFMT_S16_LE);
int16_t samples[1024];
for (int i = 0; i < 1024; i++) {
samples[i] = (int16_t)(sin(2 * M_PI * 440 * i / 22050) * 16384);
}
audio_play_samples(samples, sizeof(samples));
audio_drain(fd);
audio_shutdown();
return 0;
}
References
- OSS Programming Guide
- SDL2 Audio API: https://wiki.libsdl.org/CategoryAudio
- Doom source:
i_sound.c,doomgeneric_menios.c - ALSA PCM API (for comparison)
Related Issues
- Parent: #33 (Audio subsystem)
- Depends on: #193, #384, #96, #300
- Enables: #386 (Audio tooling)
Priority
Medium - Part of Doom audio milestone, blocked by #279
Status
BLOCKED BY #279 (Dynamic IRQ handler registration API)
This issue is part of the audio subsystem implementation for the Doom milestone. It cannot proceed until #279 is complete.
Dependency Chain
#279 (Dynamic IRQ) ← CRITICAL BLOCKER (HIGH priority)
↓
#382 + #383 (Audio driver + core) ← YOU ARE HERE
↓
#384 (Syscalls) → #385 (API) → #386 (Testing)
↓
Doom audio integration
Related Issues
- #33 (Audio subsystem parent) - umbrella issue
- #279 (Dynamic IRQ) - BLOCKS THIS ISSUE
Priority Rationale: Medium priority appropriate for Doom milestone work, but cannot start until HIGH priority #279 is complete.