Host capture gain works on punktfunk/1, and boosting no longer hard-clips #229
Merged
enricobuehler
merged 1 commits from 2026-08-14 17:20:53 +00:00
worktree-audio-soft-limit-gain into main
1
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6eb5edaff4 |
feat(audio): capture gain on punktfunk/1, and a soft knee instead of the clamp that made boosting a trap
android / android (pull_request) Failing after 1m5s
apple / swift (pull_request) Successful in 1m57s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / docs-site (pull_request) Successful in 1m37s
ci / web (pull_request) Successful in 1m58s
ci / bun-nix (pull_request) Successful in 41s
ci / rust-arm64 (pull_request) Failing after 3m56s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m50s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m41s
ci / rust (pull_request) Successful in 21m14s
`PUNKTFUNK_AUDIO_GAIN` had two defects that compounded. It existed only on the GameStream plane, so on native `punktfunk/1` it silently did nothing — and since WASAPI loopback is tapped UPSTREAM of the endpoint's master volume, turning the host's speaker slider up does not change the level a client receives either. Between the two there was no host-side way at all to lift a quiet desktop mix on the protocol that matters. And where it did apply it was `(s * gain).clamp(-1.0, 1.0)` — a hard clip. Flat-topping a waveform is a first-derivative discontinuity, which radiates harsh high-order harmonics, so any operator who pushed past roughly 1.5x heard gross distortion long before reaching the level they were chasing. A field report of "+18 dB and everything warbles" is the expected output of that line, not a fault anywhere downstream of it. `punktfunk_core::audio::apply_gain` replaces the clamp with a tanh soft knee above 0.7 (~-3.1 dBFS), chosen for three properties: C1-continuous where the branches meet (slope 1 on both sides, so the onset of limiting is not itself an audible event), bounded by construction (asymptotic to 1.0, and +-inf maps to +-1.0, so nothing leaves out of range), and odd-symmetric (benign harmonics, no DC). It is a memoryless waveshaper, so it costs zero latency in the realtime encode path. Unity is a no-op inside `apply_gain` itself, not merely at the call sites, so the default wire stays byte-for-byte identical and a future caller that forgets to gate cannot quietly bend every peak. `capture_gain` is now shared by both planes and rejects the two values that are always typos: non-positive (would invert or mute) and above 8.0/+18 dB (capped, and said out loud). This buys headroom, NOT loudness. It cannot close a peak-to-loudness gap against already-limited broadcast content; that needs a compressor with a real time constant, which this deliberately is not, and the docs say so. `SOFT_LIMIT_KNEE` is excluded from cbindgen: it is host-side capture processing that no C embedder can act on, and exporting it would add a bare `#define` against the config's own R21 rule. Verified by regenerating `include/punktfunk_core.h` — byte-identical, ABI 19 untouched. |