Input mode (VAD/PTT/Always-On), VAD threshold, and the new mic gain were
applied to the core + UI but never saved, so every relaunch reset to VAD
defaults. Each client now persists them and re-applies on connect:
- iOS: UserDefaults (SessionState.loadAndApplyVoiceSettings + setter writes)
- macOS: UserDefaults via MainWindowController didSet + loadPersistedAudioSettings
(settings window also restores the VAD slider from the stored threshold)
- Windows: new Models/VoiceSettings.cs (JSON at %AppData%\VoiceCat\voice.json,
mirrors FeedbackSettings) loaded/applied in MainForm
Add global send-side mic gain API vc_set_input_gain (applied to MIC PCM in
on_capture_frame before the VAD gate, clamped to int16) + Swift/C# bindings,
and a 0-300% (default 100%) mic-volume slider on all three clients.
Fix iOS chat: ChatView called sendText(scope:.channel) with no targetId (0),
so channel messages went nowhere; now passes session.currentChannelId.
Fix iOS per-user tuning for VoiceOver: the tuning sheet was long-press
.contextMenu only (invisible to VoiceOver); UserRow now also exposes the same
buttons via .accessibilityActions (no visual change).
Verified: core builds clean; ctest 24/27 (3 pre-existing teardown crashes,
reproduced with changes stashed); VoiceCatMac + VoiceCatiOS (arm64 sim) build
SUCCEEDED; VoiceCat.Interop dotnet build succeeded. Windows App not built
(WinForms can't build on macOS) — follows existing patterns.
54 lines
2.0 KiB
C#
54 lines
2.0 KiB
C#
using System.Text.Json;
|
||
|
||
namespace VoiceCat.App.Models;
|
||
|
||
/// <summary>
|
||
/// User's send-side voice input preferences — transmission mode, VAD sensitivity, mic input
|
||
/// gain, and the push-to-talk key. Persisted to %AppData%\VoiceCat\voice.json, same pattern as
|
||
/// <see cref="ServerListStore"/> and FeedbackSettings: a missing or corrupt file yields defaults
|
||
/// rather than throwing. Slider-position values are stored as-is so MainForm can restore the
|
||
/// TrackBars directly.
|
||
/// </summary>
|
||
public sealed class VoiceSettings
|
||
{
|
||
/// <summary>Transmission mode: 0 = voice activation, 1 = push-to-talk, 2 = always on
|
||
/// (matches Interop's VcInputMode).</summary>
|
||
public int InputMode { get; set; } = 0;
|
||
|
||
/// <summary>VAD sensitivity slider position, 1–100 (default mirrors the designer's 76).</summary>
|
||
public int VadThresholdSlider { get; set; } = 76;
|
||
|
||
/// <summary>Microphone input gain slider position, 0–300 percent (100 = unity).</summary>
|
||
public int MicGain { get; set; } = 100;
|
||
|
||
/// <summary>Push-to-talk key, stored as the integer value of System.Windows.Forms.Keys.</summary>
|
||
public int PttKey { get; set; } = (int)Keys.F8;
|
||
|
||
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
|
||
|
||
private static string AppDataDir => Path.Combine(
|
||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VoiceCat");
|
||
|
||
private static string FilePath => Path.Combine(AppDataDir, "voice.json");
|
||
|
||
public static VoiceSettings Load()
|
||
{
|
||
try
|
||
{
|
||
if (!File.Exists(FilePath)) return new VoiceSettings();
|
||
string json = File.ReadAllText(FilePath);
|
||
return JsonSerializer.Deserialize<VoiceSettings>(json) ?? new VoiceSettings();
|
||
}
|
||
catch
|
||
{
|
||
return new VoiceSettings();
|
||
}
|
||
}
|
||
|
||
public void Save()
|
||
{
|
||
Directory.CreateDirectory(AppDataDir);
|
||
File.WriteAllText(FilePath, JsonSerializer.Serialize(this, JsonOptions));
|
||
}
|
||
}
|