using System.Reflection; namespace RemSound.App; /// /// About dialog. Shows the running version, a short blurb about RemSound and the latest /// release notes (built in below — bumped per release alongside the project's /// property). /// /// Layout follows the same NVDA-friendly conventions the rest of the app uses: a small /// modal dialog with a heading label, a read-only multi-line text box for the notes that /// the user can tab into and arrow through, and a Close button as the AcceptButton / /// CancelButton. Escape dismisses. /// internal sealed class AboutDialog : Form { /// Markdown-ish release notes shown in the About box's scrolling text area. /// Bumped per release. Keep it short — the canonical release notes also live on the /// GitHub Releases page, which the user can reach via the Help menu's "Check for /// updates" path. private const string ReleaseNotes = """ RemSound v3.0.1 Hot-fix for a bug in the "Automatically open my router for incoming connections (UPnP)" tickbox. On some network setups (machines with several network adapters, a VPN connected, or a router that doesn't answer the way RemSound's UPnP library expects) ticking that box could freeze RemSound's window — audio kept flowing, but you couldn't open the window again, even from the system tray. The only way out was to end the process from Task Manager. The fault was that RemSound was doing the router discovery on the same thread that draws the window, so a slow router (or no router answering at all) would block the window until it finished — which sometimes was never. v3.0.1 moves that work off to a background thread so the window stays responsive while RemSound looks for the router. Nothing else has changed from v3.0 — same wire format, same codec list, same everything. If you were already running v3.0 happily, this update fixes a problem you may not have hit; you can install it at your leisure. RemSound v3.0 A big release with two things you'll actually notice: 1) A new "live latency" Opus mode. RemSound now sends sound in tiny 2.5-millisecond chunks instead of the usual 10 or 20 ms. End-to-end delay drops to about 5 ms of codec delay — close to PCM — perfect for playing along with someone in real time. Uses a bit more network bandwidth than the regular Opus mode but still a fraction of PCM. Best on a clean wired network. Pick it from the codec list as "Opus, live latency — for jamming and monitoring". 2) After RemSound updates itself, it now reopens on the same profile you were using. So if a silent update fires mid-session, the session drops briefly while the new files swap in, then RemSound reopens with the same devices, peers and settings — you don't see the profile picker, and you don't have to be at the computer when it happens. The next time you launch RemSound yourself, your normal startup choice applies as before. Other changes: * The codec list is now three choices, with clearer names: PCM 48K 24 bit (uncompressed); Opus, broadcast quality (loss tolerant); Opus, live latency (the new low-latency mode). The old "Opus lower quality (10 ms)" middle option has been retired — it sat between the other two without a clear reason to pick it. If your saved profile was using it, RemSound silently picks broadcast quality for you — slightly more delay, more loss tolerance. * Save on a locked (read-only) profile now goes through when you ask on purpose. The lock still suppresses the automatic "save your changes?" prompt on close (its main job), but if you press Save deliberately, a one-time warning explains what's about to happen and lets you confirm or cancel. Once you tick "do not show again", future deliberate saves on a locked profile go through silently. Save as... is unchanged — it always works. Also includes everything from the never-separately- released v2.2 work: a native Opus encoder that puts much less load on Windows' memory manager (about 97 % less per-second memory churn while sending Opus), various small CPU and memory tidy-ups, and new diagnostic log columns (cpu, memMB, wsMB, allocKBps, captureMs / sendMs / recvMs / renderMs). Compatibility note — please read: v3.0 changes how RemSound describes audio frame sizes to other RemSound machines on the wire. Two v3.0 machines talk to each other perfectly. A v3.0 machine talking to a v2.x machine will still pass audio, but the v2.x side will build up too much buffer and latency will be very high. To avoid this, update BOTH machines to v3.0. The auto-updater on v1.9 and later will handle the upgrade for you, but the timing matters: if one machine updates before the other, expect a short period of high latency until the second machine catches up. If you had previously ticked "do not show me this message again" on the v2.x "save was blocked on a read-only profile" dialog, that suppression doesn't carry over — you'll see the new one-time warning once per machine. That's deliberate; the behaviour changed and you need to know. RemSound v2.2 A maintenance release that makes RemSound use less of your computer's CPU and memory, especially when sending with the Opus codec. No new features to learn, no settings have changed, and audio sounds exactly the same. Wire format and audio pipeline are unchanged — every version from v1.5 to v2.2 still talks to every other version cleanly. What's lighter on your computer: * Opus sending uses much less memory. RemSound's Opus encoder used to put quite a lot of work on Windows' memory manager — about 4 megabytes per second of "throwaway" memory churn while sending Opus audio. v2.2 ships a native build of the same encoder that does its work in a tighter, faster way. The audio you hear is identical (it really is the same encoder, just packaged better); the memory churn drops by about 97 per cent. On laptops you should see less background CPU when streaming Opus, and longer sessions are less likely to see brief pauses while Windows tidies up memory. * Smaller all-round efficiency tidy-up. A handful of small fixes — RemSound checks the audio-device list less often, reuses some small bits of memory it used to make fresh each time, and skips some paperwork on the receive side when there's nothing to do. Each one is small on its own; together they cut RemSound's everyday memory churn modestly. * Removed some old leftover code that was retired months ago but still lived on as zero-valued columns in the diagnostic log. Same behaviour, cleaner files. For people who use the diagnostic logs: * Several new columns. "cpu" shows how much of one CPU core RemSound just used. "memMB" and "wsMB" are its memory footprint. "allocKBps" is the per-second memory-churn rate. "captureMs / sendMs / recvMs / renderMs" show how busy each of the four audio threads is. All of this only writes to the log when Enable logs is ticked; with logs off it costs nothing. * "fanCacheMs", "driftDrop", "driftRep" and "driftAcc" columns have been removed — they were always zero after the playback engine changed in May. No bug fixes in v2.2 specifically — everything carried over from v2.1's UPnP, lock-profile, wake-from-sleep and hibernate fixes is still in place. What's new: * Automatic router port opening (UPnP). RemSound can now ask your router to let peers on the internet reach you, so you don't have to set up port forwarding by hand. Off by default. Tick the new "Automatically open my router for incoming connections (UPnP)" box in Preferences to turn it on. A status line right below the tick tells you what happened — found your router and opened the port, found your router but the port couldn't be opened, or no router found that supports this feature. If your internet provider puts you behind a second layer of NAT (common on mobile broadband and some cable connections), the status line will say so and suggest using Tailscale or the relay instead. * Check for updates on startup. New checkbox in Preferences, on by default. Shortly after RemSound launches it has a quiet look for a new release. Combined with "Silently install updates", this means leaving RemSound to keep itself up to date without you ever needing to think about it. * Brief notice before a silent update installs. When RemSound finds an update at startup and is set to install silently, it now shows a small window with the version it's about to install and an 8-second countdown. Press Enter (or wait) to install now, "Skip this version" to leave the update for another day, or "Postpone" to try again at the next check. Without this notice, the app could silently close on you a few seconds after launch and you'd have no idea why. * Lock profile (read-only). New tickable item in the File menu (Alt+F, L). When ticked, anything you change while RemSound is running stays in this session and is forgotten on close — your saved profile is left untouched, and there's NO "save changes?" prompt on exit. Useful when you have a default profile you want to keep clean even if you toggle send/receive or volume during the day, and essential if a save prompt could block shutdown when you can't reach it (screen reader gone, remote session dropped, machine hibernating). Saved per profile, off by default, toggle as often as you like. Save As on a locked profile produces an unlocked copy you can edit normally. * "Cue sounds" in Preferences is now labelled "Audio cue sounds" for clarity. Bug fixes: * No sound after the computer wakes from sleep. On many setups (especially USB audio interfaces), waking the computer left RemSound's audio engine in a state where it looked like it was running but no sound actually came out — you'd have to quit and reopen RemSound to get audio back. RemSound now notices when the system has woken up, waits a moment for the USB devices to settle, and rebuilds its audio engine automatically. The "Loading audio driver" window briefly appears during the rebuild so you can see it's happening. * Receiver audio silent after waking from hibernate. A follow-up to the wake-from-sleep fix above: on hibernate (rather than ordinary sleep) the ASIO receive output's tick selection could be silently wiped during hibernation entry, leaving the receiver running silent on resume even though everything looked normal in the logs. Fixed by recognising the transient driver-disappeared state and preserving the user's tick until the driver comes back. RemSound v2.0 A smoother startup when a profile uses an ASIO driver. No wire-format or audio-pipeline changes — v1.5 through v2.0 peers interoperate. Change: * Opening an ASIO driver takes a couple of seconds, and during that time the main window used to look frozen on startup. RemSound now shows a small "Loading audio driver" window while the driver opens, so startup no longer looks hung. The window then opens as normal. Profiles that don't use an ASIO driver are unaffected — they still start instantly, with no extra window. RemSound v1.9 Critical auto-updater fix. No wire-format or audio-pipeline changes — v1.5 through v1.9 peers interoperate. Bug fix: * The auto-updater now actually installs updates. Every previous version had a fault in the update step: the folder path handed to the file-copy command ended in a backslash, which Windows mis-read, so the copy was rejected and no files were ever replaced. Check for updates would download the new version but never apply it. That copy step is fixed. Because the fault was in the OLD version doing the updating, v1.8 and earlier cannot install v1.9 for you — install v1.9 by hand once (download the zip, extract it over your RemSound folder). From v1.9 onward, Check for updates installs every update automatically. RemSound v1.8 Updater polish and a rewritten user manual. No wire-format or audio-pipeline changes — v1.5 through v1.8 peers interoperate. Changes: * Check for updates looks further down the release list, so it reliably finds the newest RemSound version. * An update can no longer overwrite your own settings, profiles, logs or recordings — it only replaces program files. * After a successful update the install folder is left tidy: temporary update files, the update log and any old failure note are cleared automatically. * If an update ever fails, the note it leaves (update-failed.txt) is now written in plain language with clear steps to follow. * The user manual (press F1) has been rewritten in plain language throughout. RemSound v1.7 Updater fix. No wire-format or audio-pipeline changes — v1.5, v1.6 and v1.7 peers interoperate. Bug fix: * Check for updates now reliably finds RemSound releases. RemSound and the relay server share one GitHub repository; the server's releases use "server-" tags. The updater previously looked only at the single newest release of any kind, so a server release could derail it (it would misread the version and report "up to date"). It now scans the release list and considers only RemSound client versions, ignoring server releases, drafts and pre-releases. RemSound v1.6 Three reliability fixes. No wire-format or audio-pipeline changes — v1.5 and v1.6 peers interoperate. Bug fixes: * Peer address recovery. If the address you connected to goes unreachable — a peer rebooted onto a new IP, or a computer name resolved to a stale address — RemSound now follows the peer to the live address it is heartbeating from, instead of sending audio into the void. Recovers on its own within a few seconds. Limited to private-network addresses so a relay can never be mistaken for a moved peer. * Fixed a crash that could happen when a peer reconnected (e.g. after rebooting). The Connectivity peer list could be read mid-rebuild with a stale index and bring the app down from the status timer. * Fixed runaway memory and CPU on a long-running receiver. Decoder sessions orphaned by peer reconnects were not being reclaimed — over hours they piled up, each holding a multi-megabyte buffer and costing render-thread time every callback. They are now reaped once idle, with a hard cap as a backstop. RemSound v1.5 Menu reorganisation, multi-peer audio-routing fix, recording fix for BothIndependent mode, and Ctrl+O for Open profile. Wire format and audio pipeline are unchanged — v1.4 and v1.5 peers interoperate. Bug fixes: * BothIndependent recording: when both WASAPI and ASIO output devices were ticked, recordings came out garbled and twice the expected duration. The recorder taps fired from both lanes' render reads and the writer thread appended both streams into one ring as if they were sequential audio. Recorder now has per-lane rings and mixes them in the writer thread. * A peer announcing audio on the WASAPI lane was inaudible when the receiver only had an ASIO output device ticked (and vice versa). Sessions whose announced lane has no active output now fall through to whichever lane IS being read. Menu reorganisation: * New Options menu (Alt+O) holds: Recording settings, Keyboard shortcuts, Startup behaviour, Preferences. These used to be scattered across File menu (Keyboard shortcuts, Preferences), Record menu (Recording settings), and inside the Preferences dialog (Startup behaviour). * Record menu mnemonic moved from Alt+O to Alt+K (rendered as "Record (Alt+K)") so Alt+O could go to Options. K is unusual for "Record" but the Record menu doesn't have a natural free letter — Alt+R is taken by Receive audio. * File menu: new Recent profiles submenu (Alt+F, R). Lists the last five profiles you've opened, most-recent first. Press 1..5 while the submenu is open to jump to a slot. File → Rename current profile moves to Alt+F, M, and Minimise to tray moves to Alt+F, N, to free up R for the new submenu. * Lock to audio clock (Audio profile tab) was Alt+K; now Alt+D (the D in "audio") since the Record menu won Alt+K. UX additions: * Ctrl+O opens the Open profile dialog (matches the menu chord). Previously the menu had no global shortcut. * New global hotkey: Start / Stop recording. Pickable from Options - Keyboard shortcuts. Unbound by default. Works system-wide — RemSound doesn't need keyboard focus. Recording feature unchanged in this release — the dialog layout, formats (WAV / MP3 / OGG-Opus / FLAC), and tap-points all the same as v1.4. RemSound v1.4 Recording-settings dialog cleanup and a few mnemonic adjustments. No wire-format or audio-pipeline changes — v1.4 and v1.3 peers interoperate. Recording settings dialog: * Channel mode is now its own dedicated listbox (Alt+C) instead of being folded into every attribute row. WAV shrinks from 6 attribute rows to 3, MP3 and OGG-Opus from 8 to 4, FLAC from 4 to 2. * FLAC compression level (0..8) is now selectable in its own listbox (Alt+L). Previously hard-fixed at the libFLAC default of 5. The list is shown only when the file format is FLAC. Friendly tags on the endpoints: "0 — fastest encode, biggest file", "5 — default (libFLAC reference)", "8 — slowest encode, smallest file". All levels produce bit-identical audio — pure encode-time vs file-size tradeoff. Record menu mnemonics: * Start / Stop recording is now Alt+O, R (was Alt+O, S). Matches the Ctrl+R global toggle so the same letter does the same job from either entry point. * Recording settings is now Alt+O, S (was Alt+O, T). Reads more naturally now that the R slot is freed. * Open folder (O) and Change folder (C) unchanged. Dialog mnemonic adjustment: * Cancel button in the Recording settings dialog moved from Alt+C to Alt+N so the Channels listbox can take Alt+C. Esc still dismisses the dialog the way it always has. RemSound v1.3 Updater hardening. The v1.2 self-updater silently failed on installs inside Dropbox-synced folders because Dropbox held write locks on the existing files during the brief window between the parent exiting and the helper script copying the new files in. Robocopy gave up after 5 seconds and the helper relaunched the old binary anyway — so the user saw the same version they started with after pressing "Yes" on the install prompt. What's new: * Helper script retries robocopy for up to 60 seconds per file (was 5). Dropbox lock release happens reliably within that window in practice. * Helper now checks robocopy's exit code. On a true failure (exit code 8 or higher), it writes update-failed.txt next to RemSound.exe with diagnostic detail and does NOT relaunch the old binary. Earlier versions silently relaunched the unmodified old binary, hiding the failure. * Helper writes a step-by-step log to _update-helper.log in the install folder. If a future update goes wrong this is where the trace lives. * Stale failure markers are cleared at the start of every new update attempt, so a successful run leaves the install folder clean. Wire format, audio pipeline, and recording feature are unchanged from v1.2. RemSound v1.2 Recording, sound cues, and receiver-side drift compensation. This release is mostly about features that sit on top of the v1.1 transport — the wire format and audio pipeline are unchanged, so v1.1 and v1.2 peers interoperate. What's new: * Recording. New Record menu (Alt+O) — Start / Stop with Ctrl+R, dedicated settings dialog, per-profile choice of source (received only, sent only, or both), file format (WAV, MP3, OGG-Opus, FLAC), bit depth or bitrate, mono or stereo, and recordings folder. Files are crash-resilient: WAV re-patches its RIFF header every 5 seconds, MP3 / FLAC / OGG-Opus all produce well-formed truncated files if the app crashes mid-recording. * OGG-Opus and FLAC encoders now wired up — they were stubs in earlier builds. OGG-Opus reuses the same Concentus encoder as the wire path; FLAC uses pure-managed CUETools FLAKE (no native DLL). * Recording start / stop sound cues. Plays a short ding when recording transitions on or off. Played via the default Windows output device, separate from the recording pipeline, so a normal recording does not include the cue. * Sound-cue Preferences. The old single "Mute connect / disconnect sounds" checkbox is replaced by a per-cue CheckedListBox: Connect / Disconnect / Recording start / Recording stop, each independently toggleable. Old profile settings that had the legacy mute on are honoured on first load. * Receiver-side drift compensation switched from discrete single-frame splices to a continuous WdlResampler running at a slowly-updated rate ratio. Smooths out long-session clock drift between sender and receiver without the occasional 21 µs splice the v1.1 corrector emitted. UI changes: * Record menu moved to Alt+O (Rec&ord). The old Alt+R chord conflicted with the Receive audio checkbox on the main form. Inside the menu the item mnemonics are unchanged (S / T / O / C for Start, settings, Open folder, Change folder). * Auto-tune interval combo label and accessible name are now mode-aware. In BothIndependent mode it reads "Auto-tune interval — WASAPI and ASIO" so it's clear the same combo drives ticks for both lanes; each lane still independently tunes to its own target latency. Earlier builds also had a bug where ticking ASIO auto-tune alone left this combo greyed out — fixed. Diagnostics (only active with Enable logs ticked): * Per-stage discontinuity probes — sender raw capture, sender pre-encode (now per lane in BothIndependent), receiver post-decode, receiver post-ring, receiver post-resampler. Lets a log inspection localise where a click was introduced (capture / wire / decode / playout). * Wire-level sequence tracking on each PCM stream: in-order / missed / reordered / duplicated packet counts in the diag log. Healthy LAN should show all-zero except in-order; non-zero on the others points to transport issues rather than software. * Clipped-sample delta in the diag log. Bug fixes: * Auto-tune interval combo no longer greys out when only ASIO auto-tune is ticked in BothIndependent. RemSound v1.1 Priority and performance hardening, plus always-on network packet priority. Aimed at the cold-start latency jitter you sometimes hear when the machine has been idle. What's new: * Use CPU and Windows performance settings in high priority mode (Audio profile tab, Alt+U). Off by default; opt in per profile. Combines eight Windows power and scheduling levers so the OS can't decide we're idle and downclock: process power-throttling off (including timer-resolution honouring), an execution-required power request, scheduler quantum pinned to 1 ms, process priority raised to High, working-set minimum locked, memory priority normalised, and SetThreadExecutionState set to keep the system awake. Fully reversed on toggle-off and on app exit. Costs battery on laptops — that's why it's off by default. * Network packet priority — always on, no toggle. The outbound UDP socket is attached to Windows' built-in qWAVE service at Voice priority (DSCP 46, WMM Voice on Wi-Fi). On a busy Wi-Fi access point, RemSound's packets win medium-contention against other clients; on wired LAN, switches that honour DSCP give us scheduling priority. Neutral across the public internet (most ISPs strip DSCP at the edge). Silent fallback if the qWAVE service isn't available on stripped-down Windows images. * Sender and receiver UDP kernel buffers bumped to 1 MB each way — enough to absorb roughly 30 ms of GC or scheduler stall without dropping datagrams. * MMCSS audio-thread priority on the network listener, mix loop, and render thread bumped from High to Critical (always on, no toggle). Bug fixes: * Toggling "Enable logs" in Preferences no longer marks the current profile as dirty. Logs are a machine-local setting, so the spurious flag was triggering the "unsaved changes?" prompt on exit after just toggling logs. RemSound v1.0 Initial public release. Highlights: * Low-latency peer-to-peer audio over UDP. WASAPI for any Windows audio device, and a parallel ASIO lane for pro audio interfaces (Audient, Komplete Audio, Focusrite, RME). Each lane keeps its own native callback latency. * Pick an ASIO driver from the dropdown at the top of the Audio inputs and outputs tab to bring ASIO into the pipeline; select "(none)" to run WASAPI-only. * Profile system. Save your entire setup — device ticks, peers, codec, latency targets, hotkeys, ASIO driver choice — into a JSON file. Pick which profile to load at every launch. * Continuous auto-tune on either lane. Watches receive jitter and nudges the latency target up or down to stay click-free without forcing you to overshoot. * Opus inband FEC. Single-packet losses recover transparently in both Opus modes; you don't hear them at all. PCM is also available for clean LAN connections. * Remote control. Configurable global hotkeys can nudge a peer's RemSound volume or their Windows default-output-device master volume, opt-in on the receiver. * Built-in self-updater. Optionally polls GitHub for newer releases on a schedule you set; can install them silently if you want. See the user manual (Help menu, or F1 from anywhere in the app) for full details on every control, the keyboard shortcuts, and the troubleshooting guide. """; public AboutDialog() { Text = "About RemSound"; FormBorderStyle = FormBorderStyle.FixedDialog; MinimizeBox = false; MaximizeBox = false; ShowInTaskbar = false; StartPosition = FormStartPosition.CenterParent; KeyPreview = true; ClientSize = new Size(560, 420); var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "?"; var headingLabel = new Label { Text = $"RemSound version {version}", AutoSize = true, Font = new Font(SystemFonts.MessageBoxFont!.FontFamily, 11f, FontStyle.Bold), AccessibleName = $"RemSound version {version}", }; var notesBox = new TextBox { Multiline = true, ReadOnly = true, TabStop = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Vertical, BorderStyle = BorderStyle.FixedSingle, Text = ReleaseNotes, AccessibleName = "Release notes (tab into and arrow to read)", }; var closeButton = new Button { Text = "Close", AutoSize = true, DialogResult = DialogResult.OK, TabIndex = 1, }; closeButton.Click += (_, _) => Close(); notesBox.TabIndex = 0; var root = new TableLayoutPanel { Dock = DockStyle.Fill, Padding = new Padding(12), ColumnCount = 1, RowCount = 3, }; root.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); var buttons = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.RightToLeft, AutoSize = true, }; buttons.Controls.Add(closeButton); root.Controls.Add(headingLabel, 0, 0); root.Controls.Add(notesBox, 0, 1); root.Controls.Add(buttons, 0, 2); Controls.Add(root); AcceptButton = closeButton; CancelButton = closeButton; KeyDown += (_, e) => { if (e.KeyCode == Keys.Escape) { Close(); e.SuppressKeyPress = true; e.Handled = true; } }; } }