47 lines
2.0 KiB
C#
47 lines
2.0 KiB
C#
namespace ytLive.Services;
|
|
|
|
/// <summary>
|
|
/// Default <see cref="IGameAudioDetector"/>: samples a full-screen monitor
|
|
/// provider (the existing <c>IFullScreenDetector</c>) and the live loopback
|
|
/// level and advances a <see cref="GameAudioHysteresis"/>. The pure transitions
|
|
/// live in GameAudioHysteresis (unit-tested); this wrapper owns the providers
|
|
/// and the raised-changed event. WPF-free — the VM owns the poll timer.
|
|
/// </summary>
|
|
public sealed class GameAudioDetector : IGameAudioDetector
|
|
{
|
|
private const float SoundFloor = 0.005f;
|
|
|
|
private readonly Func<int?> _foregroundFullScreenMonitorProvider;
|
|
private readonly Func<float> _loopbackLevelProvider;
|
|
private readonly Func<DateTime> _now;
|
|
private readonly GameAudioHysteresis _hysteresis = new();
|
|
|
|
/// <param name="foregroundFullScreenMonitorProvider">Returns the monitor a
|
|
/// full-screen foreground window covers, or null when windowed/none.</param>
|
|
/// <param name="loopbackLevelProvider">Current smoothed desktop/game level (0..1).</param>
|
|
/// <param name="now">Clock for the hysteresis windows; injectable for tests.</param>
|
|
public GameAudioDetector(
|
|
Func<int?> foregroundFullScreenMonitorProvider,
|
|
Func<float> loopbackLevelProvider,
|
|
Func<DateTime>? now = null)
|
|
{
|
|
_foregroundFullScreenMonitorProvider = foregroundFullScreenMonitorProvider;
|
|
_loopbackLevelProvider = loopbackLevelProvider;
|
|
_now = now ?? (() => DateTime.Now);
|
|
}
|
|
|
|
public bool IsGameAudioActive => _hysteresis.IsActive;
|
|
|
|
public event Action<bool>? IsGameAudioActiveChanged;
|
|
|
|
public void Poll()
|
|
{
|
|
var before = _hysteresis.IsActive;
|
|
var isFullScreen = _foregroundFullScreenMonitorProvider() != null;
|
|
var hasSound = _loopbackLevelProvider() > SoundFloor;
|
|
_hysteresis.Update(isFullScreen, hasSound, _now());
|
|
if (before != _hysteresis.IsActive)
|
|
IsGameAudioActiveChanged?.Invoke(_hysteresis.IsActive);
|
|
}
|
|
}
|