Fix UI polish: scrollbar deselect, web source inline buttons, countdown in Sources, volume click-to-position, meter decay

- Elements scrollbar no longer deselects element on click (check ScrollViewer, not StackPanel)
- Web source: URI + check/revert buttons merged into single DockPanel row, consistent order (✓ rightmost)
- Countdown timer moved from Elements panel to inline in source list row, range clamped to 1-30 min
- Volume slider click-to-position restored: e.Handled prevents IncreaseLarge from overriding position, Thumb drag still works
- Desktop audio meter: asymmetric smoothing (attack 0.2, decay 0.08) for faster response to silence
- 207 tests passing, 0 warnings
This commit is contained in:
2026-08-17 17:36:00 -07:00
parent 28054da70a
commit dc91c855ef
6 changed files with 65 additions and 48 deletions
+14 -4
View File
@@ -1,18 +1,27 @@
namespace ytLive.Services.Audio;
/// <summary>
/// Computes the smoothed mic level (0..1) from captured samples. Pure —
/// Computes the smoothed level (0..1) from captured samples. Pure —
/// unit-tested; the <see cref="AudioMixer"/> only feeds it and forwards the
/// result. RMS-based so it tracks perceived loudness, smoothed so the meter
/// does not flicker.
/// does not flicker. Supports asymmetric smoothing: fast attack (rise) and
/// configurable decay (fall) so the meter responds quickly to silence.
/// </summary>
public sealed class AudioLevelMeter
{
private const float Smoothing = 0.2f;
private float _level;
public float Level => _level;
public AudioLevelMeter(float attackSmoothing = 0.2f, float decaySmoothing = 0.08f)
{
AttackSmoothing = attackSmoothing;
DecaySmoothing = decaySmoothing;
}
public float AttackSmoothing { get; }
public float DecaySmoothing { get; }
/// <summary>
/// Maps a linear level (0..1) onto the meter's display scale: -60 dBFS..0 dBFS
/// spread linearly across 0..1, with +10 dB of input amplification. Linear RMS
@@ -44,7 +53,8 @@ public sealed class AudioLevelMeter
}
var rms = (float)Math.Sqrt(sumSquares / count);
_level = _level * (1 - Smoothing) + rms * Smoothing;
var smoothing = rms > _level ? AttackSmoothing : DecaySmoothing;
_level = _level * (1 - smoothing) + rms * smoothing;
return _level;
}
+1 -1
View File
@@ -57,7 +57,7 @@ public sealed class AudioMixer : IDisposable
_mic = mic;
_loopback = loopback;
_meter = new AudioLevelMeter();
_loopbackMeter = new AudioLevelMeter();
_loopbackMeter = new AudioLevelMeter(attackSmoothing: 0.2f, decaySmoothing: 0.08f);
_log = log;
_micGain = micGain;
_loopbackGain = loopbackGain;