Address a set of production-facing edge cases discovered during bug hunting.
Included fixes:
- make FrameQueue close handling race-safe by replacing the TOCTOU close check with a dedicated close signal channel
- relax tone frequency validation when tone amplitude is zero, and default tone amplitude to 0 to avoid unintended test-tone output
- validate PI codes consistently whenever provided, and require a PI when RDS is enabled
- harden Icecast reconnect backoff against duration overflow
- prevent duplicate hard-reload goroutines from rapid repeated ingest-save requests
- clamp BS.412 power accumulation against negative float drift before sqrt to avoid NaN gain propagation
These changes focus on shutdown safety, config correctness, reconnect robustness, and long-running DSP stability.
Wire tone frequency, tone amplitude, and audio gain through the live control path so the UI's live-update behavior matches the engine.
This changes the generator live params to carry tone and gain values, propagates them through Engine.UpdateConfig and txBridge.UpdateConfig, and extends the control-plane patch types accordingly.
It also refines the control API behavior:
- avoid holding the server config mutex across tx.UpdateConfig()
- report live=true only when a request contains at least one genuinely live-applicable field
Together these fixes align the UI semantics with the actual runtime behavior and remove a lock hazard in the config update path.
rejectBody() returns true when the request body is acceptable and false when a body must be rejected. The TX and fault-reset handlers treated the return value the wrong way around and returned early on valid empty POST requests. This prevented actions like /tx/stop from running in the normal no-body case.
Update the handlers to only abort when rejectBody() reports an actual rejection, so empty POST control actions proceed as intended.
Add a lock-free stdin PCM ingest path, streaming resampler, stereo-linked limiting and pre-MPX audio filtering, plus the engine/control wiring needed to drive live audio into TX mode. Also document the ingest API and include a helper batch script for piping ffmpeg audio into fmrtx.
Embed a browser-based control surface into the HTTP server and document the live-control API. The new UI exposes TX start/stop, runtime telemetry, frequency, levels, toggles, and RDS text updates against the existing live-config endpoints.
All major TX parameters are now hot-swappable during transmission:
- DSP params (drive, stereo, pilot, RDS levels, limiter) via
atomic.Pointer[LiveParams], loaded once per chunk (~50ms)
- RDS text (PS, RT) via atomic.Value in encoder, applied at
RDS group boundaries (~88ms)
- TX frequency via driver.Tune(), applied between chunks
Zero locks in DSP path. HTTP handler writes atomics, run loop
reads them. Validation happens before store.
New: SoapyDriver.Tune() for live frequency changes.
New: LiveConfigUpdate/LivePatch types for type-safe patching.
New: 4 engine tests for live DSP/freq/RDS updates + validation.
Keep frame sequence numbers monotonic, lock the RDS carrier to the exact pilot phase used by the stereo encoder, and apply the accompanying DSP/control cleanups needed for stable live transmission behaviour.