TASK 9 audio milestone: real stream audio + voice filters + auto-duck + free TRAX music — the mixer now feeds the encoder real audio (ffmpeg reads a Windows named pipe, -f f32le -ar 48000 -ac 2 -i \.\pipe\ytllive_audio, replacing anullsrc silence; explicit -map 0:v -map 1:a via EncoderOptions.AudioPipeName), with honest gains reaching the stream (MicVolume scales mic, GameAudioVolume + mute scale loopback, Func<double> seams on AudioMixer), an always-on pure-C# voice chain on the mic BEFORE the meter and mix (LowShelfFilter 120Hz +4dB → HighShelfFilter 8kHz +3dB → NoiseGate 0.005/hysteresis 0.5 → Compressor 0.5 4:1, all TDF2), AutoDucker (mic RMS>0.02 → loopback ×0.25, attack 0.05/release 0.005), and TRAX free background music (MusicPlayer = MediaFoundationReader → VolumeWaveProvider16 at fixed 0.20 → WaveOutEvent via the new sibling NAudio.WinMM 2.2.1 package; plays to the default device so the existing loopback carries it, ducked with game; footer TRAX button with red/yellow/green status dot, left-click toggles/picks, right-click opens the OpenFileDialog picker, tooltip shows the track name; persisted via schema v9 single-row Music). Build-time deviations from the plan (recorded in ai.md + TASKS.md): WasapiCapture in NAudio 2.2.1 exposes no overridable GetDefaultMixFormat so sources run device mix format and the mixer's TinyResampler normalizes any rate to 48kHz (resampler IS the design); FfmpegEncoder.cs untouched — MainViewModel.StopStream calls _audioMixer.StopLive() (pipe EOF) before the frame pump stops; sound-bar relabelled 'Desktop Audio', IsGameAudioBarVisible = game sound OR music playing. Tests: new AudioPipelineTests (DSP/ring-buffer/ducker/resampler units + the ONE integration test Mix_WithFiltersDuckAndGain_Lands_On_AudioPipe reading real pipe bytes; ring-buffer overwrite bug found + fixed), FfmpegEncoderTests/LayoutStorePersistenceTests updated — 196 tests passing, 0 warnings

This commit is contained in:
2026-08-14 18:09:26 -07:00
parent 3d92bd0225
commit 6d71acef8c
22 changed files with 1605 additions and 123 deletions
+11 -6
View File
@@ -2,10 +2,11 @@ namespace ytLive.Services.Encoder;
/// <summary>
/// Builds the FFmpeg command line for a live RTMP push (TASK 4 ship step 3):
/// raw BGRA frames via stdin (paced <c>-re</c>), silent placeholder audio via lavfi
/// <c>anullsrc</c> (the WASAPI step replaces this input), H.264 + AAC encoding, FLV
/// muxing to the ingestion URL. Pure — the encoder just starts
/// <c>ffmpeg.exe [Build(...)]</c>.
/// raw BGRA frames via stdin (paced <c>-re</c>), real audio via the named pipe
/// (TASK 9 — the mixer streams f32le at 48 kHz stereo into <c>\\.\pipe\&lt;name&gt;</c>;
/// WASAPI loopback + the mic ride the pipe instead of the old anullsrc silence),
/// H.264 + AAC encoding, FLV muxing to the ingestion URL. Pure — the encoder
/// just starts <c>ffmpeg.exe [Build(...)]</c>.
/// </summary>
public static class FfmpegArgs
{
@@ -24,8 +25,12 @@ public static class FfmpegArgs
"-video_size", $"{options.Width}x{options.Height}",
"-framerate", options.Fps.ToString(),
"-i", "pipe:0",
"-f", "lavfi",
"-i", $"anullsrc=channel_layout=stereo:sample_rate={options.AudioSampleRate}",
"-f", "f32le",
"-ar", options.AudioSampleRate.ToString(),
"-ac", options.AudioChannels.ToString(),
"-i", $@"\\.\pipe\{options.AudioPipeName}",
"-map", "0:v",
"-map", "1:a",
"-c:v", videoEncoder,
"-b:v", $"{options.BitrateKbps}k",
"-maxrate", $"{options.BitrateKbps}k",