TRAX: toggle refreshes the status light on play/pause, terse scrolling error on failure, fixed quiet volume (0.5), ITraxPlayer seam. Deadlocked test fixed via RunAsync (was GetAwaiter().GetResult on the UI thread). 416 pass / 1 known env flake. Refs: https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/async-programming-model-tap#dont-block-on-async-code (Unit: trax toggle)

This commit is contained in:
2026-10-09 19:05:04 -07:00
parent 87f05ba2ce
commit 5685c5dc3a
6 changed files with 199 additions and 27 deletions
+14
View File
@@ -0,0 +1,14 @@
namespace ytLive.Services.Audio;
/// <summary>Seam over the TRAX background-music player (<see cref="MusicPlayer"/>) so the
/// toggle/status-light logic is testable without a real audio device.</summary>
public interface ITraxPlayer : IDisposable
{
event Action<Exception?>? PlaybackEnded;
string? TrackName { get; }
bool IsPlaying { get; }
float LocalGain { get; set; }
void Load(string path);
void Play();
void Pause();
}
+10 -5
View File
@@ -11,12 +11,17 @@ namespace ytLive.Services.Audio;
/// The desktop/game bar's gain and mute scale <see cref="LocalGain"/>, so the
/// creator hears the game-audio controls work on the music too.
/// </summary>
public sealed class MusicPlayer : IDisposable
public sealed class MusicPlayer : ITraxPlayer
{
/// <summary>The one and only music bed — quiet, no slider (the creator's
/// voice must stay on top; game/music loudness is the ducker's job). The
/// desktop audio bar scales this via <see cref="LocalGain"/>.</summary>
public const float MusicVolume = 0.20f;
/// <summary>The one and only music bed — no slider (the creator's voice must stay on
/// top; that's the ducker's job, −12 dB while they talk). The desktop audio bar scales
/// this via <see cref="LocalGain"/>.
/// <para>2026-10-09: was 0.20 (−14 dB) — "even at max volume I can barely discern it".
/// The creator's track masters at −11.3 LUFS (typical: −9..−14), so 0.20 put the bed at
/// ~−25 LUFS, and ducked under speech ~−37 LUFS: inaudible on stream too, because the
/// loopback carries exactly what plays locally. 0.5 (−6 dB) lands a typical master at
/// ~−15..−20 LUFS — beside the leveled voice (≈ −17 LUFS), and well under it when ducked.</para></summary>
public const float MusicVolume = 0.5f;
private MediaFoundationReader? _reader;
private VolumeWaveProvider16? _volume;