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:
co-authored by
Claude Opus 4.7
parent
a0fe8070ed
commit
c59e413c1f
@@ -52,6 +52,17 @@ internal sealed class SenderLane
|
||||
private OpusEncoderState opusEncoder;
|
||||
private int opusFrameStereoSamples;
|
||||
|
||||
// Per-lane pre-encode discontinuity probe. Moved here from AudioSender (2026-05-15) so
|
||||
// each lane has its OWN probe state and the cross-buffer step measurement (which carries
|
||||
// lastL/lastR across calls) only sees samples from one continuous audio stream. With the
|
||||
// earlier shared-probe design, BothIndependent mode mixed two unrelated streams' samples
|
||||
// into the same probe's cross-buffer carry, producing synthetic "steps" of arbitrary
|
||||
// magnitude every time the two lanes' callbacks interleaved — making the diag log unable
|
||||
// to tell a real capture glitch from instrumentation aliasing. Per-lane separation fixes
|
||||
// that without changing what the probe measures.
|
||||
private readonly AudioStepProbe preEncodeStepProbe = new();
|
||||
public float TakeMaxPreEncodeStep() => preEncodeStepProbe.TakeMax();
|
||||
|
||||
// Which render route this lane announces in its format packets. The receiver reads the
|
||||
// Lane byte on the wire and tags the matching SessionPlayout, which makes PlayoutEngine
|
||||
// route the lane's audio to the corresponding per-route IWaveProvider surface (lane
|
||||
@@ -144,6 +155,20 @@ internal sealed class SenderLane
|
||||
var emitStart = diag ? System.Diagnostics.Stopwatch.GetTimestamp() : 0L;
|
||||
EnsureFormatPacketSent();
|
||||
|
||||
// Recording tap — the recorder gets the float audio about to be encoded. The lane
|
||||
// doesn't know whether the recorder is running; the dispatcher early-outs when no
|
||||
// callback is wired. Captured here (before encoding) so the recording is bit-clean
|
||||
// float, independent of which codec the wire is using.
|
||||
owner.DispatchSentSamples(stereoFloats);
|
||||
|
||||
// Discontinuity probe — what does the audio look like just before we encode it?
|
||||
// Compared to the receiver's per-stage probes, this tells us whether artefacts are
|
||||
// present at the sender side already (capture hardware glitch, mix-bus issue) or
|
||||
// introduced somewhere in the wire / decode / playout chain. Per-lane probe — see
|
||||
// <see cref="preEncodeStepProbe"/> field comment for why this isn't shared with the
|
||||
// other lane in BothIndependent.
|
||||
preEncodeStepProbe.ScanStereo(span);
|
||||
|
||||
switch (owner.Codec)
|
||||
{
|
||||
case AudioTransportCodec.Pcm:
|
||||
|
||||
Reference in New Issue
Block a user