Service startup volume: unmute + set level on boot or on every service start

Feature (requested 2026-07-26): Additional service options gains 'Set the machine's
volume when the service starts' - a checkbox, a percent field (0-100, also unmutes),
and a WHEN list: 'Only the first start after each boot' (default) or 'Every time the
service starts'. For an unattended machine that boots muted or turned down, the
service makes it audible again with nobody at the keyboard; boot-only mode means a
mid-day manual service restart never blasts the volume while someone's using the box.

Mechanics: settings live machine-wide beside the service logging flag (the service
reads them fresh each start - no restart needed to change them); boot identity comes
from now-minus-uptime persisted in a marker file, so 'first start after boot'
survives same-boot service restarts and re-fires after a real reboot; the marker is
only written on a SUCCESSFUL apply, so a boot-time audio-stack race retries on the
next qualifying start. Applies via the same endpoint-volume helper the remote-control
commands use (master endpoint volume is device-global, so session 0 works). Outcome
logged to the always-on service events log.

The new round-trip test immediately caught SaveLoggingEnabled clobbering the volume
fields in the shared settings file - rewired both savers to load-modify-save. The
dialog audit picks up the new controls (mnemonics + names) automatically.
Gate 65/65.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-26 23:45:55 +01:00
co-authored by Claude Fable 5
parent a46af2ee51
commit bb94a56109
6 changed files with 253 additions and 18 deletions
+10
View File
@@ -64,6 +64,16 @@ internal static class SystemVolumeHelper
}
}
/// <summary>Sets the default render device's master volume to <paramref name="percent"/> and
/// unmutes it, in one call. The service's startup-volume option uses this so a machine that
/// booted muted (or was left turned down) is audible again without anyone at the keyboard.
/// Master endpoint volume is device-global, so this works from session 0 too.</summary>
public static bool TrySetVolumeAndUnmute(int percent) => TryDo(v =>
{
v.MasterVolumeLevelScalar = Math.Clamp(percent, 0, 100) / 100f;
v.Mute = false;
});
private static bool TryDo(Action<AudioEndpointVolume> action)
{
lock (cacheLock)