Commit Graph
8 Commits
Author SHA1 Message Date
gramps 8d23502220 fix(trax): the fader now actually changes the level — and the meter follows it
Reported: pegging the TRAX fader changes nothing audible, the ladder never reaches red, and the
ladder does not scale with the fader.

Cause (two defects in the same setters):
- UserGain wrote the OUTPUT DEVICE's volume. NAudio's WaveOut ignores that, so the fader was inert.
- LocalGain (the desktop-mute follow) recomputed the sample-chain volume WITHOUT the user gain, so
  every duck/mute silently discarded whatever the creator had set.

Fix: one `EffectiveVolume(user, local)` = MusicVolume × user × local, applied to the sample chain by
both setters. The meter sits AFTER that stage, so the ladder now scales with the fader and can reach
the peak band on a loud track — exactly what pegging should look like. Max user gain is +6 dB, so
"pegged" means the file at unity, not a magic boost.

Guard: MusicLevelTests pins the arithmetic (default half, pegged double, mute multiplies to zero),
that the user gain survives the mute follow, and that nothing may drive the output device's volume
again — the no-op that made this invisible.
2026-10-30 13:32:24 -07:00
gramps 2607ecf210 fix(dock): TRAX ladder gets the mic's ballistics (and a 33 Hz beat)
Reported: the TRAX bar moves, but its sampling looks far slower than the mic meter.

Cause: the TRAX number was a raw instantaneous peak per metering block — a stutter sampled by the UI
timer, with no smoothing. The mic looks fluid because it is smoothed at audio-callback rate.

Fix: the player now smooths its measured level with the SAME curve the mic uses
(MeterScale.NextLevel: fast attack, gentle decay), and the dock's meter clock runs at ~30 Hz instead
of 20. Two ladders that move alike, because they are the same ballistics.

Guard: MeterScaleTests pins the attack/decay behaviour alongside peak-hold.
2026-10-30 13:28:32 -07:00
gramps a54149bd7c feat(dock): TRAX gets its own ladder — measured at the player's output
Creator: "why is there no meter for when trax is playing?"

Routing answer: TRAX rides the desktop loopback, so it already moved the GAME ladder — there was no
separate TRAX signal in the mix. But that ladder cannot answer the question a deaf creator actually
has: "is the music producing sound at all?" The play glyph cannot either — an open file with a dead
decoder still reports Playing.

So the player now measures its own output: \`MusicPlayer\` wraps the volume stage in an NAudio
\`MeteringSampleProvider\` and exposes \`Level\`; \`ITraxPlayer.Level\` keeps the fake honest. The VM maps
it through the same display scale (\`TraxMeterFraction\`), zeroed when not playing, and the TRAX bay
carries a 24-segment ladder above its fader.

This reverses my earlier "no ladder, nothing honest to show" — the honest thing arrived with a real
measurement. TraxBayTests flipped accordingly (it now asserts the ladder AND its source).
2026-10-30 13:22:13 -07:00
gramps 7339df5fcd feat(dock): the TRAX bay — track, play/pause, level, duck badge
Unit 7. The bay shows the track name (left-click toggles playback, right-click opens the picker —
the label stays the control), a play/pause button, the duck badge, and a dB level.

No ladder here, deliberately: nothing measures the music bed, and a decorative ladder would break
the measured-not-painted rule. A test asserts the bay has no SegmentMeter.

The level is real: `ITraxPlayer.UserGain` is a new creator gain multiplied onto the fixed design
volume, separate from `LocalGain` (which follows the desktop mute). The VM's `TraxVolumeDb` is the
same dB scale as the other faders, so unity means the bed's design level.
2026-10-30 13:15:17 -07:00
gramps 5685c5dc3a 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) 2026-10-09 19:05:04 -07:00
gramps 30a193b2c9 TRAX: error display, retain track on failure, gray idle dot
1. Error text shows in red inside the desktop audio meter bar (scrollable)
   when TRAX load or playback fails — user gets feedback, not just a
   silent red dot.
2. LoadTrax no longer nulls _music on error — the track path is retained
   so the user sees what failed and can retry.
3. OnTraxPlaybackEnded now receives the NAudio exception and surfaces it.
4. TRAX dot is green (playing) or gray (idle/no track) — no more yellow
   or red states.

207 tests, 0 warnings.
2026-08-18 08:44:21 -07:00
gramps a0a8331569 TASK 24 post-pause polish batch (creator's 8 review issues, 2026-08-15): (1) desktop/game audio volume slider + (2) mute were no-ops — the AudioMixer's gain Func seams defaulted to unity because the VM never passed them; new AudioGainProvider (Services/Audio/) wires MicMuted/MicVolume/GameMuted/GameAudioVolume into the mixer at construction, read live each mix tick, so sliders and mutes are stream-honest; the game bar also scales TRAX locally via new MusicPlayer.LocalGain (UpdateTraxLocalGain on every game volume/mute change + on load) so the creator HEARS the controls work on the music in the headphones. (3) TRAX played once then stopped — OnPlaybackStopped only looped on Position>=Length (unreliable for MediaFoundationReader); now any clean stop rewinds + replays, errors/explicit stops surface via PlaybackEnded; unused using dropped. (4+6) TRAX moved right of Socials in the footer's left cluster (centered under scenes/sources); the mic cluster is now MIC + meter + mute + volume. (5) mic source persists — LayoutStore gains a Settings key/value table (SaveMicSourceName/LoadMicSourceName); the VM saves on pick and restores before the mixer's first Start, so a restart reconnects the same vetted device (green) or reports it missing (yellow). (7) Backdrop's missing trashcan shifted the edit/eye icons right — new HiddenBoolToVisibilityConverter keeps the trash column reserved (Hidden, not Collapsed) so every source row's icons stay in fixed columns. (8) a 1px hairline with top/bottom padding separates scenes from sources in the left panel. Docs in the same commit: ai.md (gains honest + drive TRAX locally, TRAX loops, mic persistence, footer layout), TASKS.md TASK 24, HANDOFF.md shipped state, ViewModels/index.md. ONE integration test: AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback (real mixer + provider gains through the pipe harness: scaled loopback audible, silence after mute). Build 0 warnings, 197 tests passing 2026-08-15 10:23:49 -07:00
gramps 6d71acef8c 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 2026-08-14 18:09:26 -07:00