Initial commit: RemSound v1.0
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using System.Windows.Forms;
|
||||
using RemSound.Core;
|
||||
|
||||
namespace RemSound.App;
|
||||
|
||||
/// <summary>
|
||||
/// CheckedListBox subclass that exposes the protected <c>RefreshItem</c> method publicly. Used
|
||||
/// for the connectivity dialog's "Connected peers" list, where each row's text is updated in
|
||||
/// place (live RTT, codec, direction) without re-adding items — that would destroy NVDA's row
|
||||
/// focus on every tick.
|
||||
/// </summary>
|
||||
internal sealed class LiveCheckedListBox : CheckedListBox
|
||||
{
|
||||
public void RefreshItemPublic(int index) => RefreshItem(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User-facing choice that maps a friendly label to a codec + Opus frame size pair. The frame
|
||||
/// size is only meaningful when Codec == Opus; for PCM it's ignored.
|
||||
/// </summary>
|
||||
internal sealed record CodecChoice(string Label, AudioTransportCodec Codec, int OpusFrameMs)
|
||||
{
|
||||
public override string ToString() => Label;
|
||||
}
|
||||
|
||||
internal sealed record AudioDeviceChoice(string Name, string? DeviceId, CaptureKind Kind = CaptureKind.Loopback)
|
||||
{
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
|
||||
internal sealed record RememberedPeerItem(string Entry)
|
||||
{
|
||||
public override string ToString() => Entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Live per-peer status surfaced in the connectivity dialog's listbox text. Mutated in place
|
||||
/// each tick by MainForm.SyncAllDialogPeerLists, then ListBox.RefreshItem(i) is called on the
|
||||
/// containing item so the visible label updates without rebuilding the listbox (which would
|
||||
/// destroy NVDA focus on the row).
|
||||
/// </summary>
|
||||
internal sealed class PeerLineStatus
|
||||
{
|
||||
public bool Connected;
|
||||
/// <summary>True when our sender is actively pushing audio at this peer.</summary>
|
||||
public bool Sending;
|
||||
/// <summary>True when audio is arriving from this peer's IP (a fresh receiver session exists).</summary>
|
||||
public bool Receiving;
|
||||
/// <summary>Codec label like "Opus 10ms", "Opus 20ms", "PCM". Null when not connected.</summary>
|
||||
public string? CodecLabel;
|
||||
/// <summary>Round-trip ping ms from heartbeat. Null when not connected or pending.</summary>
|
||||
public int? RttMs;
|
||||
}
|
||||
|
||||
internal sealed class PeerListItem
|
||||
{
|
||||
public PeerAnnouncement Peer { get; }
|
||||
public PeerLineStatus Status { get; } = new();
|
||||
|
||||
public PeerListItem(PeerAnnouncement peer) { Peer = peer; }
|
||||
|
||||
/// <summary>
|
||||
/// Stable identity for signature-based listbox-rebuild detection. Does NOT include live
|
||||
/// status — that gets updated in-place via RefreshItem so NVDA focus survives tick updates.
|
||||
/// </summary>
|
||||
public string StableKey() => $"{Peer.InstanceId}:{Peer.Name}:{Peer.Address}:{Peer.AudioPort}";
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Base label: "hostname (ip)" for discovered peers, just "ip" for manual-by-IP entries
|
||||
// (where hostname equals the IP address). Avoids "192.168.1.95 (192.168.1.95)" duplication.
|
||||
var addr = Peer.Address.ToString();
|
||||
var basePart = Peer.Name == addr ? addr : $"{Peer.Name} ({addr})";
|
||||
|
||||
if (!Status.Connected)
|
||||
{
|
||||
return basePart;
|
||||
}
|
||||
|
||||
// Connected line — extra metadata after a dash. Comma-separated so NVDA reads naturally:
|
||||
// "Andre's PC (1.2.3.4) — connected, Opus 10ms, send and receive, 32ms"
|
||||
var parts = new List<string> { "connected" };
|
||||
if (Status.CodecLabel is { Length: > 0 } codec) parts.Add(codec);
|
||||
|
||||
var direction = (Status.Sending, Status.Receiving) switch
|
||||
{
|
||||
(true, true) => "send and receive",
|
||||
(true, false) => "send only",
|
||||
(false, true) => "receive only",
|
||||
_ => null,
|
||||
};
|
||||
if (direction is not null) parts.Add(direction);
|
||||
|
||||
if (Status.RttMs is { } rtt) parts.Add($"{rtt}ms");
|
||||
|
||||
return $"{basePart} — {string.Join(", ", parts)}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user