diff --git a/MANUAL.md b/MANUAL.md index d5415c4..7c9c4dc 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -1243,7 +1243,7 @@ Most likely your ASIO latency target is below the network's real-world jitter le ### A peer rebooted or changed address and the sound didn't come back -RemSound follows a peer to its new address on its own. If the address you connected to stops responding but the same peer is still reaching you from a different address on your network — because they rebooted onto a new address, for example — RemSound re-points to the live address within a few seconds and the sound resumes without you doing anything. If it doesn't recover, the peer is genuinely unreachable (off, asleep, or a firewall is blocking the new path). +RemSound keeps trying the address you connected to, so when a peer comes back at the same address — the usual case after a reboot — the sound returns on its own within a few seconds, with nothing for you to do. If the peer comes back at a _different_ address (a new DHCP lease, say), reconnect to it: pick it again from **Discovered peers** , or enter its new address with **Add peer by IP** (Connectivity tab, Alt+A). If the sound still doesn't return, the peer is genuinely unreachable — off, asleep, or a firewall is blocking the path. ### One side says “unreachable” even though sound is flowing diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b609e81..4b99eee 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,29 +1,45 @@ -# RemSound v4.6 +# RemSound v4.7 -Follow your Windows default audio device — for both the sound you receive and the mic you send. +Sound comes back on its own after a reboot — plus a few screen-reader and status improvements. -## Use the Windows default audio device +## Sound returns by itself after a reboot -Both WASAPI device lists now have a new entry at the very top — **"Use Windows default audio device, follows Windows changes"** — in the outputs for received sound, and in the inputs to send. +If RemSound started up before your network or VPN was ready, it could lock onto the wrong machine on your network and sit silent until you closed and reopened it. RemSound now keeps trying the peer you actually chose and connects the moment it answers — no manual reconnect needed. -Tick it and RemSound uses whatever Windows currently treats as the default (the default speakers for received sound, the default microphone for sending), and — the useful part — it **follows that default automatically**. Make a headset your default and RemSound switches to it on its own; unplug it and it moves back, all without touching the list. +(With thanks to the singer who reported this and captured the log that pinned it down.) -It works alongside the specific devices: tick the default entry *and* particular cards, and the sound plays out of (or is captured from) all of them at once. When you first turn it on, RemSound offers to untick the other devices so you use only the default — with a "Don't ask me this again" option. If you hide that question and later want it back, use **Options → Reset the default audio device prompt**. +## Hearing the status, improved + +The **Speak the RemSound status information** hotkey (for screen-reader users) now: + +- reads the status **a line at a time** — peers, ping, uptime, rates, totals — so each part lands as its own short phrase; +- shows big data totals in **gigabytes** once they pass a gigabyte; +- copies the whole status to the **clipboard** on a quick **double press**, so you can paste it to someone. + +The status line now also shows **how much CPU and memory RemSound itself is using**. + +## If RemSound won't start: Install Scripts + +RemSound needs the .NET 10 desktop runtime. On the rare machine that doesn't have it, RemSound can't start to install it itself — so there's now an **Install Scripts** folder next to the program containing a one-click installer (a `.cmd` and a `.ps1`) that fetches and installs the runtime for you. + +## Other fixes + +- The **"what's new"** notes no longer appear a second time after an update that didn't finish. ## Compatibility -**v4.6 talks to v3.3 through v4.5 with no trouble** — the over-the-network format is unchanged, so you don't have to update both ends at once. (Everyone still needs **v3.3 or newer**, where end-to-end encryption came in.) +**v4.7 talks to v3.3 through v4.6 with no trouble** — the over-the-network format is unchanged, so you don't have to update both ends at once. (Everyone still needs **v3.3 or newer**, where end-to-end encryption came in.) ## Install -1. Download `RemSound-v4.6.zip` from this release. +1. Download `RemSound-v4.7.zip` from this release. 2. Close RemSound. 3. Extract the zip **over your existing RemSound folder**, overwriting program files when prompted. The zip is program files only — it won't touch your settings, profiles, logs or recordings. 4. Run `RemSound.exe`. ## Upgrading -**From v3.6 or newer:** Help → Check for updates installs v4.6 with the in-app updater — and if it can't finish, it puts your old version back exactly as it was. +**From v3.6 or newer:** Help → Check for updates installs v4.7 with the in-app updater — and if it can't finish, it puts your old version back exactly as it was. **From v1.9–v3.5:** Check for updates works, but uses your current version's older updater for this one hop. If auto-update has been failing on your machine, install by hand using the steps above. diff --git a/readme.html b/readme.html index 04ac3be..17c92b0 100644 --- a/readme.html +++ b/readme.html @@ -1292,7 +1292,7 @@ RemSound.exe --connect 192.168.1.42
RemSound follows a peer to its new address on its own. If the address you connected to stops responding but the same peer is still reaching you from a different address on your network — because they rebooted onto a new address, for example — RemSound re-points to the live address within a few seconds and the sound resumes without you doing anything. If it doesn't recover, the peer is genuinely unreachable (off, asleep, or a firewall is blocking the new path).
+RemSound keeps trying the address you connected to, so when a peer comes back at the same address — the usual case after a reboot — the sound returns on its own within a few seconds, with nothing for you to do. If the peer comes back at a different address (a new DHCP lease, say), reconnect to it: pick it again from Discovered peers, or enter its new address with Add peer by IP (Connectivity tab, Alt+A). If the sound still doesn't return, the peer is genuinely unreachable — off, asleep, or a firewall is blocking the path.
The health check-ins use the same channel as the audio, so if the sound gets through, the check-ins should too. If one side shows “unreachable” while the sound plays fine, make sure both computers are running the same version of RemSound — an older version on either end can speak a slightly different check-in language.
diff --git a/src/RemSound.App/AboutDialog.cs b/src/RemSound.App/AboutDialog.cs index 9ac211a..e33db42 100644 --- a/src/RemSound.App/AboutDialog.cs +++ b/src/RemSound.App/AboutDialog.cs @@ -20,6 +20,35 @@ internal sealed class AboutDialog : Form /// updates" path. private const string ReleaseNotes = """ + RemSound v4.7 + + Sound now comes back on its own after a reboot. + + If RemSound started before your network or VPN + was up, it could latch onto a different machine + on your network and stay silent until you closed + and reopened it. It now keeps trying the peer you + chose and connects the moment it answers — no + manual reconnect. (Thank you to the singer who + reported this and sent the log that pinned it + down.) + + A few improvements for screen-reader users. The + "Speak the RemSound status information" hotkey + now reads the status a line at a time, shows big + data totals in gigabytes, and a quick double + press copies the status to the clipboard so you + can share it. The status also now includes how + much CPU and memory RemSound itself is using. + + If RemSound ever won't start because the .NET + runtime is missing, there's a new "Install + Scripts" folder next to the program with a + one-click installer for it. + + And the "what's new" notes no longer pop up a + second time after an update that didn't finish. + RemSound v4.6 Follow your Windows default audio device. diff --git a/src/RemSound.App/MainForm.cs b/src/RemSound.App/MainForm.cs index 8304c98..478d33c 100644 --- a/src/RemSound.App/MainForm.cs +++ b/src/RemSound.App/MainForm.cs @@ -322,9 +322,6 @@ public sealed class MainForm : Form private int continuousTuneIntervalSec = 5; private long lastObservedUnderrunCount; private HeartbeatService? heartbeatService; - // Last time TryAdoptLiveHeartbeatAddress re-pointed the sender at a peer's live address. - // Gives a fresh endpoint time to prove healthy before another swap can fire (anti-thrash). - private DateTime lastAddressAdoptionUtc = DateTime.MinValue; // Tracks whether each peer was last considered CONNECTED, for the connect/disconnect cues. // "Connected" now means audio is actually flowing OR the heartbeat is healthy — not the // heartbeat alone (see DetectAndAnnouncePeerHealthTransitions). The bool, rather than the @@ -1201,7 +1198,6 @@ public sealed class MainForm : Form UpdateStatus(); SnapshotLogIfDue(); EnsureRequestedAudioRunning(); - TryAdoptLiveHeartbeatAddress(); // Refresh the Connectivity tab's peer lists from the same 1 Hz tick — replaces // the dialog's old 1.5 s dedicated refresh timer. Each Sync* helper short-circuits // when its signature is unchanged so NVDA isn't spammed with re-announcements. @@ -5277,7 +5273,7 @@ public sealed class MainForm : Form // heard as heavy crackle (Tech Singer's Win7-over-VPN report, 2026-05-31). Keeping the // endpoint pinned while it's still passing heartbeats stops the thrash. A genuine move // (DHCP renewal, Wi-Fi switch) makes the old endpoint go unreachable first, at which - // point the guard lets the move through; TryAdoptLiveHeartbeatAddress backs it up. + // point the guard lets the move through. foreach (var (id, oldEndpoint) in selectedPeerEndpoints.ToList()) { if (!knownPeers.TryGetValue(id, out var peer)) continue; @@ -5366,58 +5362,6 @@ public sealed class MainForm : Form /// messier multi-peer case is left for the user to sort out by hand. Runs once per second /// from the status ticker. 2026-05-15. /// - private void TryAdoptLiveHeartbeatAddress() - { - if (heartbeatService is null || !connected) return; - // Cooldown: adoption re-points the sender; give a freshly-adopted endpoint time to - // prove healthy (or fail) before another swap can fire. - if (DateTime.UtcNow - lastAddressAdoptionUtc < TimeSpan.FromSeconds(10)) return; - - var unreachable = heartbeatService.GetAllPeerHealth() - .Where(h => h.State == PeerHealthState.Unreachable) - .ToList(); - if (unreachable.Count != 1) return; // 0 = nothing wrong; 2+ = ambiguous - - var liveSources = heartbeatService.GetUntrackedPingSources(); - if (liveSources.Count != 1) return; // 0 = no candidate; 2+ = ambiguous - - var deadEp = unreachable[0].AudioEndpoint; - var liveAddr = liveSources[0]; - if (liveAddr.Equals(deadEp.Address)) return; // same machine — nothing to adopt - if (!IsPrivateLanAddress(liveAddr)) return; // never adopt a public / relay source - - // Find the selected-peer entry whose endpoint is the dead one. - var match = selectedPeerEndpoints - .FirstOrDefault(kv => kv.Value.Address.Equals(deadEp.Address) && kv.Value.Port == deadEp.Port); - if (match.Key == Guid.Empty) return; - - // Reuse the dead endpoint's port — a peer that moved on the LAN keeps its audio port. - var newEp = new IPEndPoint(liveAddr, deadEp.Port); - selectedPeerEndpoints[match.Key] = newEp; - var label = selectedPeerLabels.GetValueOrDefault(match.Key, deadEp.Address.ToString()); - logFile.Event($"heartbeat: adopted live address for \"{label}\": {deadEp} unreachable, peer is pinging from {newEp}"); - lastAddressAdoptionUtc = DateTime.UtcNow; - - // ApplyAudioRuntime re-points BOTH the audio sender (SetReceivers) and heartbeat - // tracking (SetTrackedPeers); PushAllowedReceiveSenders re-points the receiver - // allow-list; PushDiscoveryUnicastHints feeds the new address to discovery too. - ApplyAudioRuntime(); - PushAllowedReceiveSenders(); - PushDiscoveryUnicastHints(); - } - - ///