Fix heat map scaling, add per-channel amplifier
- Add per-channel amplifier (0-100, 1.0x to 10.0x) applied to intensity before limiter gates strength - Fix heat map scaling: use LimitScaleA/B (valA/DesiredA ratio) instead of valA/200 — was capping colors at green, never reaching orange - Scale heat map intensity by limiter ratio so colors reflect actual output after limiter - Remove dead LastIntensityA/B field (replaced by heat map scaling) - Amp trackbars: 0=1.0x, 100=10.0x, integer value labels - Close button quits app instead of hiding to tray
This commit is contained in:
+82
-4
@@ -31,10 +31,16 @@ public class MainForm : Form
|
||||
readonly Label _lblRecvB;
|
||||
readonly TrackBar _limitBarA;
|
||||
readonly TrackBar _limitBarB;
|
||||
readonly TrackBar _ampBarA;
|
||||
readonly TrackBar _ampBarB;
|
||||
readonly Label _lblLimitA;
|
||||
readonly Label _lblLimitB;
|
||||
readonly Label _lblLimitValA;
|
||||
readonly Label _lblLimitValB;
|
||||
readonly Label _lblAmpA;
|
||||
readonly Label _lblAmpB;
|
||||
readonly Label _lblAmpValA;
|
||||
readonly Label _lblAmpValB;
|
||||
readonly CheckBox _chkScaleA;
|
||||
readonly CheckBox _chkScaleB;
|
||||
readonly CheckBox _chkSwap;
|
||||
@@ -61,7 +67,7 @@ public class MainForm : Form
|
||||
_loopCts = new CancellationTokenSource();
|
||||
|
||||
Text = $"Substation {typeof(MainForm).Assembly.GetName().Version}";
|
||||
ClientSize = new Size(420, 450);
|
||||
ClientSize = new Size(420, 520);
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
MaximizeBox = false;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
@@ -291,7 +297,60 @@ public class MainForm : Form
|
||||
_chkScaleB.CheckedChanged += OnLimitChanged;
|
||||
OnLimitChanged(null, EventArgs.Empty);
|
||||
|
||||
Controls.AddRange(new Control[] { _lblBle, _lblWs, _lblDeviceName, _txtDeviceName, _btnConnect, _chkSwap, _btnTest, _btnLive, _btnPattern, _btnStop, _lblAudioDev, _cboAudioDev, _lblTrack, _lblSendA, _heatA, _lblSendB, _heatB, _lblRecvA, _gaugeRecvA, _lblRecvB, _gaugeRecvB, _lblLimitA, _limitBarA, _lblLimitValA, _chkScaleA, _lblLimitB, _limitBarB, _lblLimitValB, _chkScaleB });
|
||||
_lblAmpA = new Label
|
||||
{
|
||||
Text = "Amp A",
|
||||
Location = new Point(16, 394),
|
||||
Size = new Size(48, 20),
|
||||
Font = new Font(Font.FontFamily, 9, FontStyle.Bold)
|
||||
};
|
||||
_ampBarA = new TrackBar
|
||||
{
|
||||
Location = new Point(68, 390),
|
||||
Size = new Size(212, 45),
|
||||
Minimum = 0,
|
||||
Maximum = 100,
|
||||
TickFrequency = 25,
|
||||
Value = 0
|
||||
};
|
||||
_ampBarA.ValueChanged += OnAmpChanged;
|
||||
FixTrackbarKeys(_ampBarA);
|
||||
_lblAmpValA = new Label
|
||||
{
|
||||
Text = "0",
|
||||
Location = new Point(286, 394),
|
||||
Size = new Size(32, 20),
|
||||
TextAlign = ContentAlignment.MiddleRight
|
||||
};
|
||||
|
||||
_lblAmpB = new Label
|
||||
{
|
||||
Text = "Amp B",
|
||||
Location = new Point(16, 442),
|
||||
Size = new Size(48, 20),
|
||||
Font = new Font(Font.FontFamily, 9, FontStyle.Bold)
|
||||
};
|
||||
_ampBarB = new TrackBar
|
||||
{
|
||||
Location = new Point(68, 438),
|
||||
Size = new Size(212, 45),
|
||||
Minimum = 0,
|
||||
Maximum = 100,
|
||||
TickFrequency = 25,
|
||||
Value = 0
|
||||
};
|
||||
_ampBarB.ValueChanged += OnAmpChanged;
|
||||
FixTrackbarKeys(_ampBarB);
|
||||
_lblAmpValB = new Label
|
||||
{
|
||||
Text = "0",
|
||||
Location = new Point(286, 442),
|
||||
Size = new Size(32, 20),
|
||||
TextAlign = ContentAlignment.MiddleRight
|
||||
};
|
||||
OnAmpChanged(null, EventArgs.Empty);
|
||||
|
||||
Controls.AddRange(new Control[] { _lblBle, _lblWs, _lblDeviceName, _txtDeviceName, _btnConnect, _chkSwap, _btnTest, _btnLive, _btnPattern, _btnStop, _lblAudioDev, _cboAudioDev, _lblTrack, _lblSendA, _heatA, _lblSendB, _heatB, _lblRecvA, _gaugeRecvA, _lblRecvB, _gaugeRecvB, _lblLimitA, _limitBarA, _lblLimitValA, _chkScaleA, _lblLimitB, _limitBarB, _lblLimitValB, _chkScaleB, _lblAmpA, _ampBarA, _lblAmpValA, _lblAmpB, _ampBarB, _lblAmpValB });
|
||||
|
||||
// Tray icon — three cached variants: neutral=gray, active=gold, hot=red-orange
|
||||
_iconNeutral = CreateVoltageIcon(Color.Gray);
|
||||
@@ -353,8 +412,17 @@ public class MainForm : Form
|
||||
{
|
||||
BeginInvoke(() =>
|
||||
{
|
||||
_heatA.AddTick(freqA, intA, _state.LastActiveA);
|
||||
_heatB.AddTick(freqB, intB, _state.LastActiveB);
|
||||
double aScale = _state.LimitScaleA;
|
||||
double bScale = _state.LimitScaleB;
|
||||
var scaledIntA = new byte[4];
|
||||
var scaledIntB = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
scaledIntA[i] = (byte)Math.Round(intA[i] * aScale);
|
||||
scaledIntB[i] = (byte)Math.Round(intB[i] * bScale);
|
||||
}
|
||||
_heatA.AddTick(freqA, scaledIntA, _state.LastActiveA);
|
||||
_heatB.AddTick(freqB, scaledIntB, _state.LastActiveB);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -555,6 +623,16 @@ public class MainForm : Form
|
||||
_lblLimitValB.Text = $"{maxB}";
|
||||
}
|
||||
|
||||
void OnAmpChanged(object? sender, EventArgs e)
|
||||
{
|
||||
var ampA = 1.0 + _ampBarA.Value / 100.0 * 9.0;
|
||||
var ampB = 1.0 + _ampBarB.Value / 100.0 * 9.0;
|
||||
_state.SetAmpA(ampA);
|
||||
_state.SetAmpB(ampB);
|
||||
_lblAmpValA.Text = $"{_ampBarA.Value}";
|
||||
_lblAmpValB.Text = $"{_ampBarB.Value}";
|
||||
}
|
||||
|
||||
void OnSwapChanged(object? sender, EventArgs e)
|
||||
{
|
||||
_state.SwapChannels = _chkSwap.Checked;
|
||||
|
||||
@@ -17,7 +17,7 @@ public class State
|
||||
|
||||
public int ActualA, ActualB;
|
||||
public int LastSentA, LastSentB;
|
||||
public int LastIntensityA, LastIntensityB;
|
||||
public double LimitScaleA, LimitScaleB;
|
||||
public byte[]? LastFreqA, LastIntA, LastFreqB, LastIntB;
|
||||
public bool LastActiveA, LastActiveB;
|
||||
|
||||
@@ -26,6 +26,8 @@ public class State
|
||||
public LimitMode LimitModeA = LimitMode.Clamp;
|
||||
public LimitMode LimitModeB = LimitMode.Clamp;
|
||||
public bool SwapChannels;
|
||||
public double AmpA = 1.0;
|
||||
public double AmpB = 1.0;
|
||||
|
||||
public void SetLimitA(int max, LimitMode mode)
|
||||
{
|
||||
@@ -47,6 +49,24 @@ public class State
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAmpA(double amp)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
AmpA = Math.Clamp(amp, 0.1, 10.0);
|
||||
DirtyA = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAmpB(double amp)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
AmpB = Math.Clamp(amp, 0.1, 10.0);
|
||||
DirtyB = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSignaling
|
||||
{
|
||||
get
|
||||
@@ -115,20 +135,31 @@ public class State
|
||||
var valB = ApplyLimit(DesiredB, MaxB, LimitModeB);
|
||||
LastSentA = valA;
|
||||
LastSentB = valB;
|
||||
LimitScaleA = DesiredA > 0 ? (double)valA / DesiredA : 0;
|
||||
LimitScaleB = DesiredB > 0 ? (double)valB / DesiredB : 0;
|
||||
DirtyA = false;
|
||||
DirtyB = false;
|
||||
|
||||
bool hasA = !StreamA.IsEmpty || (LoopFreqA != null && LoopIntA != null);
|
||||
bool hasB = !StreamB.IsEmpty || (LoopFreqB != null && LoopIntB != null);
|
||||
var (freqA, intA) = PopWave(StreamA, LoopFreqA, LoopIntA);
|
||||
var (freqB, intB) = PopWave(StreamB, LoopFreqB, LoopIntB);
|
||||
var (freqA, intARaw) = PopWave(StreamA, LoopFreqA, LoopIntA);
|
||||
var (freqB, intBRaw) = PopWave(StreamB, LoopFreqB, LoopIntB);
|
||||
|
||||
// Apply amplifier to intensity, clamp to 0-100
|
||||
var ampA = AmpA;
|
||||
var ampB = AmpB;
|
||||
var intA = new byte[4];
|
||||
var intB = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
intA[i] = (byte)Math.Clamp((int)Math.Round(intARaw[i] * ampA), 0, 100);
|
||||
intB[i] = (byte)Math.Clamp((int)Math.Round(intBRaw[i] * ampB), 0, 100);
|
||||
}
|
||||
|
||||
LastActiveA = hasA;
|
||||
LastActiveB = hasB;
|
||||
LastFreqA = freqA; LastIntA = intA;
|
||||
LastFreqB = freqB; LastIntB = intB;
|
||||
LastIntensityA = hasA ? (intA[0] + intA[1] + intA[2] + intA[3]) / 4 : 0;
|
||||
LastIntensityB = hasB ? (intB[0] + intB[1] + intB[2] + intB[3]) / 4 : 0;
|
||||
|
||||
if (SwapChannels)
|
||||
{
|
||||
@@ -140,7 +171,7 @@ public class State
|
||||
(LastFreqA, LastFreqB) = (LastFreqB, LastFreqA);
|
||||
(LastIntA, LastIntB) = (LastIntB, LastIntA);
|
||||
(LastActiveA, LastActiveB) = (LastActiveB, LastActiveA);
|
||||
(LastIntensityA, LastIntensityB) = (LastIntensityB, LastIntensityA);
|
||||
(LimitScaleA, LimitScaleB) = (LimitScaleB, LimitScaleA);
|
||||
}
|
||||
|
||||
return (modeA, valA, modeB, valB, freqA, intA, freqB, intB);
|
||||
|
||||
Reference in New Issue
Block a user