Bump to v3.0.0: Opus live latency, codec rename, profile-resume after update, save-through-lock
* New "Opus, live latency" codec mode: 2.5 ms frames (120 samples/ch at 48 kHz)
via the float-input encode path. End-to-end codec delay drops to ~5 ms (vs
~12.5 ms at standard 10 ms Opus). Test on LAN: 400 pps/lane, zero missed /
reordered / duplicate packets, ~15 ms one-way saved end-to-end.
* Wire-format change: AudioFormatInfo.FrameDurationMilliseconds renamed to
FrameSamplesPerChannel (int sample-count at announced sample rate). Removes
the lossy 48000*ms/1000 conversion that couldn't represent 2.5 ms. v3 <-> v3
exact; v3 <-> v2 still passes audio (Opus decoder is self-describing from
packet TOC) but v2 side over-sizes its buffer wildly. v2.x profiles auto-
migrate via <120 sentinel rule in RemSoundSettingsStore (anything below 120
is treated as legacy ms and multiplied by 48). Profile JSON key kept as
OpusFrameMilliseconds via [JsonPropertyName] so old profile files still load.
* Codec dropdown rebuilt with use-case names: "PCM 48K 24 bit - uncompressed",
"Opus, broadcast quality - loss tolerant", "Opus, live latency - for jamming
and monitoring". Middle 10 ms option retired; saved 480-sample profiles
collapse to broadcast quality (safer-side default).
* Profile auto-resume after self-update: RemSoundUpdater writes a one-shot
_resume-after-update.txt sentinel containing the active profile title before
exit; Program.Main reads + deletes it on next start and silently loads that
profile, skipping the picker. Helper batch's robocopy /XF excludes the
sentinel and the failure-branch cleans it up if the install aborts. Falls
through to normal startup behaviour (StartWithProfileTitle or picker) if the
sentinel is missing, empty, or names a profile that no longer exists.
* Read-only profile saves now go through on explicit Ctrl+S / File -> Save
with a one-time TaskDialog warning ("Save anyway" / "Cancel" + Do-not-show-
again). Lock continues to suppress the automatic unsaved-changes prompt on
close / profile switch (its main job). AppConfig.SaveOnReadOnlyMessageSuppressed
renamed to SaveOnReadOnlyWarningSuppressed; v2.x suppression flag is silently
discarded since the behaviour changed and the user needs to see the warning
once on each machine.
* Manual (readme.html) updated: codec table rewritten with the three new
choices and corrected bandwidth figures, send-rate description updated, new
sections "The same profile picks up automatically after an update" and
"Saving on purpose while a profile is locked".
* Subsumes the never-separately-released v2.2 work: native Opus encoder
(~97% less per-second memory churn on Opus send path via Concentus.Native),
efficiency tidy-ups (item 4 ASIO probe rate, item 6 WaitHandle, item 7
snapshot cache, items 14/16 heartbeat + discovery), legacy cleanup
(items 30/34/35/36: KeepAlive infrastructure, drift drop/repeat/accumulator
fields, fan-out cache stat). New diagnostic columns cpu/memMB/wsMB/
allocKBps/captureMs/sendMs/recvMs/renderMs gated on Enable-logs.
About dialog updated with v3.0 block at top; v2.2 block retained for the
subsumed work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6d6d6897e4
commit
af0e7c3fff
+45
-10
@@ -35,18 +35,53 @@ internal static class Program
|
||||
|
||||
Profile? profile;
|
||||
string? title;
|
||||
// Auto-load shortcut: if AppConfig.StartWithProfileTitle is set and the named
|
||||
// profile actually exists in the current store, skip the picker entirely and
|
||||
// load that profile directly. This is what the Startup behaviour dialog's
|
||||
// "Start with a specific profile" toggle drives. Combined with the Windows
|
||||
// auto-start registry entry and the StartMinimised flag, it lets the user
|
||||
// boot a machine and have RemSound up and streaming with no clicks. Falls
|
||||
// through to the normal picker if the configured profile no longer exists
|
||||
// (deleted since it was selected, or the profiles folder changed) so the user
|
||||
// isn't stuck.
|
||||
// Auto-load shortcut: two paths.
|
||||
//
|
||||
// (a) Resume-after-update sentinel — a one-shot file written by the updater
|
||||
// just before it relaunches RemSound.exe (see RemSoundUpdater
|
||||
// ResumeProfileSentinelName). Holds the title of whichever profile was
|
||||
// loaded at the moment the update fired. If present, we load that profile
|
||||
// silently and delete the sentinel — so a silent or mid-session update
|
||||
// restores the same session the user was running, without dropping them
|
||||
// at the picker. This takes precedence over StartWithProfileTitle because
|
||||
// a mid-session update may have moved the user away from their configured
|
||||
// startup profile.
|
||||
//
|
||||
// (b) AppConfig.StartWithProfileTitle — the persistent "Start with a specific
|
||||
// profile" preference set via the Startup behaviour dialog. Loaded if (a)
|
||||
// didn't fire. Combined with the Windows auto-start registry entry and the
|
||||
// StartMinimised flag, it lets the user boot a machine and have RemSound
|
||||
// up and streaming with no clicks.
|
||||
//
|
||||
// Either path falls through to the normal picker if the named profile no longer
|
||||
// exists (deleted since it was selected, or the profiles folder changed) so the
|
||||
// user isn't stuck.
|
||||
Profile? autoLoaded = null;
|
||||
string? autoLoadedTitle = null;
|
||||
if (!string.IsNullOrWhiteSpace(appConfig.StartWithProfileTitle))
|
||||
|
||||
var resumeSentinelPath = Path.Combine(AppContext.BaseDirectory, RemSoundUpdater.ResumeProfileSentinelName);
|
||||
string? resumeTitle = null;
|
||||
if (File.Exists(resumeSentinelPath))
|
||||
{
|
||||
try { resumeTitle = File.ReadAllText(resumeSentinelPath).Trim(); }
|
||||
catch { resumeTitle = null; }
|
||||
// Delete the sentinel unconditionally — it's a one-shot. If the load fails
|
||||
// below, the user gets the picker on this launch and a normal start next
|
||||
// time, rather than the sentinel re-firing on every relaunch forever.
|
||||
try { File.Delete(resumeSentinelPath); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(resumeTitle))
|
||||
{
|
||||
try
|
||||
{
|
||||
autoLoaded = store.Load(resumeTitle!);
|
||||
if (autoLoaded is not null) autoLoadedTitle = resumeTitle;
|
||||
}
|
||||
catch { /* fall through to StartWithProfileTitle / picker */ }
|
||||
}
|
||||
|
||||
if (autoLoaded is null && !string.IsNullOrWhiteSpace(appConfig.StartWithProfileTitle))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user