Volume/pan/EQ tab overhaul: one master switch, peer checklist, 16-band parametric EQ
Reworks the per-peer shaping tab (held for next release):
* Renamed the tab to "Volume, pan and EQ for peers"; the Preferences toggle now defaults ON.
* Collapsed the two master switches (Enable EQ / Enable pan) into ONE: "Enable volume, pan and
EQ for all peers" (Alt+E). Volume now obeys it too. PeerDspChain.Build takes a single enabled
flag; Profile.EnableAllPeerShaping replaces the two bools (old ones kept for load-migration).
* Peer picker is now a CheckedListBox: ticking a peer shapes them (per-peer bypass via new
PeerShaping.Enabled, default true); the focused row is the one the controls edit. Effective
shaping = master switch AND that peer's tick. Letter-nav suppressed so keys never toggle a tick.
* Three EQ modes, renamed: "3 band simple EQ", "12 band advanced graphic EQ", and the new
"16 band parametric EQ" (PeerEqMode.Parametric16Band).
* Parametric EQ: up to 16 user bands, each a boost/cut across a start->end range (PeerShaping
.ParametricBands; ParametricToPeaking maps range -> peaking centre+Q, shared by DSP and curve).
Add band dialog (spin-or-type, numeric-only, live preview, OK/Escape); Bands list sorted
bass->treble reading "X Hz to Y Hz, plus/minus N dB"; Delete key / Delete button, multi-select.
Set peer EQ to default clears the parametric list too.
* dB now spoken as words ("plus 3 dB" / "minus 6 dB" / "flat") on the graphic sliders and the
parametric list, since NVDA users typically have punctuation off and never hear a "+".
* New unbound machine-wide global shortcut "Toggle volume, pan and EQ for all peers" (not stored
in any profile) via the hotkey controller + settings store.
* Renamed the Inputs/outputs "Set volume for all received audio" to "Master receive volume".
* Added EqCurveControl: a purely-visual EQ response graph (not focusable, invisible to NVDA).
* Full manual sweep (readme.html + regenerated MANUAL.md).
Build clean; --selftest passes. Deployed to both test folders. Held for next release.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
eaae76d015
commit
033edd776f
@@ -37,29 +37,44 @@ public sealed class PeerDspChain
|
||||
/// and it pays nothing.</summary>
|
||||
public bool IsNoOp => !hasGain && left.Length == 0;
|
||||
|
||||
/// <summary>Builds a chain for one peer from its saved shaping and the profile's two master
|
||||
/// switches. Returns null if there's nothing to do — pan disabled or centred, and EQ disabled or
|
||||
/// completely flat. Runs on the UI thread; the result is swapped onto the audio thread atomically.</summary>
|
||||
public static PeerDspChain? Build(PeerShaping? shaping, bool applyPan, bool applyEq)
|
||||
/// <summary>Builds a chain for one peer from its saved shaping and the profile's single master
|
||||
/// switch (volume + pan + EQ together). Returns null if there's nothing to do — master off, or pan
|
||||
/// centred, volume 100% and EQ flat. Runs on the UI thread; the result is swapped onto the audio
|
||||
/// thread atomically.</summary>
|
||||
public static PeerDspChain? Build(PeerShaping? shaping, bool enabled)
|
||||
{
|
||||
// Pan (only when enabled) and the per-peer volume (always applied) fold into one L/R gain.
|
||||
// Pan is a balance control: it keeps the peer's stereo image (never sums to mono). Centre is
|
||||
// unity on both sides; panning toward one side attenuates the OPPOSITE channel, reaching zero
|
||||
// at the extreme. Volume then scales both sides. So a stereo signal leans left/right and sits
|
||||
// at the level you set, without ever collapsing to mono.
|
||||
float pan = applyPan && shaping is not null ? Math.Clamp(shaping.Pan, -1f, 1f) : 0f;
|
||||
// Master off (or no saved shaping) → the peer passes through untouched.
|
||||
if (!enabled || shaping is null) return null;
|
||||
|
||||
// Pan and the per-peer volume fold into one L/R gain. Pan is a balance control: it keeps the
|
||||
// peer's stereo image (never sums to mono). Centre is unity on both sides; panning toward one
|
||||
// side attenuates the OPPOSITE channel, reaching zero at the extreme. Volume then scales both
|
||||
// sides. So a stereo signal leans left/right and sits at the level you set, never collapsing.
|
||||
float pan = Math.Clamp(shaping.Pan, -1f, 1f);
|
||||
float panL = pan > 0f ? 1f - pan : 1f;
|
||||
float panR = pan < 0f ? 1f + pan : 1f;
|
||||
float vol = shaping is null ? 1f : Math.Clamp(shaping.Volume, 0f, 1f);
|
||||
float vol = Math.Clamp(shaping.Volume, 0f, 1f);
|
||||
float gainL = panL * vol;
|
||||
float gainR = panR * vol;
|
||||
bool hasGain = gainL != 1f || gainR != 1f;
|
||||
|
||||
var l = new List<BiQuadFilter>();
|
||||
var r = new List<BiQuadFilter>();
|
||||
if (applyEq && shaping is not null)
|
||||
var mode = shaping.EqMode;
|
||||
if (mode == PeerEqMode.Parametric16Band)
|
||||
{
|
||||
foreach (var band in shaping.ParametricBands)
|
||||
{
|
||||
if (band is null || MathF.Abs(band.GainDb) < 0.05f) continue; // flat band — skip
|
||||
PeerEqBands.ParametricToPeaking(band.StartHz, band.EndHz, out float centre, out float q);
|
||||
float nyquist = PeerEqBands.MixSampleRate / 2f;
|
||||
if (centre <= 0f || centre >= nyquist) continue;
|
||||
l.Add(BiQuadFilter.PeakingEQ(PeerEqBands.MixSampleRate, centre, q, band.GainDb));
|
||||
r.Add(BiQuadFilter.PeakingEQ(PeerEqBands.MixSampleRate, centre, q, band.GainDb));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var mode = shaping.EqMode;
|
||||
var bands = mode == PeerEqMode.Advanced10Band ? PeerEqBands.Advanced : PeerEqBands.Simple;
|
||||
var gains = mode == PeerEqMode.Advanced10Band ? shaping.AdvancedBandsDb : shaping.SimpleBandsDb;
|
||||
for (int i = 0; i < bands.Length; i++)
|
||||
|
||||
Reference in New Issue
Block a user