# Audio Capture / Playback Spike Chanora proof-of-concept. **Not product code.** | Field | Value | |---|---| | PoC name | `audio-capture-playback-spike` | | PoC plan | [`docs/architecture/proof-of-concept-plan.md`](../../docs/architecture/proof-of-concept-plan.md) §2 | | Purpose | Prove platform audio capture + playback behaviour | | Exit criterion (plan) | "Capture/playback works on at least one desktop and one mobile target" | | Authority | DEC-011 (platform-native first), audio family in SysDes / SRS | ## Honest status **Partial pass.** - **Linux desktop**: verified. cpal opens both the default input and the default output device, capture streams build (or report a typed error when no hardware-backed input is available), and playback drives a full PCM stream end-to-end through the platform audio stack (ALSA → PipeWire on this host). - **Mobile (Android / iOS)**: **not verified**. cpal supports Oboe (Android) and AVAudioEngine (iOS) but this PoC has not been built or run on either. The exit criterion's mobile half remains an open item for a follow-up spike — see "Gaps" below. ## What it proves - `AudioCapture::record_to_wav(path, duration)` — opens the default input device, reads frames in any supported sample format, down-mixes to mono, writes 16-bit-PCM WAV. - `AudioPlayback::play_wav(path)` — opens the default output device, picks a stream config matching the file's sample rate, blocks until the file drains. - `synth_sine_wav` — produces a deterministic 440 Hz sine WAV so the playback path is exercisable on hosts without a usable input device. - A typed `AudioError` covering the practical failure modes (no device, config mismatch, stream-build failure, WAV I/O, unsupported format). Production code (`chanora_audio`) will widen this when DSP + Opus + jitter buffer surfaces land. - Headless-friendly behaviour: if capture cannot build a stream, the CLI falls back to synth and the playback path is still exercised. This is the same shape production code would use for a "device test" UI. ## Layout ```text audio-capture-playback-spike/ src/ lib.rs # crate root + re-exports capture.rs # AudioCapture, AudioError, sample-format adapters playback.rs # AudioPlayback synth.rs # synth_sine_wav (deterministic test signal) main.rs # audio-capture-playback-cli tests/ audio.rs # 2 tests Cargo.toml ``` ## Reproduce (Linux desktop) Requires Rust stable, ALSA dev headers (cpal builds against ALSA on Linux), and a working audio stack (PipeWire / Pulse / pure ALSA). ```bash cargo test cargo run --bin audio-capture-playback-cli -- roundtrip ``` CLI modes: | Mode | What it does | |---|---| | `capture` | Records 1 s of audio from the default input device to `${TMPDIR}/chanora_audio_spike_capture.wav`. | | `synth` | Writes a 440 Hz / 500 ms sine wave to `${TMPDIR}/chanora_audio_spike_synth.wav`. | | `playback` | Plays the synth WAV through the default output device. | | `roundtrip` (default) | Tries `capture`; on any failure, falls back to `synth`; then `playback`. | ### PipeWire-only Linux hosts On hosts where the only audio server is PipeWire (no `pulseaudio` package), `cpal` still uses ALSA underneath and needs a `pcm.!default` alias to the PipeWire PCM plugin. The package `pipewire-alsa` (or your distro equivalent) plus a one-line `~/.asoundrc` is the standard fix: ```text pcm.!default { type pipewire } ctl.!default { type pipewire } ``` ## Gaps (the mobile half of the exit criterion) | Target | What's missing | Notes | |---|---|---| | Android | Build & run cpal-oboe path on an emulator or device | Needs NDK + Gradle wiring + emulator. cpal supports it; this spike has not built it. | | iOS | Build & run cpal-AVAudioEngine path on a device | Needs macOS + Xcode + a developer account. | These should be closed by a follow-up spike (or by promotion into the product `chanora_audio` crate scaffold) before the audio half of the PoC matrix is considered complete. ## Scope boundaries - **No DSP.** HPF / NS / AEC / AGC / mixing / Opus encode-decode / jitter buffer all belong to `chanora_audio` and are out of scope. - **No bit-exact loopback assertion.** This PoC verifies *that* a capture stream can be opened and *that* a playback stream can drive a file to completion. It does not verify capture-to-playback signal integrity. - **No latency measurement.** - **No device-permission flows.** Mobile microphone consent UI is product-level. - **No echo-cancellation evidence.** Different PoC. ## Verification log See `VERIFICATION.md` in this directory.