v0.4: persistent config in AppData, voices in LOCALAPPDATA, remove Speak button
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using System.Text.Json;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Robovoice.App;
|
||||
|
||||
public sealed class AppConfig
|
||||
{
|
||||
public string Voice { get; set; } = string.Empty;
|
||||
public int PttKey { get; set; } = (int)Keys.F8;
|
||||
public string OutputDevice { get; set; } = string.Empty;
|
||||
public int NoiseScale { get; set; } = 667;
|
||||
public int LengthScale { get; set; } = 100;
|
||||
public int NoiseWScale { get; set; } = 800;
|
||||
public bool MinimizeToTray { get; set; } = true;
|
||||
|
||||
public static string AppDataDir => Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Hackmaster", "Robovoice");
|
||||
|
||||
public static string ConfigPath => Path.Combine(AppDataDir, "config.json");
|
||||
public static string VoicesDir => Path.Combine(AppDataDir, "Piper-voices");
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
public static AppConfig Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(ConfigPath))
|
||||
{
|
||||
using var stream = File.OpenRead(ConfigPath);
|
||||
return JsonSerializer.Deserialize<AppConfig>(stream) ?? new AppConfig();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return new AppConfig();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(AppDataDir);
|
||||
using var stream = File.Create(ConfigPath);
|
||||
JsonSerializer.Serialize(stream, this, JsonOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+2
-9
@@ -22,7 +22,6 @@ partial class MainForm
|
||||
private Button btnClearLog = null!;
|
||||
private Label lblTextInput = null!;
|
||||
private TextBox txtTextInput = null!;
|
||||
private Button btnSpeakInput = null!;
|
||||
private Label lblNoise = null!;
|
||||
private TrackBar trkNoise = null!;
|
||||
private Label lblNoiseVal = null!;
|
||||
@@ -61,7 +60,6 @@ partial class MainForm
|
||||
btnClearLog = new Button();
|
||||
lblTextInput = new Label();
|
||||
txtTextInput = new TextBox();
|
||||
btnSpeakInput = new Button();
|
||||
lblNoise = new Label();
|
||||
trkNoise = new TrackBar();
|
||||
lblNoiseVal = new Label();
|
||||
@@ -85,6 +83,7 @@ partial class MainForm
|
||||
txtPttKey.Size = new Size(80, 23);
|
||||
txtPttKey.ReadOnly = true;
|
||||
txtPttKey.TextAlign = HorizontalAlignment.Center;
|
||||
txtPttKey.TabStop = false;
|
||||
|
||||
// lblVoice
|
||||
lblVoice.Text = "Voice:";
|
||||
@@ -221,13 +220,8 @@ partial class MainForm
|
||||
|
||||
// txtTextInput
|
||||
txtTextInput.Location = new Point(50, 118);
|
||||
txtTextInput.Size = new Size(680, 23);
|
||||
txtTextInput.Size = new Size(762, 23);
|
||||
|
||||
// btnSpeakInput
|
||||
btnSpeakInput.Text = "Speak";
|
||||
btnSpeakInput.Location = new Point(737, 117);
|
||||
btnSpeakInput.Size = new Size(75, 25);
|
||||
btnSpeakInput.UseVisualStyleBackColor = true;
|
||||
txtLog.ReadOnly = true;
|
||||
txtLog.Font = new Font("Consolas", 9F);
|
||||
txtLog.BackColor = Color.FromArgb(30, 30, 30);
|
||||
@@ -272,7 +266,6 @@ partial class MainForm
|
||||
Controls.Add(lblNoiseWVal);
|
||||
Controls.Add(lblTextInput);
|
||||
Controls.Add(txtTextInput);
|
||||
Controls.Add(btnSpeakInput);
|
||||
Controls.Add(txtLog);
|
||||
Controls.Add(chkMinimizeToTray);
|
||||
Controls.Add(btnClearLog);
|
||||
|
||||
@@ -10,8 +10,9 @@ namespace Robovoice.App;
|
||||
|
||||
internal sealed partial class MainForm : Form
|
||||
{
|
||||
private readonly string _voicesDir = @"C:\data\opencode\robovoice-voices";
|
||||
private readonly string _voicesDir = AppConfig.VoicesDir;
|
||||
private readonly string _espeakDataPath;
|
||||
private readonly AppConfig _config;
|
||||
|
||||
private LibPiperTtsEngine? _tts;
|
||||
private AudioOutput? _audioOutput;
|
||||
@@ -25,22 +26,41 @@ internal sealed partial class MainForm : Form
|
||||
{
|
||||
InitializeComponent();
|
||||
_espeakDataPath = Path.Combine(AppContext.BaseDirectory, "espeak-ng-data");
|
||||
_config = AppConfig.Load();
|
||||
Directory.CreateDirectory(AppConfig.AppDataDir);
|
||||
Directory.CreateDirectory(_voicesDir);
|
||||
Load += OnLoad;
|
||||
FormClosing += OnFormClosing;
|
||||
}
|
||||
|
||||
private async void OnLoad(object? sender, EventArgs e)
|
||||
{
|
||||
ActiveControl = txtLog;
|
||||
PopulateVoices();
|
||||
PopulateOutputDevices();
|
||||
|
||||
_pttKey = (Keys)_config.PttKey;
|
||||
if (_pttKey == Keys.None)
|
||||
_pttKey = Keys.F8;
|
||||
txtPttKey.Text = KeyToDisplayString(_pttKey);
|
||||
if (cmbVoice.Items.Count > 0)
|
||||
|
||||
if (!string.IsNullOrEmpty(_config.Voice) && cmbVoice.Items.Contains(_config.Voice))
|
||||
cmbVoice.SelectedItem = _config.Voice;
|
||||
else if (cmbVoice.Items.Count > 0)
|
||||
cmbVoice.SelectedIndex = 0;
|
||||
|
||||
if (!string.IsNullOrEmpty(_config.OutputDevice) && cmbOutput.Items.Contains(_config.OutputDevice))
|
||||
cmbOutput.SelectedItem = _config.OutputDevice;
|
||||
else
|
||||
AutoSelectCableOutput();
|
||||
|
||||
trkNoise.Value = _config.NoiseScale;
|
||||
trkSpeed.Value = _config.LengthScale;
|
||||
trkNoiseW.Value = _config.NoiseWScale;
|
||||
OnSliderScroll(null, EventArgs.Empty);
|
||||
|
||||
chkMinimizeToTray.Checked = _config.MinimizeToTray;
|
||||
|
||||
btnBrowseFile.Click += OnBrowseFile;
|
||||
btnTestVoice.Click += OnTestVoice;
|
||||
btnManageVoices.Click += OnManageVoices;
|
||||
@@ -57,6 +77,10 @@ internal sealed partial class MainForm : Form
|
||||
trkSpeed.MouseUp += OnSliderReleased;
|
||||
trkNoiseW.MouseUp += OnSliderReleased;
|
||||
|
||||
chkMinimizeToTray.CheckedChanged += (_, _) => SaveConfig();
|
||||
|
||||
Resize += OnResize;
|
||||
|
||||
SetupTray();
|
||||
|
||||
await InitializeEngineAsync();
|
||||
@@ -98,6 +122,7 @@ internal sealed partial class MainForm : Form
|
||||
_captureHook?.Dispose();
|
||||
_captureHook = null;
|
||||
SetupHotkey();
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
private void CancelCapture()
|
||||
@@ -334,6 +359,7 @@ internal sealed partial class MainForm : Form
|
||||
cmbVoice.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
SaveConfig();
|
||||
_ = InitializeEngineAsync();
|
||||
}
|
||||
|
||||
@@ -344,6 +370,7 @@ internal sealed partial class MainForm : Form
|
||||
|
||||
if (cmbVoice.SelectedItem is string name)
|
||||
{
|
||||
SaveConfig();
|
||||
_ = ReinitializeEngineAsync(name);
|
||||
}
|
||||
}
|
||||
@@ -368,6 +395,7 @@ internal sealed partial class MainForm : Form
|
||||
|
||||
private void OnSliderReleased(object? sender, MouseEventArgs e)
|
||||
{
|
||||
SaveConfig();
|
||||
if (cmbVoice.SelectedItem is string name && VoiceCatalogue.IsVoiceInstalled(_voicesDir, name))
|
||||
{
|
||||
_ = InitializeEngineAsync();
|
||||
@@ -379,6 +407,7 @@ internal sealed partial class MainForm : Form
|
||||
if (_orchestrator is not null)
|
||||
_orchestrator.OutputDeviceName = cmbOutput.SelectedItem as string ?? string.Empty;
|
||||
Log($"Output device: {_orchestrator?.OutputDeviceName}");
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
private void UpdateLineStatus()
|
||||
@@ -415,6 +444,15 @@ internal sealed partial class MainForm : Form
|
||||
_trayIcon.DoubleClick += (_, _) => ShowWindow();
|
||||
}
|
||||
|
||||
private void OnResize(object? sender, EventArgs e)
|
||||
{
|
||||
if (WindowState == FormWindowState.Minimized && chkMinimizeToTray.Checked)
|
||||
{
|
||||
Hide();
|
||||
WindowState = FormWindowState.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWindow()
|
||||
{
|
||||
Show();
|
||||
@@ -422,6 +460,18 @@ internal sealed partial class MainForm : Form
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
_config.Voice = cmbVoice.SelectedItem as string ?? string.Empty;
|
||||
_config.PttKey = (int)_pttKey;
|
||||
_config.OutputDevice = cmbOutput.SelectedItem as string ?? string.Empty;
|
||||
_config.NoiseScale = trkNoise.Value;
|
||||
_config.LengthScale = trkSpeed.Value;
|
||||
_config.NoiseWScale = trkNoiseW.Value;
|
||||
_config.MinimizeToTray = chkMinimizeToTray.Checked;
|
||||
_config.Save();
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
@@ -438,15 +488,9 @@ internal sealed partial class MainForm : Form
|
||||
|
||||
private void OnFormClosing(object? sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (e.CloseReason == CloseReason.UserClosing && chkMinimizeToTray.Checked && _trayIcon?.Visible == true)
|
||||
{
|
||||
e.Cancel = true;
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
_pttHotkey?.Dispose();
|
||||
_trayIcon!.Visible = false;
|
||||
_orchestrator?.DisposeAsync().AsTask().Wait();
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user