menios icon indicating copy to clipboard operation
menios copied to clipboard

Userland audio API and libc support

Open pbalduino opened this issue 3 months ago • 0 comments

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/dsp on 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/write implemented 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.

pbalduino avatar Oct 29 '25 22:10 pbalduino