Proof-of-concept addressing the audio exit criterion from
docs/architecture/proof-of-concept-plan.md §2:
"Capture/playback works on at least one desktop and one mobile
target."
PARTIAL PASS. The desktop half is verified on Linux; the mobile
half is NOT verified by this PoC and remains a documented open gap.
Implements via cpal (matching DEC-011 'platform-native first'):
- AudioCapture::record_to_wav opens the default input device,
handles f32/i16/u16 sample formats, down-mixes to mono, writes
16-bit PCM WAV via hound.
- AudioPlayback::play_wav opens the default output device, picks
a stream config matching the WAV, blocks until drained.
- synth_sine_wav produces a deterministic 440 Hz test signal for
headless verification of the playback path when no microphone
is available.
- Typed AudioError DTO with NoInputDevice, NoOutputDevice,
DefaultConfig, BuildStream, PlayStream, Wav, Io,
UnsupportedFormat arms.
Verified on 2026-05-13 (Linux + cpal + PipeWire). Capture stream
opened against the system default input; build failed against the
auto_null source (typed AudioError::BuildStream returned cleanly,
demonstrating the production error path); fallback to synth fired;
playback drove 24,000 frames to completion through
Rust → cpal → ALSA → pcm_pipewire → PipeWire → auto_null.
Both audio.rs tests pass.
Mobile gap (explicit, NOT closed):
- Android Oboe path not built or run.
- iOS AVAudioEngine path not built or run.
Surfaced finding for the decision register: DEC-011 does not pin an
audio crate. The PoC uses cpal; production code needs an owner
ruling, ideally after the mobile spike closes the gap.
Out of scope: DSP (HPF/NS/AEC/AGC), Opus encode/decode, jitter
buffer, mixer, latency measurement, bit-exact loopback, device
permission flows. These belong to chanora_audio.
Authority: PoC plan §2, DEC-011, SysDes audio subsystem.
Not product code; not promoted into chanora_audio.
37 lines
1.4 KiB
Rust
37 lines
1.4 KiB
Rust
//! Chanora PoC — audio capture / playback spike.
|
|
//!
|
|
//! Authority:
|
|
//! * `docs/architecture/proof-of-concept-plan.md` §2 — Audio
|
|
//! capture/playback spike. Exit criterion: "Capture/playback works
|
|
//! on at least one desktop and one mobile target."
|
|
//! * DEC-011 — platform-native first; Rust/WebRTC-style as fallback.
|
|
//! * SysDes audio subsystem.
|
|
//!
|
|
//! Honest scope:
|
|
//! * Linux desktop is verified here via `cpal` against the system
|
|
//! audio stack (PipeWire / Pulse / ALSA, whichever is active).
|
|
//! * **Mobile targets are NOT verified by this PoC.** That is a
|
|
//! documented gap, not a passed criterion.
|
|
//!
|
|
//! What the PoC shows:
|
|
//! * `AudioCapture` — opens the default input device and writes
|
|
//! 16-bit PCM samples to a WAV file via `hound`.
|
|
//! * `AudioPlayback` — opens the default output device and plays
|
|
//! back a WAV file end-to-end.
|
|
//! * `synth_sine_wav` — when no input device is available
|
|
//! (genuinely headless box), the PoC can still exercise the
|
|
//! playback path against a synthesised waveform. This is what
|
|
//! keeps the spike useful in CI.
|
|
//!
|
|
//! Production code (`chanora_audio`) will own the real DSP chain
|
|
//! (HPF, NS, AEC, AGC, Opus encode/decode, jitter buffer, mixer).
|
|
//! None of that lives here.
|
|
|
|
pub mod capture;
|
|
pub mod playback;
|
|
pub mod synth;
|
|
|
|
pub use capture::{AudioCapture, AudioError};
|
|
pub use playback::AudioPlayback;
|
|
pub use synth::synth_sine_wav;
|