Skip to main content

Module pipeline

Module pipeline 

Source
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§

MicCaptureHandle
Tauri-managed handle for the active mic pipeline. Mirror of crate::preview::CameraPipelineHandle. Wrapping Option<MicCapturePipeline> in a Mutex rather than an AtomicCell keeps the dep surface small; contention is bounded by user start/stop clicks, which can’t race meaningfully.
MicCapturePipeline
Mic-pipeline worker handle. Owns the spawned thread and a cooperative cancel flag; Drop cancels + joins so a panicking caller can never leave a zombie gst child behind.

Constants§

MIC_CHANNELS
Native channel count the worker requests. 2 = stereo — audioconvert upmixes mono inputs and downmixes higher-count inputs cleanly. Matches the M-MIC.0 device-enumeration default for channels == 0.
MIC_CHUNK_FRAMES
Frames per next_chunk call. 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 → one mic-level event). 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. audioresample converts on the input side if the device doesn’t natively support it. 48 kHz matches the recorder’s encoder target + the M-MIC.0 device-enumeration default for sample_rate_hz == 0.

Functions§

advance_to_running 🔒
Mark the mic lifecycle as Running (idempotent — already- Running stays running). Called on each successful chunk; the mark_running transition 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-level Tauri 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 Idle on shutdown OR on gst-side startup failure (no mic attached, permission denied, etc.).
run_pipeline 🔒
The actual worker loop. & borrows let the public-facing spawn move cloned values onto the thread without keeping Self alive on the thread.