Bump to v1.2.0: recording, sound cues, drift compensation, diagnostics

New user-facing features:

* Recording. Dedicated Record menu (Alt+O — moved from Alt+R to
  avoid clashing with the Receive audio checkbox), Start/Stop on
  Ctrl+R, settings dialog, per-profile source / format / bit-depth /
  channel-mode / folder. Three source modes (received only, sent
  only, both). Files are crash-resilient — a process crash
  mid-recording leaves a playable file containing everything up to
  the last header refresh (~5 seconds).

* Four output formats, all functional:
    - WAV: 16/24-bit PCM or 32-bit float, custom writer with
      periodic RIFF re-patching.
    - MP3: LAME 128–320 kbps CBR (via NAudio.Lame).
    - OGG-Opus: 96–256 kbps VBR (via Concentus.Oggfile, reusing the
      Concentus encoder from the wire path).
    - FLAC: 16/24-bit lossless (via CUETools.Codecs.FLAKE — pure
      managed, no native DLL).

* Recording start/stop sound cues. record start.wav and
  record stop.wav play around the recording transition. Played via
  System.Media.SoundPlayer to the default Windows output, separate
  from the recording pipeline so a normal recording does not contain
  the cue.

* Per-cue Preferences. The old single "Mute connect/disconnect
  sounds" checkbox is replaced by a CheckedListBox: Connect /
  Disconnect / Recording start / Recording stop. Old profiles with
  the legacy MuteConnectionCues=true are honoured on first load via
  a migration path in the new Load* helpers.

* Receiver-side drift compensation switched from discrete
  single-frame splices to a continuous WdlResampler at a smoothed
  rate ratio. SessionPlayout.cs rewrite.

Diagnostics (only active with Enable logs ticked):

* Per-stage discontinuity probes — sender raw capture (per backend,
  PushModeWasapi + Asio both wired), sender pre-encode (now per
  lane in BothIndependent, fixing a cross-stream artefact), receiver
  post-decode, post-ring, post-resampler.

* Wire-level packet sequence tracking on each PCM stream — in-order
  / missed / reordered / duplicated counts in the diag log.

* Clipped-sample delta in the diag log.

* New AudioStepProbe in RemSound.Core with per-channel scan helper.

UI changes:

* Record menu uses Alt+O (Rec&ord). Inside the menu, item mnemonics
  unchanged (S / T / O / C).

* Auto-tune interval combo label is mode-aware: "Auto-tune latency
  interval" in classic modes, "Auto-tune interval — WASAPI and ASIO"
  in BothIndependent. The combo's Enabled state now follows EITHER
  lane's auto-tune checkbox (was only the WASAPI one — bug).

Wire format and audio pipeline unchanged from v1.1 — v1.1 and v1.2
peers interoperate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-05-15 12:40:01 +01:00
co-authored by Claude Opus 4.7
parent a0fe8070ed
commit c59e413c1f
27 changed files with 3348 additions and 350 deletions
+25 -1
View File
@@ -62,8 +62,23 @@ public sealed class Profile
/// profile (not in AppConfig) because the right answer genuinely differs between
/// profiles.</summary>
public bool PriorityMode { get; set; }
/// <summary>True suppresses the connect/disconnect sound cues. Off by default.</summary>
/// <summary>Legacy combined "mute connect/disconnect sounds" toggle. True suppresses
/// both connect AND disconnect cues. Superseded 2026-05-15 by the four individual
/// <c>Enable*Cue</c> flags below — the new flags take precedence when set. This field
/// is preserved on the profile for backward compatibility with older builds that don't
/// know about the per-cue flags; on first load the per-cue flags inherit from this
/// (true → connect+disconnect cues disabled).</summary>
public bool MuteConnectionCues { get; set; }
/// <summary>Per-cue enable flags. Nullable so a missing entry in an older profile JSON
/// falls back to the legacy <see cref="MuteConnectionCues"/> migration path; once the
/// user touches the new UI we write a concrete <c>true</c>/<c>false</c> and the legacy
/// field stops mattering. Defaults to "play the sound" (true) for both cases — the
/// audio cues are part of the normal user feedback loop, not opt-in. 2026-05-15.</summary>
public bool? EnableConnectCue { get; set; }
public bool? EnableDisconnectCue { get; set; }
public bool? EnableRecordStartCue { get; set; }
public bool? EnableRecordStopCue { get; set; }
public int MaxLatencyMs { get; set; } = 80;
public int Smoothness { get; set; } = 3;
public bool ContinuousAutoTuneEnabled { get; set; }
@@ -95,6 +110,15 @@ public sealed class Profile
set => ConcealmentArtifactRaw = (int)value;
}
// === Recording ===
/// <summary>Recording source / format / attributes. The whole settings object is saved
/// per profile so different profiles can record different things (a "long session"
/// profile might record everything to MP3, a "monitoring" profile might not record at
/// all but keep the dialog defaults sensible). The recording isn't running until the
/// user explicitly triggers it via the Record menu; this just holds the configuration
/// the recorder picks up when it starts.</summary>
public RecordingSettings RecordingSettings { get; set; } = new();
// === Peers ===
public List<string> RememberedPeers { get; set; } = [];
/// <summary>Peer addresses (IP or host[:port]) the user had ticked in the connected