Files
RemSound/src/RemSound.App/RemSound.App.csproj
T
EdnunpandClaude Opus 4.8 959720f54d Release v3.3: end-to-end encrypted audio, plus cue and reliability fixes
Headline: all audio is now encrypted (AES-256-GCM), keyed by a per-profile
password. Mandatory — v3.3 only interoperates with v3.3+.

Encryption
- RemSoundCrypto (Core): PBKDF2 key derivation, AES-GCM encrypt/decrypt
  (low-alloc, into-span), password fingerprint, light on-disk obfuscation.
- Wire: SenderLane encrypts the audio payload (PCM split across parts when the
  +28 overhead crosses MTU); AudioReceiver/StreamSession decrypt via a shared
  single-thread AudioDecryptor. Fingerprint piggybacks on the Format packet
  (offset 36, backward-compatible) so a peer can detect a password mismatch.
- Profile.Password (scrambled), carried through BuildCurrentProfile; MainForm
  derives + pushes the key/fingerprint to sender + receiver (RecomputeAudioCrypto).
- UX: ask-for-password on profile create; File -> Change this profile's password
  (ProfilePasswordDialog); Options -> Profile passwords (manager); a gate that
  prompts before streaming without a password; and a clear "passwords don't
  match" / "peer needs to update" message driven by the fingerprint.

Cue fixes
- CuePlayer (NAudio) replaces System.Media.SoundPlayer, which silently failed
  on the 96 kHz/24-bit cue WAVs (and any custom file) — cues now play reliably,
  resampled to 48 kHz/16-bit. Also fixes the Preferences preview button.
- Connect/disconnect cues now audio-gated with hysteresis: connected when audio
  flows OR heartbeat healthy; lost only when audio stops AND heartbeat
  unreachable. Kills false disconnects and the receive-only "no cues" case.
- Honest cue logging (played / muted / not loaded).

Smaller
- Endpoint stickiness: keep the audio target pinned to the heartbeat-proven
  address instead of chasing a multi-homed peer's other (unreachable) address.
- "Online/offline" label now audio+heartbeat aware, not discovery-only.
- "Show what's new after each update" preference (on by default).

Docs: About v3.3 block, RELEASE_NOTES, README (encryption as a headline),
manual section 12 "Passwords and encryption" (+ renumber), MANUAL.md regenerated.
Version 3.2.0 -> 3.3.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:57:32 +01:00

110 lines
6.6 KiB
XML

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWindowsForms>true</UseWindowsForms>
<RootNamespace>RemSound.App</RootNamespace>
<AssemblyName>RemSound</AssemblyName>
<ApplicationManifest>app.manifest</ApplicationManifest>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ApplicationHighDpiMode>SystemAware</ApplicationHighDpiMode>
<!-- Release version. The self-updater (RemSoundUpdater) compares this against the
tag_name on the latest GitHub release; bump it on every public release. The
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
is what the About dialog and the updater both read. -->
<Version>3.3.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RemSound.Core\RemSound.Core.csproj" />
<ProjectReference Include="..\RemSound.Sender\RemSound.Sender.csproj" />
<ProjectReference Include="..\RemSound.Receiver\RemSound.Receiver.csproj" />
<PackageReference Include="NAudio" Version="2.3.0" />
<!-- LAME wrapper for MP3 encoding. Pulled in for the recording feature. The native
libmp3lame.dll ships with the package and is copied to the output folder. -->
<PackageReference Include="NAudio.Lame" Version="2.1.0" />
<!-- OGG container writer that wraps Concentus-encoded Opus. Same Concentus the wire
path already uses (transitive dep), so the recording-side OGG-Opus output is
byte-for-byte the same encoder we'd send on a live Opus stream. -->
<PackageReference Include="Concentus.Oggfile" Version="1.0.7" />
<!-- Pure-managed FLAC encoder (Flake). Same encoder CUETools uses; no native DLL,
no P/Invoke. Lossless, ships as managed IL, smaller files than WAV (~50%) without
sample-data loss. -->
<PackageReference Include="CUETools.Codecs.FLAKE" Version="1.0.5" />
<!-- UPnP / NAT-PMP / PCP client library for automatically opening the audio port on
the user's router so peers on the public internet can reach this machine without
manual port forwarding. Cross-protocol — picks whichever the router speaks. Used
under the AppConfig.UpnpEnabled toggle, off by default. -->
<PackageReference Include="Mono.Nat" Version="3.0.4" />
<!-- Native libopus binaries that Concentus 2.0+ auto-detects at runtime and routes
encode/decode calls through. Without this package Concentus uses its pure-managed
C# fallback, which `new`s ~15 working buffers per encode call (issue #22 on the
Concentus repo, open since 2018) and produces ~4.5 MB/s of GC pressure per
encoding lane at 10 ms frames. Switching to native via this package keeps the
encoder state allocated once (C-side) and reuses it across calls. Same encoder
settings, bit-for-bit identical audio output. Installed at the top-level project
(per the package's install guidance) so `dotnet publish` correctly trims native
binaries for irrelevant RIDs from the release output. 2026-05-23. -->
<PackageReference Include="Concentus.Native" Version="1.5.2" />
<!-- Explicit pin for the transitive Concentus.Native.NetCore. The parent package
declares a minimum version of 1.5.1, but 1.5.1 was never published to nuget.org —
only 1.5.2 was. NuGet still resolves correctly (it picks 1.5.2) but emits NU1603
as a warning, which our TreatWarningsAsErrors policy promotes to an error.
Pinning explicitly skips that warning and is self-documenting. -->
<PackageReference Include="Concentus.Native.NetCore" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<!-- All cue sounds live in a sounds\ subfolder under the publish output (moved out of
the install-root flat layout 2026-05-28). Default cue WAVs ship inside that folder;
user-supplied custom cue paths set in Preferences override the defaults at runtime.
Filenames containing a space (e.g. "record start.wav") are preserved verbatim on
copy so the load-by-filename path in TryLoadCueSound finds them exactly as written. -->
<Content Include="..\..\sounds\connect.wav">
<Link>sounds\connect.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\sounds\disconnect.wav">
<Link>sounds\disconnect.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\sounds\record start.wav">
<Link>sounds\record start.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\sounds\record stop.wav">
<Link>sounds\record stop.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- New cues introduced 2026-05-28: save.wav fires after a successful Save / Save As;
profile.wav fires immediately after a profile finishes loading. Both default WAVs
to be supplied by the project owner; absent files are gracefully ignored at load
time (the cue just doesn't play) so the build is fine without them. -->
<Content Include="..\..\sounds\save.wav" Condition="Exists('..\..\sounds\save.wav')">
<Link>sounds\save.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\sounds\profile.wav" Condition="Exists('..\..\sounds\profile.wav')">
<Link>sounds\profile.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- update.wav (2026-05-31): plays just before an update starts installing, so a silent
background update still gives an audible heads-up. Per-profile mute + custom-sound
override available in Preferences, same as the other cues. -->
<Content Include="..\..\sounds\update.wav" Condition="Exists('..\..\sounds\update.wav')">
<Link>sounds\update.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- User manual. F1 anywhere in the app opens this via the user's default browser
(HelpLauncher.OpenManual). The PreserveNewest mode means a fresh publish overwrites
the published copy whenever the source is newer; manually-edited copies inside
publish/ get clobbered, which is correct (publish is build output, not config). -->
<Content Include="..\..\readme.html">
<Link>readme.html</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>