Add ALSA audio backend and espeak-ng browser demo#698
Draft
mho22 wants to merge 18 commits into
Draft
Conversation
PAGE_FLIP now queues into pending_flips and the host's 60 Hz vblank pump drains every process's queue into the per-fd event_ring, stamped with the new sequence + host monotonic time. Replaces the v1 synchronous in-ioctl drain so libdrm's PageFlip + drmHandleEvent loop returns at monitor refresh, not ioctl rate. Three issues had to land together: * startVblankPump() now runs unconditionally at end of init() (was only started by attachKmsCanvas / attachKmsStats). Vitest fixtures exercising PAGE_FLIP without attaching a canvas would otherwise block on poll() indefinitely. tickVblank no-ops gracefully when no canvas or stats SAB is attached. * poll_check(card0) gates POLLIN on event_ring being non-empty (mirrors /dev/input/mice). Without this a poll + read + drmHandleEvent loop races the pump: poll reports ready, read returns 0, drmHandleEvent reports a short read. * libdrm_stub.drmHandleEvent polls before read. Real libdrm relies on Linux blocking-mode read on /dev/dri/cardN; sys_read on card0 returns Ok(0) on an empty ring (centralized mode has no SYS_READ EAGAIN-retry plumbing), so a straight read would race the vblank tick and the caller's pending flip would stay queued, hitting EBUSY on the next PageFlip. The PAGE_FLIP unit test in syscalls.rs is rewritten to drive the drain via dri::drain_pending_flips_for_process (the per-process variant the global pump invokes), keeping it free of GLOBAL_PROCESS_TABLE side effects. ABI: kernel_vblank's exported signature is unchanged (still () -> u32); no new exports, no marshalled struct changes. Snapshot stays in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nstants First slice of plan 5 / evdev. Adds pub mod input as a sibling of pub mod dri: WpkInputEvent (24-byte repr(C) with explicit _pad at offset 12 so ev_type lands at offset 16 where C's struct timeval puts it), WpkInputId, WpkInputAbsinfo, EV_/KEY_/BTN_/REL_/ABS_/SYN_ codes, BUS_VIRTUAL, EVIOCGVERSION/EVIOCGID/EVIOCGRAB plus the variable-length nr bases for EVIOCGNAME / EVIOCGBIT / EVIOCGABS. KEY_* covers 0..248 — the full Linux input-event-codes.h surface that browsers can emit through KeyboardEvent.code, so Phase B2's translation table is just a key-by-key lookup. input_tests verifies struct sizes (24/8/24), field offsets (the _pad is load-bearing; without it ev_type sits at offset 12 and the C reader silently misreads every record), and re-derives each EVIOC* number from (dir, magic, nr, size) so a copy-paste typo cannot survive. Purely additive — no ABI_VERSION bump; snapshot regen lands in A7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Second slice of plan 5 / evdev. Extends VirtualDevice with the
InputEvent { device: u8 } struct variant (kbd / ptr; host_handle
-10 / -11), wires match_virtual_device to recognise event0 +
event1 (event2+ deliberately returns None), and adds the per-OFD
InputFdState sidecar: device + 24 KiB event ring + grabbed flag
+ dropped flag + ring_high_water diagnostic. Sidecar is parallel
to dri_state, not nested — disjoint state machines.
devfs lists event0 + event1 alongside the existing mice entry.
sys_read on an evdev fd is a placeholder Ok(0) for now — A5
lands the ring drain + SYN_DROPPED resync semantics. Fork
deserialise leaves input_state None with a TODO(A4/A5)
breadcrumb; no observable consumer exists yet.
Two pre-existing assertions adjusted:
* match_virtual_device_recognizes_mice no longer claims
/dev/input/event0 returns None.
* test_virtual_device_roundtrip's "first sentinel past the
range" moves from -10 to -12 since -10 / -11 are now kbd / ptr.
Purely additive — no ABI_VERSION bump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ndling Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…OPPED resync Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sys_close snapshots OFD.input_state on last-ref and drops it via new input_release_ofd_state helper (mirrors dri_release_ofd_state; v1 body is a no-op drop, signature reserved for plan-9 grab-released hook). Fork/exec serialise the per-OFD ring + grab + dropped flags + high water mark across write_input_state / read_input_state (modelled on write_dri_state / read_dri_state). Reader bounds-checks against INPUT_RING_MAX_BYTES and rejects non-record-aligned lengths as EINVAL. Tests: - close_releases_grab_so_next_open_can_grab — OFD slot is freed, a fresh open on the same node comes up clean and re-grabbable. - fork_then_close_in_child_keeps_grab_on_parent — child inherits grab + queued events; closing the child's fd doesn't touch parent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A4 added `kernel_input_event` (host → kernel event producer) and `kernel_set_input_canvas_dims` (host → kernel pointer canvas geometry). Both are pure additions to the kernel-wasm export set — no existing entry changes — so per CLAUDE.md ABI policy this is additive-compatible and `ABI_VERSION` stays at 14. Note: `scripts/check-abi-version.sh` continues to flag `kernel_reserve_host_region` + `kernel_reserve_host_region_at` as "removed" and reports `host_adapter` + `process_memory_layout` as reshaped vs `upstream/main`. That is the same pre-existing upstream snapshot drift from PR #629 ("Make pthread control slots dynamic") already documented in 00d123b — not introduced by this branch and not addressed here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase A artifacts removed: - ring_high_water field on InputFdState: debug-only counter no producer reads; carried 4 bytes per OFD across the fork wire. - input::wait::wake_event_reader stub + was_empty/woken_ofds bookkeeping in push_event: empty no-op. Phase B will add real wake routing where it is actually wired. - input_release_ofd_state + the sys_close snapshot block: 4-param helper whose body was `let _ = state;`. Slot Drop already releases the box on dec_ref — the take/helper pattern only exists for dri_state because that path calls into the host. - VirtualDevice::InputEvent host_handle catchall returning -10: collapsed to `device => -10 - device as i64`. Trim the matching test (push_event_tracks_high_water) and the sys_read comment that referenced wake_event_reader by name. ABI snapshot byte-identical; cargo test 983/983. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds host/src/input/input-source.ts with the InputEvent record (device 0|1, ev_type, code, value) and the InputSource interface (start(dispatch), stop()) that subsequent commits implement per host: BrowserInputSource captures DOM events (keyboard/pointer/wheel) and translates to evdev codes; NodeInputSource is a null-source for headless test runs. Convention encoded in the doc-comment: the source emits the type-specific record then a SYN_REPORT to close the logical frame, mirroring Linux evdev. Host wires dispatch to kernel.exports.kernel_input_event at boot, after kernel.exports.kernel_set_input_canvas_dims. Vitest is import-and-instantiate sanity only — a StubSource records two dispatches, stop() clears the dispatch handle, post-stop emits are dropped. No kernel or ABI surface change; pure-additive host module with no consumers until B4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_* translation
Adds host/src/input/browser-input-source.ts (DOM keyboard/pointer/wheel
capture, evdev translation, SYN_REPORT framing) and
host/src/input/key-code-table.ts (~130-entry KeyboardEvent.code →
KEY_* lookup mirroring shared::input KEY_* values — Linux UAPI
verbatim, same numeric space SDL2's evdev backend consumes on real
Linux).
Coordinate convention: pointer-lock active → REL_X/REL_Y deltas
(movementX/Y); inactive → ABS_X/ABS_Y absolute (offsetX/Y). A bare
SYN_REPORT on pointerlockchange gives libinput / SDL2 a re-sync point
so a stale axis value doesn't carry across the lock-mode transition.
Wheel normalisation handles browser deltaMode quanta: PIXEL (Chromium
±100/±120 per notch, Safari ±1–10), LINE (Firefox ±3 per notch). A
small-but-nonzero delta clamps to ±1 tick so continuous-trackpad
scrolls still emit at least one record (otherwise Math.trunc(0.3 /
120) = 0 swallows the entire scroll).
Vitest covers every translation path with 17 specs: keydown
down/repeat/unknown, keyup, pointermove ABS vs REL with mocked
pointerLockElement, single-axis movement skip, three pointer buttons,
wheel PIXEL/LINE/HWHEEL/small-delta/zero-delta, lock-change SYN, and
stop() listener removal. Uses a FakeTarget EventTarget stub and
vi.stubGlobal('document', …) — no jsdom/happy-dom dependency.
The unused canvas constructor parameter is intentionally stashed for
B4, which calls kernel_set_input_canvas_dims off it.
No kernel or ABI surface change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds host/src/input/node-input-source.ts implementing InputSource as a pair of no-ops. There's no DOM in Node, and the integration tests drive evdev events directly via kernel.exports.kernel_input_event(…) instead of synthesising KeyboardEvent / PointerEvent. The Node host still registers an InputSource at boot so the init path is symmetric with the browser-side one (CLAUDE.md §"Two hosts" — dual-host parity is load-bearing). Vitest: start() registers but emits no records; start()/stop() are safe to call repeatedly. No kernel or ABI surface change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the host-side plumbing for evdev: both BrowserKernel and
NodeKernelHost gain injectInputEvent / setInputCanvasDims raw entry
points plus an attachInputSource(source, dims) helper that mirrors
the boot pattern across the two hosts. CLAUDE.md §"Two hosts —
DUAL-HOST PARITY IS LOAD-BEARING": every layer gets a parallel diff
in the same commit (protocol message type, host class method, worker
entry switch case, shared kernel-worker.ts wrapper, shared kernel.ts
export wrapper).
Three layers, symmetric on both sides:
1. host/src/kernel.ts (WasmPosixKernel) — calls the new
kernel_input_event / kernel_set_input_canvas_dims exports from
A4 + A7. Silent-drop pattern if the kernel module isn't yet
instantiated, same as injectMouseEvent.
2. host/src/kernel-worker.ts (CentralizedKernelWorker) — wraps the
calls and schedules a blocked-reader wake on injectInputEvent
so processes parked in sys_read on /dev/input/event{0,1} get
re-poked. Picks "extend pendingPipeReaders" for the wake
mechanism (handoff-28's B4-open choice) by reusing
scheduleWakeBlockedRetries — same path mice uses.
3. host/src/{browser,node}-kernel-protocol.ts +
host/src/{browser,node}-kernel-worker-entry.ts +
host/src/{browser,node}-kernel-host.ts — main↔worker message
plumbing and public host-class API. attachInputSource(source,
dims) sets canvas dims then starts the source with a dispatch
callback that funnels each emitted record through
injectInputEvent. Both hosts share the same method shape so
callers see identical surface; only constructor patterns for
the InputSource differ (BrowserInputSource on the browser,
NodeInputSource null-source on Node).
Vitest:
- input-attach-source.test.ts: attachInputSource posts dims-msg
once, calls source.start once, routes dispatched records through
injectInputEvent → input_event_inject. setInputCanvasDims and
injectInputEvent also tested standalone. Bypasses init() (which
spawns a real worker_thread) by monkey-patching sendToWorker;
the constructor only stores options, so a bare new
NodeKernelHost() is safe.
- Browser-side end-to-end exercised at Phase C via Playwright per
plan §C; pure-logic translation already covered by
browser-input-source.test.ts (B2, 17 specs).
Symmetry sweep verified: every new symbol
(input_event_inject / set_input_canvas_dims / injectInputEvent /
setInputCanvasDims / attachInputSource) shows parallel hits in
browser-* and node-* trees at every layer.
No Rust changes; no ABI surface change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B5 closes Phase B with an end-to-end gate for the evdev path. `input-evdev-smoke.c` opens /dev/input/event0 + event1, does EVIOCGNAME / EVIOCGABS, and drains the per-OFD rings. Each phase gates on a stdin byte so the host injects events AFTER the OFD exists — `kernel_input_event` fans out at push time and a pre-open injection would land nowhere. The vitest drives `NodeKernelHost.injectInputEvent` (B4) directly and asserts: EVIOCGNAME returns "wpk virtual keyboard"; EVIOCGABS(ABS_X).maximum reports canvas_w-1; KEY_A↓+SYN_REPORT round-trip with monotonic-non-decreasing CLOCK_MONOTONIC stamps; REL_X=+5 + SYN_REPORT; and overflow drains to 1025 records (1 synthesised SYN_DROPPED at index 0, then 1024 surviving ring records) with the last record still an EV_KEY/KEY_A — i.e. the incoming records were the ones discarded, Linux semantics. `it.skipIf(!fixtureBinary)` keeps the suite green when the wasm fixture isn't built locally, matching the dri-kms-pageflip pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… browser spec Closes the evdev plan's Phase C (PR #3 scope) plus a build-script regression that blocked the demo from compiling. - `libc/musl-overlay/include/linux/input.h` + `libc/musl-overlay/include/linux/input-event-codes.h` — vendored minimal subset (C1). `programs/evdev_demo.c` is the lone consumer in tree; its `_Static_assert(sizeof(struct input_event) == 24)` is the compile-time guard against wasm32 layout drift. - `programs/evdev_demo.c` (~100 LoC) — opens /dev/input/event{0,1}, prints EVIOCGNAME for both, then polls forever and logs every key and pointer event. Free-running (no stdin-barrier harness) so it works as the interactive Kandelo pane (C2). - `apps/browser-demos/pages/kandelo/presets.ts` + `kernel-host/live-setup.ts` — wire an `evdev` preset that stages the binary into /usr/local/bin, attaches a BrowserInputSource to window so DOM key/pointer events reach `kernel_input_event`, and runs the demo through bash so its stdout lands in the Shell pane. Boot path mirrors the existing `modeset` preset. - `apps/browser-demos/test/kandelo-evdev.spec.ts` — Playwright spec that drives KeyA + pointer moves and asserts the on-canvas log contains `key down: code=30` and `ptr (abs|rel) code=N value=N`. Proves the B4 dual-host parity claim end-to-end in a real browser. - `scripts/build-musl.sh` steps 10-11 — restores libdrm.a + libgbm.a compile-and-archive steps that were lost when commit b25ef59 ported the DRI sources forward but missed the build wiring from commits a091a5b and 2f7fead. Without these, programs/dri-modeset (and friends) fail to link with `no such file or directory: libgbm.a`. Manual browser verification (CLAUDE.md item 6 / C4) passes: keystrokes and pointer movement appear in the on-canvas log under `./run.sh browser` → `?demo=evdev`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First pass dropped the unused `canvas` ctor arg on BrowserInputSource and trimmed top-of-file what-narration in input.h / evdev_demo.c / live-setup.ts / input/mod.rs. Second pass strips the param-table doc on `kernel_input_event` + `kernel_set_input_canvas_dims`, collapses the per-case EVIOCG* headers and ioctl-decode preamble in `handle_input_ioctl`, tightens the SYN_DROPPED / blocking-read narration in the evdev read drain and poll gate, collapses the dispatch.rs file-doc, drops the three-phase prose and ioctl-encoding paragraph from `input-evdev-smoke.c`, and trims test-internal narration that restates test names across `syscalls.rs`, `ofd.rs`, `input/mod.rs`, and `devfs.rs`. Kept the load-bearing WHYs: ENOTTY-not-EINVAL for SDL2 probe walking, SYN_DROPPED resync after overflow, blocking-read-returns-0 retry contract, concurrency-justification for tests that mutate canvas-dim globals. Net across both passes: -184 LoC (12 files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire a Kandelo ALSA subsystem (devfs nodes, PCM ioctl dispatch, SAB ring, mmap pages, tick, poll, fork serialisation) and a host audio stack (`AudioDriver`, Browser + Node drivers, AudioWorklet, drain-on-stop, instrumented wrapper) on both Node and browser hosts. Port espeak-ng with a vendored pcaudiolib `audio_kandelo` backend, bake the binary and English data dir into `shell.vfs.zst`, and expose a kandelo "ALSA – espeak-ng" demo preset with a Playwright spec. ABI delta is additive (three new exports, two new virtual devices) and does not bump `ABI_VERSION`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ructure' into explore-dri-evdev-and-alsa # Conflicts: # apps/browser-demos/pages/kandelo/kernel-host/live-setup.ts
Phase B-1 matrix build status —
|
| Package | Arch | Status | Sha |
|---|---|---|---|
| libcurl | wasm32 | built | 05aae726 |
| libcxx | wasm32 | built | 7924fbe0 |
| libcxx | wasm64 | built | cfe94678 |
| libpng | wasm32 | built | e11caae0 |
| libxml2 | wasm32 | built | 6db06345 |
| libxml2 | wasm64 | built | bfabb4ff |
| openssl | wasm32 | built | 2708b58d |
| openssl | wasm64 | built | a1be8942 |
| sqlite | wasm32 | built | 0affae3a |
| sqlite | wasm64 | built | 2638b6ad |
| zlib | wasm32 | built | a74da2ed |
| zlib | wasm64 | built | 76101599 |
| bc | wasm32 | built | 46973bfb |
| bzip2 | wasm32 | built | ffa5f4c4 |
| coreutils | wasm32 | built | 4d8239d1 |
| curl | wasm32 | built | dca90723 |
| dash | wasm32 | built | ad59e65d |
| diffutils | wasm32 | built | d67cb386 |
| dinit | wasm32 | built | 6780ca3d |
| fbdoom | wasm32 | built | a4dcc46f |
| file | wasm32 | built | d2312c00 |
| findutils | wasm32 | built | 3767b4e1 |
| gawk | wasm32 | built | faedad8e |
| git | wasm32 | built | f50d2974 |
| grep | wasm32 | built | b9b6b1e6 |
| gzip | wasm32 | built | 539fee04 |
| kandelo-sdk | wasm32 | built | bdddb4fe |
| kernel | wasm32 | built | 51062af5 |
| less | wasm32 | built | 5180eac9 |
| lsof | wasm32 | built | 96fa5be8 |
| m4 | wasm32 | built | 0aa7ce01 |
| make | wasm32 | built | 5cdd9bd8 |
| mariadb | wasm32 | built | 4c185e36 |
| mariadb | wasm64 | built | ec3ea5f8 |
| msmtpd | wasm32 | built | aa3de664 |
| nano | wasm32 | built | 4a52b067 |
| ncurses | wasm32 | built | ff1ca5a3 |
| netcat | wasm32 | built | 20b49fdf |
| nginx | wasm32 | built | 5c15a366 |
| php | wasm32 | built | f1bfba4b |
| posix-utils-lite | wasm32 | built | df2ce6e9 |
| sed | wasm32 | built | 80e62002 |
| spidermonkey | wasm32 | built | 0b3b3066 |
| tar | wasm32 | built | 0532dd66 |
| tcl | wasm32 | built | eba6fe94 |
| unzip | wasm32 | built | b4f32850 |
| userspace | wasm32 | built | 1d4c6a6f |
| vim | wasm32 | built | 9895fd65 |
| wget | wasm32 | built | 6c82e2c4 |
| xz | wasm32 | built | 7bf9b496 |
| zip | wasm32 | built | 261d8148 |
| zstd | wasm32 | built | 0c16fdb0 |
| bash | wasm32 | built | 15fc009b |
| mariadb-test | wasm32 | built | 884a67b1 |
| mariadb-vfs | wasm32 | built | dd1e248f |
| mariadb-vfs | wasm64 | built | f3286762 |
| nethack | wasm32 | built | 73993e86 |
| node | wasm32 | built | 2e1b58a3 |
| spidermonkey-node | wasm32 | built | 22cfbf7c |
| vim-browser-bundle | wasm32 | built | 0deeb2d2 |
| nethack-browser-bundle | wasm32 | built | 8561a152 |
| rootfs | wasm32 | built | 74c566e1 |
| 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Give Kandelo a working audio stack and prove it end-to-end with a real Wasm program. The kernel previously had no audio devices, so packages that rely on
/dev/snd/*(espeak-ng via pcaudiolib, SDL audio, mpg123) could not run unmodified. This PR adds the ALSA subsystem on both hosts, ports espeak-ng with a kandelo pcaudiolib backend, and surfaces the result as a browser demo that plays a spoken greeting.Summary
crates/kernel/src/audio/:/dev/snd/{pcmC0D0p, controlC0}devfs nodes, PCM ioctl dispatch, an mmap-able ring of pages backed by SharedArrayBuffer, a period-driven tick, poll readiness, and fork/exec state serialisation for both PCM and control fdskernel_audio_init_sab,kernel_audio_period_tick,kernel_audio_get_appl_ptr— and the matchingshared::abiconstants; the existingaudio.rsOSS-emulation module moves underaudio/oss.rsAudioDriverinterface plusBrowserAudioDriver(AudioWorklet + monotonichwPtr+ producer-pointer gate to prevent head truncation + drain-on-stop) andNodeAudioDriver(headless null sink so vitest exercises the same code paths)CLAUDE.md§"Two hosts":host/src/{browser,node}-kernel-{host,protocol,worker-entry}.tsplus the sharedkernel-worker.tsandkernel.tsinstrumentAudioDriver(inner, onFramesConsumed?)intohost/src/audio/instrumented-audio-driver.tsso demos can observeframesConsumedtotals without re-implementing the forwarding contract on every call siteaudio_objectbackend (audio_kandelo.c) and cross-compile espeak-ng with the English-only data dir for size; recipe + build script live underpackages/registry/espeak-ng/espeak-ng-data/intoshell.vfs.zstvia a durableinstall_local_binary shell …step in the image builder, with a package-system test that pins the installsound/asound.hunderlibc/musl-overlay/include/so libc consumers see the same UAPI the kernel implements; regenerateabi/snapshot.json,host/src/generated/abi.ts, andlibc/glue/abi_constants.hNotes
ABI_VERSIONis unchanged andscripts/check-abi-version.shconfirms the delta vsorigin/main./dev/snd/controlC0opens succeed through devfs but ioctls fall through to the defaultEINVAL. espeak-ng does not call them; the node exists so ALSA consumers that probe the control device before opening the PCM device see a real fd rather thanENOENT.BrowserAudioDriver.stop()defersaudioCtx.close()by(pending / sampleRate) * 1000 + 100 msto drain the AudioContext output queue. The 100 ms margin is empirical and browser-dependent; the rationale is inline inhost/src/audio/browser-audio-driver.ts.audio-driver.test.ts,browser-audio-driver-drain.test.ts(synchronous close whenpending == 0, deferred close path with exact drain time,clearIntervalon stop), andinstrumented-audio-driver.test.ts(forwarding all sevenstart()args +getApplPtridentity +framesConsumedaccumulation +stop()delegation — pins the argument-dropping regression that previously silenced the demo).Testing
cargo test -p kandelo --target aarch64-apple-darwin --lib→ 1047/1047 passcargo test -p wasm-posix-shared --target aarch64-apple-darwin→ 27/27 passnpx vitest run host/test/audio-driver.test.ts host/test/browser-audio-driver-drain.test.ts host/test/instrumented-audio-driver.test.ts tests/package-system/→ 22/22 pass across 6 filesbash scripts/check-abi-version.sh→ in sync; additive-compatible diff vsorigin/main, noABI_VERSIONbump requiredbash build.sh→ freshlocal-binaries/kernel.wasmexportskernel_audio_init_sab,kernel_audio_period_tick,kernel_audio_get_appl_ptr./run.sh browser /?demo=espeakplays "Welcome to Kandelo, the WebAssembly POSIX kernel"scripts/run-libc-tests.shandscripts/run-posix-tests.shnot run in this checkout (submodules absent); the audio surface is purely additive and has no libc/POSIX touchpoints these suites exercise