Skip to content

audio: provide OSS-compatible /dev/dsp PCM playback - #947

Open
brandonpayton wants to merge 7 commits into
mainfrom
emdash/complete-dsp-support-8tsc5
Open

audio: provide OSS-compatible /dev/dsp PCM playback#947
brandonpayton wants to merge 7 commits into
mainfrom
emdash/complete-dsp-support-8tsc5

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 14, 2026

Copy link
Copy Markdown
Member

Why

Kandelo applications currently have no standard POSIX-style device for playing digital audio. Programs such as SDL games can render graphics and accept input, but they either need application-specific audio code or run silently because /dev/dsp does not exist. /dev/dsp is the conventional Open Sound System (OSS) device used by many portable Unix programs for Pulse-Code Modulation (PCM) audio samples.

Adding this device at the platform layer lets unmodified software use its normal OSS backend in both Node.js and the browser. It also keeps buffering, blocking writes, polling, and close/drain behavior in one authoritative kernel model instead of recreating those semantics in each demo.

What changed

  • Add a playback PCM core with bounded buffering, producer/consumer accounting, one owner per open file description, blocking and nonblocking writes, poll wakeups, drain, reset, and silence during underruns.
  • Expose that core through a fixed wasm32 OSS /dev/dsp Application Binary Interface (ABI), including the control operations used by SDL2 and SDL3 plus truthful buffer size, delay, capability, nonblocking, post, and output-position reporting.
  • Pace browser playback with a packaged AudioWorklet; pace Node.js playback with a wall-clock null sink. Both hosts preserve the same kernel-visible playback position and drain behavior.
  • Build and test the unmodified upstream SDL2/SDL3 OSS backends and SDL_mixer playwave. Simplify fbDOOM so it uses the standard <sys/soundcard.h> interface instead of private audio definitions.
  • Document supported controls, compatibility boundaries, host lifecycle, porting guidance, latency, memory use, and artifact size.

This PR supersedes #698. That earlier PR established useful AudioWorklet and host-transport experiments, but it also introduced an Advanced Linux Sound Architecture (ALSA) /dev/snd interface and ALSA-specific state that are outside this focused OSS device contract.

Compatibility boundary

The initial device is playback-only and exposes one exclusive default stream. It supports unsigned 8-bit audio, signed 16-bit little-endian audio, and signed 16-bit big-endian audio; mono or stereo; and rates clamped to 8–192 kHz. Capture, duplex, memory mapping, mixer controls, trigger control, multi-client mixing, and ALSA interfaces remain explicitly unsupported and are not advertised.

SDL and application evidence

  • SDL2 is exercised through its upstream blocking-write OSS path.
  • SDL3 is exercised through its upstream SNDCTL_DSP_GETOSPACE wait path.
  • Upstream SDL_mixer 2.8.2 playwave runs at 44.1 kHz signed 16-bit stereo and 22.05 kHz unsigned 8-bit mono. The Node.js sink captures bytes consumed from the shared ring buffer; the test compares the non-silent payload byte-for-byte with the generated WAV file, checks real-time pacing and negotiated controls, and requires a clean drain with no discarded bytes.
  • fbDOOM uses the Software Development Kit (SDK) OSS header instead of private control definitions and device-specific pacing.
  • The Chromium test proves that queued audio stays blocked while the browser AudioContext is suspended, a trusted click resumes the packaged worklet, and close waits for the remaining audio to drain.

Validation

  • Kernel tests: 1,164 passed, 0 failed
  • Vitest: 183 files passed, 5 skipped; 1,473 passed, 2 expected failures, 19 skipped; teardown follow-up 3 passed
  • musl libc-test: 303 passed, 0 failed, 20 expected failures, 1 flaky pass
  • Open POSIX Test Suite: 174 passed, 0 failed, 3 expected failures, 2 unsupported skips
  • ABI snapshot, C header, and generated TypeScript binding check: passed
  • Focused production Chromium AudioWorklet test: passed

Automated browser validation covers user activation, worklet loading, clock-driven consumption, drain, suspend/resume, and teardown. A human audible browser check remains unrun because it requires a reviewer with audio output.

Footprint and buffering

  • Kernel WebAssembly: 532,311 / 143,233 bytes raw/compressed before; 612,569 / 147,554 bytes after (+80,258 / +4,321 bytes)
  • Host ECMAScript Module (ESM) entries: +60,007 / +10,123 bytes raw/compressed
  • Packaged PCM AudioWorklet: 8,863 / 2,462 bytes
  • SDL_mixer playwave WebAssembly: 1,800,045 / 434,029 bytes
  • Shared PCM transport: fixed 65,664-byte allocation
  • Default active queue: 21.333 ms; 24 ms configured software buffering with a 128-frame browser quantum at 48 kHz

@github-actions

Copy link
Copy Markdown
Contributor

Phase B-1 matrix build status — pr-947-staging

ABI v40. 2 built, 73 failed, 75 total.

Package Arch Status Sha
icu wasm32 failed
libcurl wasm32 failed
libcxx wasm32 built d86a56cf
libcxx wasm64 built 187a1130
libiconv wasm32 failed
libpng wasm32 failed
libxml2 wasm32 failed
libzip wasm32 failed
openssl wasm32 failed
openssl wasm64 failed
sdl2 wasm32 failed
sdl3 wasm32 failed
sqlite wasm32 failed
sqlite wasm64 failed
zlib wasm32 failed
zlib wasm64 failed
bc wasm32 failed
bzip2 wasm32 failed
coreutils wasm32 failed
curl wasm32 failed
dash wasm32 failed
diffutils wasm32 failed
dinit wasm32 failed
fbdoom wasm32 failed
file wasm32 failed
findutils wasm32 failed
gawk wasm32 failed
git wasm32 failed
grep wasm32 failed
gzip wasm32 failed
hello wasm32 failed
kandelo-sdk wasm32 failed
kernel wasm32 failed
less wasm32 failed
lsof wasm32 failed
m4 wasm32 failed
make wasm32 failed
mariadb wasm32 failed
mariadb wasm64 failed
modeset wasm32 failed
msmtpd wasm32 failed
nano wasm32 failed
ncurses wasm32 failed
netcat wasm32 failed
nginx wasm32 failed
php wasm32 failed
posix-utils-lite wasm32 failed
ruby wasm32 failed
sdl-dsp-test wasm32 failed
sdl2-mixer-playwave wasm32 failed
sed wasm32 failed
spidermonkey wasm32 failed
tar wasm32 failed
tcl wasm32 failed
unzip wasm32 failed
userspace wasm32 failed
vim wasm32 failed
wget wasm32 failed
xz wasm32 failed
zip wasm32 failed
zstd wasm32 failed
bash wasm32 failed
mariadb-test wasm32 failed
mariadb-vfs wasm32 failed
mariadb-vfs wasm64 failed
nethack wasm32 failed
node wasm32 failed
spidermonkey-node wasm32 failed
vim-browser-bundle wasm32 failed
nethack-browser-bundle wasm32 failed
rootfs wasm32 failed
shell wasm32 failed
lamp wasm32 failed
node-vfs wasm32 failed
wordpress wasm32 failed

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

brandonpayton added a commit that referenced this pull request Aug 12, 2026
Forward-port PR #947 core commit d77e5a9 onto the combined ABI 43
contract. Keep PCM ownership on refcounted open file descriptions and
expose the bounded shared-clock transport without dropping later ABI
exports or regenerating package cache keys prematurely.
brandonpayton added a commit that referenced this pull request Aug 12, 2026
Forward-port the accepted browser portion of PR #947 onto the ABI 43
integration branch. Keep the current lazy test-runner artifact loading
while adding the real AudioWorklet lifecycle proof and machine-level
resume controls.

Source: c288f562146acf263f8ee28405fdd7d3bcc21caf
brandonpayton added a commit that referenced this pull request Aug 12, 2026
Forward-port the accepted SDL package and integration-test slice of PR
#947 onto ABI 43. Build official SDL2 and SDL3 releases through the
worktree SDK, keep their upstream OSS backends, and exercise paced PCM
through the production Node path.

The generated program-package projection remains reserved for the final
artifact commit.

Source: 66e8dcbe8dcb5da95de43b5b9da214a3879bbf4a
brandonpayton added a commit that referenced this pull request Aug 17, 2026
Forward-port PR #947 core commit d77e5a9 onto the combined ABI 43
contract. Keep PCM ownership on refcounted open file descriptions and
expose the bounded shared-clock transport without dropping later ABI
exports or regenerating package cache keys prematurely.
brandonpayton added a commit that referenced this pull request Aug 17, 2026
Forward-port the accepted browser portion of PR #947 onto the ABI 43
integration branch. Keep the current lazy test-runner artifact loading
while adding the real AudioWorklet lifecycle proof and machine-level
resume controls.

Source: c288f562146acf263f8ee28405fdd7d3bcc21caf
brandonpayton added a commit that referenced this pull request Aug 17, 2026
Forward-port the accepted SDL package and integration-test slice of PR
#947 onto ABI 43. Build official SDL2 and SDL3 releases through the
worktree SDK, keep their upstream OSS backends, and exercise paced PCM
through the production Node path.

The generated program-package projection remains reserved for the final
artifact commit.

Source: 66e8dcbe8dcb5da95de43b5b9da214a3879bbf4a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant