Expand description
M-MIC.1 / AUT-278 — microphone-capture worker thread.
Owns a dedicated OS thread that runs the gst-launch-1.0 autoaudiosrc capture subprocess and pulls PCM chunks into Rust.
Mirror of crate::preview::pipeline for the audio path —
same Drop-safety, cancel-flag-+-join lifecycle, idempotent
mark_running transition.
Just the **gst-into-Rust** layer for microphone PCM:
1. `start_mic_capture(mic_id)` spawns a [`MicCapturePipeline`].
2. Worker opens
[`media::gstreamer_audio::GstreamerAudioCapture::from_microphone`],
triggering the macOS `NSMicrophoneUsageDescription` prompt on
first run.
3. Worker loops `next_chunk()` and advances the
[`MicLifecycle`](crate::audio::MicLifecycle) — `Starting →
Running` on first successful chunk.
4. `stop_mic_capture` drops the worker; `Drop` flips the cancel
flag and joins the thread. The gst child is killed by
`GstreamerAudioCapture`'s own `Drop` impl (per CLAUDE.md
"Drop-kill the child" pattern).
NOT yet shipped:
* **Per-device selection** — `autoaudiosrc` always opens the OS
default; the `mic_id` parameter is plumbed and logged but not
yet used to pick a specific input (deferred to a follow-up;
pattern is `osxaudiosrc device-uid=…` on macOS,
`pulsesrc device=…` on Linux).
* **RMS event emission to Leptos** — the chunks are pulled and
dropped. M-MIC.2 wires the `audio-levels` Tauri event when it
needs the meter.
* **Encode path** — M-RECORD multiplexes mic PCM into the
final encoded stream.Thread-affinity contract — GstreamerAudioCapture owns a
std::process::Child + a ChildStdout reader. Both are Send,
so the worker thread can own the stream exclusively. No Rc /
RefCell anywhere in the type, safe to move into a spawned
thread.
Structs§
- MicCapture
Handle - Tauri-managed handle for the active mic pipeline. Mirror of
crate::preview::CameraPipelineHandle. WrappingOption<MicCapturePipeline>in aMutexrather than anAtomicCellkeeps the dep surface small; contention is bounded by user start/stop clicks, which can’t race meaningfully. - MicCapture
Pipeline - Mic-pipeline worker handle. Owns the spawned thread and a
cooperative cancel flag;
Dropcancels + joins so a panicking caller can never leave a zombie gst child behind.
Constants§
- MIC_
CHANNELS - Native channel count the worker requests. 2 = stereo —
audioconvertupmixes mono inputs and downmixes higher-count inputs cleanly. Matches theM-MIC.0device-enumeration default forchannels == 0. - MIC_
CHUNK_ FRAMES - Frames per
next_chunkcall. 2400 frames @ 48 kHz = 50 ms of audio per chunk — gives the M-AUDIO.METER / AUT-287 audio-level meter ~20 Hz update cadence (one chunk → one RMS sample → onemic-levelevent). Previously 4800 (100 ms / 10 Hz); reduced for meter smoothness without measurable IPC overhead. - MIC_
LEVEL_ EMA_ ALPHA - EMA smoothing factor for the mic level meter (M-AUDIO.METER / AUT-287). Higher = more reactive to transients; lower = smoother. 0.3 balances “responds visibly when you speak” against “doesn’t flicker on micro-pauses.”
- MIC_
SAMPLE_ RATE - Native sample rate the worker requests from gst.
audioresampleconverts on the input side if the device doesn’t natively support it. 48 kHz matches the recorder’s encoder target + theM-MIC.0device-enumeration default forsample_rate_hz == 0.
Functions§
- advance_
to_ 🔒running - Mark the mic lifecycle as
Running(idempotent — already-Runningstays running). Called on each successful chunk; themark_runningtransition is idempotent so we don’t need a “first chunk” guard. - emit_
mic_ 🔒level - Push the smoothed mic level to the webview via the
mic-levelTauri event (M-AUDIO.METER / AUT-287). Failures are swallowed +tracing::trace!’d — at 20 Hz a missed emit is invisible, and surfacing the error to the worker loop would break audio capture for cosmetic event-bus issues. - reset_
lifecycle 🔒 - Drive the lifecycle back to
Idleon shutdown OR on gst-side startup failure (no mic attached, permission denied, etc.). - run_
pipeline 🔒 - The actual worker loop.
&borrows let the public-facingspawnmove cloned values onto the thread without keepingSelfalive on the thread.