Files
ytLlive/ViewModels/index.md
T
gramps b00a4cbd5e Mic audio UX: realtime read-only meter + mic picker
- Meter is a READ-ONLY realtime level display: fill = Math.Min(1, AudioLevel * MicVolume) (AudioLevel = live input from the future mixer, 0 with no input; MicVolume is gain on ambient noise). While the slider is dragged the bar previews the slider position (PreviewMouseLeftButtonDown/Up + LostMouseCapture -> SetVolumeAdjusting); on release it returns to the live level (bounces to 0 with no input). Clicking the meter does nothing.
- MicVolume drives MicMuted (read-only, muted <=> volume 0): sliding to 0 flips the speaker to the red slashed mute icon, sliding up from 0 clears it; speaker button runs volume to 0 or restores the prior level (_volumeBeforeMute, default 0.8) and flashes the meter to the restored position for ~300ms (BeginVolumeFlash/EndVolumeFlash DispatcherTimer, cancelled if the slider is grabbed).
- MIC label opens MicPickerDialog (WinRT audio-capture device enumeration, mirrors camera picker, no new packages); the chosen voice source name shows left-justified INSIDE the meter bar (fill at 75% opacity so text + ruler markings show through).
- New files: Services/IMicrophoneEnumerator, MicrophoneDeviceInfo, WinRtMicrophoneEnumerator; ViewModels/MicPickerViewModel; MicPickerDialog.
- Docs updated (ai.md, TASKS.md, Services/index.md, ViewModels/index.md); build 0 warnings, 65 tests passing
2026-08-07 17:44:16 -07:00

5.3 KiB
Raw Blame History

ViewModels — index

MVVM layer. See schema.md for the memory-map conventions.

File Purpose
MainViewModel.cs The app brain: scenes/elements collections + commands, stream state (IsLive/IsOffline/IsConnected), chat feed, overlays, layout save/open, resolution dropdown (QualityOptions, SelectedQuality, ResolutionHelp, ApplyStreamQuality) that computes the output rect over the 1920×1080 master (OutputRectX/Y/W/H, DimRects, IsOutputCropped, ResolutionBadgeText). Auth: loads the saved DPAPI session at startup (LoadSavedSessionAsync), SignInAsync feeds the GoLive dialog; End Livestream signs out (StopStream clears session + token — crash-safe). Connected account: top-bar avatar/name (AccountAvatarUrl/AccountDisplayName via SyncConnectedAccount, cleared on End). Audio (KISS — the mic is the creator's only audio control; capture pipeline still pending): the sound meter is a READ-ONLY realtime level display: fill = Math.Min(1, AudioLevel * MicVolume) (MeterFillWidth/MeterBrush, green→yellow→red, zone markers at 60%/80%), where AudioLevel (live input, 0 with no input) is fed by the audio mixer once capture lands and MicVolume acts as a gain on ambient noise; while the slider is dragged the bar previews the slider position (SetVolumeAdjusting), returning to the live level on release (0 with no input — clicking the meter does nothing); MicVolume (default 0.8) + read-only MicMuted/MicMuteText/ToggleMicMuteCommandMicVolume drives MicMuted (muted ⇔ volume 0): sliding to 0 flips the speaker to muted, sliding up from 0 clears it; muting stores the prior volume, unmuting restores it (default 0.8 if unknown) and flashes the meter to the restored position ~300ms (BeginVolumeFlash/EndVolumeFlash); MicSourceName = picked voice source, shown left-justified inside the meter bar (the fill runs at 75% opacity so the text + ruler markings show through); OpenMicPickerCommand/PickMicrophone() open the MicPickerDialog; desktop/game audio is automatic (WASAPI loopback at unity, zero UI). Webcam: owns CameraManager (MediaCapture), AddWebcamToActiveSceneAsync (picker → default 480×270 bottom-right placement → acquire; the Windows camera picker ALWAYS opens so the creator chooses — never silently reuses the previous camera; picking a different camera swaps the app-wide identity via SwapWebcamIdentityAsync, same path as ChangeWebcamAsync; propagates the running shared bitmap via CameraManager.GetPreviewBitmap first, so a webcam added mid-session never renders a transparent container; greys out when the active scene already has a config via CanAddWebcamToActiveScene), ChangeWebcamAsync (device swap — ReleaseAllAsync old + re-acquire), ShowWebcamInActiveScene (reveal hidden config / empty-canvas right-click), ReacquireWebcam after layout load (re-propagates the shared bitmap to every config), OnCameraPreviewBitmapChanged forwards the shared bitmap into every WebcamSceneConfig.VideoImageSource; SelectedElement drives the preview overlay + internal ClampWebcamToBounds(config, sceneName) (per-scene size cap seam: 50%-per-dimension everywhere, half-screen-AREA in Chat via MaxWebcamWidthFor/MaxWebcamHeightFor). Scenes (five-scene catalog): empty DB seeds Starting/Live/BRB/Chat/Ending (SceneCatalog.All); AddScene(name) accepts only canonical, missing names; MissingScenes/ShowAddScene drive the "+" button (hidden once all five exist; its menu lists only the missing scenes via AddSceneCommand). Screen backdrop (Live-only by policy): owns ScreenCaptureManager + ScreenCaptureSourceFactory (WinRT GraphicsCapture), internal static EnsureBackdrop heals one per backdrop-enabled scene (gated on Scene.HasBackdrop), ReacquireScreenCaptures/RefreshBackdropAutoCapture/NoteBackgroundWindow key by full-screen game monitor or the primary display via Win32FullScreenDetector (GetDisplays()/PrimaryMonitorIndex()), ChangeBackdropCaptureAsync (OS picker) + SetBackdropCapture(DisplayInfo) (in-app "Capture Display"), RedesignateBackdropAsync re-targets all backdrops and releases orphaned sessions, internal static EnforceBackdropPolicy normalizes Scene.HasBackdrop by name on every load + strips lingering non-Live backdrops, CanChangeBackdrop gates the capture menu to Live (the only scene with a backdrop), BackdropImage hides the preview watermark (ShowPreviewPlaceholder)
GoLiveViewModel.cs Start Stream dialog: account row (saved channel shown with Change Account, or Sign in to YouTube; Start gated on IsSignedIn/IsBusy) + title/description/visibility, start/cancel requests
ReuseImageViewModel.cs Add Image dialog: candidate list (ReuseImageCandidate), reuse/new/cancel
CameraPickerViewModel.cs Add Webcam dialog: async camera list (loading / has / none states), UseRequested(CameraDeviceInfo)/CancelRequested
MicPickerViewModel.cs Select Microphone dialog (voice source): async mic list (loading / has / none states), UseRequested(MicrophoneDeviceInfo)/CancelRequested

Related: Models/index.md for the data; Services/index.md for what the ViewModels drive; base class in Helpers/ViewModelBase.cs.