From 8f3099756ae639182e4eefa02b2a4f9398a7c043 Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 23 Dec 2023 11:30:12 +0100 Subject: [PATCH 01/13] Update Crowdin configuration file --- crowdin.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 crowdin.yml diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000..ed3c5a9 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /Languages/English.json + translation: /Languages/%language%.json From 628e707599a59c4e2dfd6271949163b782ee46fa Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 23 Dec 2023 17:05:37 +0100 Subject: [PATCH 02/13] Update crowdin.yml --- crowdin.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crowdin.yml b/crowdin.yml index ed3c5a9..b9f97a0 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,5 @@ +commit_message: "Crowdin: Edited %language%" +append_commit_message: false files: - source: /Languages/English.json translation: /Languages/%language%.json From 15413aea8a0dbc2833f6800c0f7e4e4e820df642 Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 17 Feb 2024 01:07:04 +0100 Subject: [PATCH 03/13] Crowdin: Edited Polish --- Languages/Polish.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Languages/Polish.json diff --git a/Languages/Polish.json b/Languages/Polish.json new file mode 100644 index 0000000..da18bf1 --- /dev/null +++ b/Languages/Polish.json @@ -0,0 +1,28 @@ +{ + "Attribution": "PhoenixWyllow", + "Language": "Angielski", + "PluginDescription": "Plugin do Macro Decka", + "VoiceMeeterConnected": "Voicemeeter Połączony", + "VoiceMeeterDisconnected": "Voicemeeter Odłączony", + "ToggleActionName": "Włącz/Wyłącz użądzenie", + "ToggleActionDescription": "Toggle an option on a strip or bus", + "Device": "Użądzenie", + "Action": "Akcja", + "AdvancedActionName": "Zaawansowane/Standardowe", + "AdvancedActionDescription": "Zaawansowane/Standardowe opcje do kontrolowania Voicemeetera przy użyciu języka Voicemeeter API.\nProsze przeczytaj dokumentacje Voicemeetera po pomoc.", + "Commands": "Komędy (rozdzielone przez ',', ';' albo nową linią)", + "LabelParameter": "Parametr", + "ParameterError": "Wystąpił błąd podczas weryfikowania parametru.", + "ParameterExists": "Ten parametr już istnieje.", + "NoCommandsMsg": "Brak komęd do wykonania", + "NCommandsMsg": "{0} komęd", + "CommandActionName": "Wywołaj komęde", + "CommandActionDescription": "Wybież komęde z Voicemeetera do wykonaia", + "Command": "Komęda", + "CommandValue": "Wartość komędy", + "CommandError": "Nastąpił błąd podczas weryfikowania komędy.", + "SliderActionName": "Sterowanie suwakiem", + "SliderActionDescription": "Zmień wartość suwaka o daną ilość", + "SliderValue": "Zmień wartość suwaka o daną ilość", + "ErrorZeroSliderValue": "Wartość nie może być równa zeru." +} \ No newline at end of file From e2731d223e58c7bec0e9dca3ca486563d03fb3cd Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:24:43 +0100 Subject: [PATCH 04/13] Crowdin: Edited Polish --- Languages/Polish.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Languages/Polish.json b/Languages/Polish.json index da18bf1..b2c2d20 100644 --- a/Languages/Polish.json +++ b/Languages/Polish.json @@ -1,7 +1,7 @@ { - "Attribution": "PhoenixWyllow", - "Language": "Angielski", - "PluginDescription": "Plugin do Macro Decka", + "Attribution": "szopen", + "Language": "Polski", + "PluginDescription": "Plugin do Macro Deck'a", "VoiceMeeterConnected": "Voicemeeter Połączony", "VoiceMeeterDisconnected": "Voicemeeter Odłączony", "ToggleActionName": "Włącz/Wyłącz użądzenie", From 7b266f9b897b21e31d59bfa16924952041c209eb Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Thu, 28 Mar 2024 23:48:16 +0100 Subject: [PATCH 05/13] Crowdin: Edited Polish --- Languages/Polish.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Languages/Polish.json b/Languages/Polish.json index b2c2d20..8cb785c 100644 --- a/Languages/Polish.json +++ b/Languages/Polish.json @@ -2,27 +2,27 @@ "Attribution": "szopen", "Language": "Polski", "PluginDescription": "Plugin do Macro Deck'a", - "VoiceMeeterConnected": "Voicemeeter Połączony", - "VoiceMeeterDisconnected": "Voicemeeter Odłączony", - "ToggleActionName": "Włącz/Wyłącz użądzenie", - "ToggleActionDescription": "Toggle an option on a strip or bus", - "Device": "Użądzenie", + "VoiceMeeterConnected": "Voicemeeter połączony", + "VoiceMeeterDisconnected": "Voicemeeter rozłączony", + "ToggleActionName": "Przełącz urządzenie", + "ToggleActionDescription": "Przełącz opcję na pasku lub szynie", + "Device": "Urządzenie", "Action": "Akcja", - "AdvancedActionName": "Zaawansowane/Standardowe", - "AdvancedActionDescription": "Zaawansowane/Standardowe opcje do kontrolowania Voicemeetera przy użyciu języka Voicemeeter API.\nProsze przeczytaj dokumentacje Voicemeetera po pomoc.", - "Commands": "Komędy (rozdzielone przez ',', ';' albo nową linią)", + "AdvancedActionName": "Zaawansowane/Niestandardowe", + "AdvancedActionDescription": "Zaawansowane/Niestandardowe opcje do kontrolowania Voicemeeter'a przy użyciu Voicemeeter API.\nInstrukcje znajdziesz w dokumentacji Voicemeeter.", + "Commands": "Komendy (rozdzielone przez ',', ';' albo nową linią)", "LabelParameter": "Parametr", "ParameterError": "Wystąpił błąd podczas weryfikowania parametru.", "ParameterExists": "Ten parametr już istnieje.", - "NoCommandsMsg": "Brak komęd do wykonania", - "NCommandsMsg": "{0} komęd", - "CommandActionName": "Wywołaj komęde", - "CommandActionDescription": "Wybież komęde z Voicemeetera do wykonaia", - "Command": "Komęda", - "CommandValue": "Wartość komędy", - "CommandError": "Nastąpił błąd podczas weryfikowania komędy.", + "NoCommandsMsg": "Brak komend do wykonania", + "NCommandsMsg": "{0} komend", + "CommandActionName": "Wywołaj komendę", + "CommandActionDescription": "Wybierz komendę z Voicemeeter'a do wykonaia", + "Command": "Komenda", + "CommandValue": "Wartość komendy", + "CommandError": "Nastąpił błąd podczas weryfikowania komendy.", "SliderActionName": "Sterowanie suwakiem", - "SliderActionDescription": "Zmień wartość suwaka o daną ilość", - "SliderValue": "Zmień wartość suwaka o daną ilość", + "SliderActionDescription": "Przesuń suwak o daną wartość", + "SliderValue": "Przesuń suwak o wartość", "ErrorZeroSliderValue": "Wartość nie może być równa zeru." } \ No newline at end of file From 645e872f52e26119fe4bd0c936217d9ae57e7b9c Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:12:29 +0100 Subject: [PATCH 06/13] upgrade vmrapi to v2.0.0 --- ExtensionManifest.json | 4 ++-- Models/AdditionalVariablesModel.cs | 2 +- Models/VmIOInfo.cs | 1 - Models/VmIOOptions.cs | 1 - README.md | 11 +++++++++- Services/Voicemeeter/Control.Connect.cs | 3 ++- Services/Voicemeeter/Control.Core.cs | 19 +++++++++------- Services/Voicemeeter/Control.Parameters.cs | 9 ++++---- Services/Voicemeeter/ControlHelpers.cs | 15 ++++++++----- ViewModels/StatusButtonViewModel.cs | 5 ++++- Views/AddAdditionalVariablesConfigView.cs | 7 ++---- Views/AdvancedActionConfigView.cs | 2 +- Views/DeviceSelectorConfigView.Designer.cs | 2 +- Views/DeviceSelectorConfigView.cs | 1 + VoicemeeterPlugin.cs | 4 +--- VoicemeeterPlugin.csproj | 25 +++++++++++++--------- 16 files changed, 66 insertions(+), 45 deletions(-) diff --git a/ExtensionManifest.json b/ExtensionManifest.json index 34ba025..b39de08 100644 --- a/ExtensionManifest.json +++ b/ExtensionManifest.json @@ -4,8 +4,8 @@ "author": "PhoenixWyllow", "repository": "https://github.com/PhoenixWyllow/MacroDeck.Voicemeeter", "packageId": "PhoenixWyllow.VoicemeeterPlugin", - "version": "2.0.0", + "version": "2.1.0", "target-plugin-api-version": 40, - "target-macro-deck-version": "2.13.0", + "target-macro-deck-version": "2.14.1", "dll": "Voicemeeter Plugin.dll" } \ No newline at end of file diff --git a/Models/AdditionalVariablesModel.cs b/Models/AdditionalVariablesModel.cs index 01a0c18..f9f3be1 100644 --- a/Models/AdditionalVariablesModel.cs +++ b/Models/AdditionalVariablesModel.cs @@ -7,7 +7,7 @@ namespace PW.VoicemeeterPlugin.Models; [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] public sealed class AdditionalVariablesModel : ISerializableConfiguration { - public List Options { get; init; } = new(); + public List Options { get; init; } = []; public string Serialize() { diff --git a/Models/VmIOInfo.cs b/Models/VmIOInfo.cs index 16bd7ac..83365c0 100644 --- a/Models/VmIOInfo.cs +++ b/Models/VmIOInfo.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace PW.VoicemeeterPlugin.Models; diff --git a/Models/VmIOOptions.cs b/Models/VmIOOptions.cs index 8c1e0ee..bf585ce 100644 --- a/Models/VmIOOptions.cs +++ b/Models/VmIOOptions.cs @@ -1,6 +1,5 @@ using SuchByte.MacroDeck.Variables; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Text.Json.Serialization; diff --git a/README.md b/README.md index c1cd919..d502373 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Create a button and configure how you want to control Voicemeeter. | Slider control | Change a slider by a given amount | Change slider by a relative amount (between -10 and +10) | | Program commands | Execute Voicemeeter commands | Shutdown, Restart, Show, Reset/Load/Save configuration, Load/Eject cassette | | Advanced/Custom | Send a custom command to Voicemeeter | This option allows configuring custom commands using the Voicemeeter API language.
Please see the official [VoicemeeterRemoteAPI pdf](https://download.vb-audio.com/Download_CABLE/VoicemeeterRemoteAPI.pdf) for more info. | +| Use Macro Button | Activate/Toggle a Macro Button (Voicemeeter) | Activate or toggle a Voicemeeter Macro Button. Ids are 0-79 and you will need to find and confugure them separately, according to the documentation for your Voicemeeter version. | #### Available parameters/variables @@ -45,7 +46,15 @@ Clicking on an option that you've already included allows you to edit or delete >- **Restart Macro Deck** to refresh with the variables you've added or removed >- I can only provide minimal support for this since it's all your own stuff being added. Use it wisely. -*More features/actions coming soon...* +#### Use Voicemeeter Macro Buttons + +Macro Buttons are a feature of Voicemeeter that allows you to execute a series of commands with a single button press.\ +Some actions are only possible with Macro Buttons, like sending special commands (SendText, Wait, etc.). + +To use a Macro Button, you must first create it in Voicemeeter and then configure the plugin to use it.\ +The plugin will only activate or toggle the button, it will not create or edit the button for you. + +The Macro Button ID is a number between 0 and 79, and you can find it in the Voicemeeter Macro Buttons editor window as "Logical ID".\ *** ## Need this in your language? diff --git a/Services/Voicemeeter/Control.Connect.cs b/Services/Voicemeeter/Control.Connect.cs index e908db8..51ee893 100644 --- a/Services/Voicemeeter/Control.Connect.cs +++ b/Services/Voicemeeter/Control.Connect.cs @@ -1,4 +1,5 @@ using AtgDev.Voicemeeter; +using AtgDev.Voicemeeter.Extensions; using AtgDev.Voicemeeter.Types; using System; using System.Timers; @@ -91,7 +92,7 @@ private void Login() private void StartPolling() { - _timer ??= new() + _timer ??= new Timer { Interval = 300, AutoReset = true, diff --git a/Services/Voicemeeter/Control.Core.cs b/Services/Voicemeeter/Control.Core.cs index f735abd..dd76b8e 100644 --- a/Services/Voicemeeter/Control.Core.cs +++ b/Services/Voicemeeter/Control.Core.cs @@ -5,12 +5,14 @@ using SuchByte.MacroDeck.Variables; using System; using System.Linq; +using AtgDev.Voicemeeter.Extensions; +using AtgDev.Voicemeeter.Types; namespace PW.VoicemeeterPlugin.Services.Voicemeeter; public sealed partial class Control { - private RemoteApiExtender? VmrApi { get; } + private RemoteApiWrapper? VmrApi { get; } private VoicemeeterGlobalConfigModel Config { get; } public Control() @@ -19,7 +21,7 @@ public Control() try { AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; - VmrApi = new(AtgDev.Voicemeeter.Utils.PathHelper.GetDllPath()); + VmrApi = new RemoteApiWrapper(AtgDev.Voicemeeter.Utils.PathHelper.GetDllPath()); StartPolling(); } catch (Exception ex) @@ -74,25 +76,26 @@ private void UpdateVariables() { return; } - foreach (var option in AvailableValues.IoOptions) - { + _connected = CheckConnected(out _); if (!_connected) { break; } + foreach (var option in AvailableValues.IoOptions) + { SetVariable(option.AsParameter, option.AsVariable, option.Type); } } private void SetVariable(string parameter, string variable, VariableType type) { - if ((_connected = CheckConnected(out _)) && TryGetValue(parameter, type, out object? val, infoOnly: true)) + if (TryGetValue(parameter, type, out var val, infoOnly: true)) { - VariableManager.SetValue(variable, val!, type, PluginInstance.Plugin, Array.Empty()); + VariableManager.SetValue(variable, val, type, PluginInstance.Plugin, Array.Empty()); } } - public bool TryGetValue(string parameter, VariableType type, out object? val, bool infoOnly = false) + public bool TryGetValue(string parameter, VariableType type, out object val, bool infoOnly = false) { bool ok; switch (type) @@ -111,7 +114,7 @@ public bool TryGetValue(string parameter, VariableType type, out object? val, bo val = Constants.On.Equals(valb); break; default: - val = null; + val = string.Empty; return false; } return ok; diff --git a/Services/Voicemeeter/Control.Parameters.cs b/Services/Voicemeeter/Control.Parameters.cs index d0d3382..84dc1b4 100644 --- a/Services/Voicemeeter/Control.Parameters.cs +++ b/Services/Voicemeeter/Control.Parameters.cs @@ -1,4 +1,5 @@ using AtgDev.Voicemeeter.Types; +using AtgDev.Voicemeeter.Extensions; namespace PW.VoicemeeterPlugin.Services.Voicemeeter; @@ -65,14 +66,14 @@ public void SetParameters(string parameters) ControlHelpers.TestResult(VmrApi.SetParameters(parameters)); } - public void GetLevel(ref VoicemeeterLevel type) + public void GetLevel(ref VoicemeeterLevel level) { - ControlHelpers.TestLevelResult(VmrApi.GetLevel(ref type)); + ControlHelpers.TestLevelResult(VmrApi.GetLevel(ref level)); } - public float GetLevel(VoicemeeterLevelType type, VoicemeeterChannel channel) + public float GetLevel(VoicemeeterLevelType levelType, VoicemeeterChannel channel) { - ControlHelpers.TestLevelResult(VmrApi.GetLevel(type, channel, out float value)); + ControlHelpers.TestLevelResult(VmrApi.GetLevel(levelType, channel, out float value)); return value; } } \ No newline at end of file diff --git a/Services/Voicemeeter/ControlHelpers.cs b/Services/Voicemeeter/ControlHelpers.cs index 2ee9e26..ce44ce4 100644 --- a/Services/Voicemeeter/ControlHelpers.cs +++ b/Services/Voicemeeter/ControlHelpers.cs @@ -84,19 +84,24 @@ public static bool TestResultInfo(int result, [CallerMemberName] string callerNa } //0: OK(no error). + //>0: error in line //-1: error - //-2: no server. + //-2: no server //-3: unknown parameter //-4: structure mismatch - internal static void TestResultThrow(int result) + private static void TestResultThrow(int result) { + if (result > 0) + { + throw new Exception("Error in line " + result); + } switch (result) { case ResultCodes.Ok: break; - case ResultCodes.Error: throw new("Parameter Error"); - case ResultCodes.NoServer: throw new("Not Connected"); + case ResultCodes.Error: throw new Exception("Parameter Error"); + case ResultCodes.NoServer: throw new Exception("Not Connected"); case ResultCodes.UnexpectedError1: throw new ArgumentException("Parameter not found"); - case ResultCodes.UnexpectedError2: throw new("Structure mismatch"); + case ResultCodes.UnexpectedError2: throw new Exception("Structure mismatch"); default: throw UnknownError(result); } } diff --git a/ViewModels/StatusButtonViewModel.cs b/ViewModels/StatusButtonViewModel.cs index 1eb5b89..062ae69 100644 --- a/ViewModels/StatusButtonViewModel.cs +++ b/ViewModels/StatusButtonViewModel.cs @@ -55,6 +55,9 @@ private void UpdateStatusButton() _statusToolTip.SetToolTip(StatusButton, toolip); } } - catch { } + catch + { + // ignored + } } } \ No newline at end of file diff --git a/Views/AddAdditionalVariablesConfigView.cs b/Views/AddAdditionalVariablesConfigView.cs index 8227d21..3986a6b 100644 --- a/Views/AddAdditionalVariablesConfigView.cs +++ b/Views/AddAdditionalVariablesConfigView.cs @@ -55,7 +55,7 @@ private bool CheckOption(ReadOnlySpan id, string option, ReadOnlySpan id[i], }; } - opt = new(optId.ToString(), option.Split(';')[0], Enum.Parse(typeStr)); + opt = new VmIoOptions(optId.ToString(), option.Split(';')[0], Enum.Parse(typeStr)); int idxBracket = opt.Id.IndexOf('('); if (idxBracket < -1 && !int.TryParse(optId[idxBracket + 1].ToString(), out _)) { @@ -123,10 +123,7 @@ private void ListParameters_SelectedIndexChanged(object sender, EventArgs e) private void ButtonOk_Click(object sender, EventArgs e) { - AdditionalVariablesModel variables = new() - { - Options = new() - }; + AdditionalVariablesModel variables = new(); foreach (var item in listParameters.Items) { variables.Options.Add((VmIoOptions)item); diff --git a/Views/AdvancedActionConfigView.cs b/Views/AdvancedActionConfigView.cs index c5eead0..a72bb34 100644 --- a/Views/AdvancedActionConfigView.cs +++ b/Views/AdvancedActionConfigView.cs @@ -26,7 +26,7 @@ private void ApplyLocalization() public override bool OnActionSave() { _action.Configuration = commandsBox.Text; - var commands = _action.Configuration.Split(new[] { Environment.NewLine, ";", "," }, StringSplitOptions.RemoveEmptyEntries); + var commands = _action.Configuration.Split([Environment.NewLine, ";", ","], StringSplitOptions.RemoveEmptyEntries); _action.ConfigurationSummary = commands.Length switch { 0 => LocalizationManager.Instance.NoCommandsMsg, diff --git a/Views/DeviceSelectorConfigView.Designer.cs b/Views/DeviceSelectorConfigView.Designer.cs index 2c87976..55c974a 100644 --- a/Views/DeviceSelectorConfigView.Designer.cs +++ b/Views/DeviceSelectorConfigView.Designer.cs @@ -91,7 +91,7 @@ private void InitializeComponent() this.actionSliderValue.Minimum = -10; this.actionSliderValue.Maximum = 10; this.actionSliderValue.Size = new System.Drawing.Size(75, 31); - this.actionSliderValue.Font = new(this.actionSliderValue.Font.FontFamily, 14); + this.actionSliderValue.Font = new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.actionSliderValue.TabIndex = 0; // // DeviceSelectorView diff --git a/Views/DeviceSelectorConfigView.cs b/Views/DeviceSelectorConfigView.cs index 29e8175..91c7de6 100644 --- a/Views/DeviceSelectorConfigView.cs +++ b/Views/DeviceSelectorConfigView.cs @@ -21,6 +21,7 @@ public DeviceSelectorConfigView(DeviceSelectorViewModel viewModel) ApplyLocalization(); actionSliderValue.Visible = _viewModel.IsSlider; + labelSlider.Visible = _viewModel.IsSlider; if (_viewModel.IsSlider) { actionSliderValue.Value = (decimal)_viewModel.SliderValue; diff --git a/VoicemeeterPlugin.cs b/VoicemeeterPlugin.cs index 20f42c7..482bdd2 100644 --- a/VoicemeeterPlugin.cs +++ b/VoicemeeterPlugin.cs @@ -1,11 +1,8 @@ using SuchByte.MacroDeck.Plugins; using PW.VoicemeeterPlugin.Actions; using System; -using SuchByte.MacroDeck.GUI.CustomControls; using VoicemeeterControl = PW.VoicemeeterPlugin.Services.Voicemeeter.Control; using PW.VoicemeeterPlugin.Services; -using System.Net.Sockets; -using System.Windows.Forms; namespace PW.VoicemeeterPlugin; @@ -33,6 +30,7 @@ public override void Enable() new DeviceSliderAction(), new CommandAction(), new AdvancedAction(), + new MacroButtonAction(), }; } diff --git a/VoicemeeterPlugin.csproj b/VoicemeeterPlugin.csproj index 9a239af..7f67a98 100644 --- a/VoicemeeterPlugin.csproj +++ b/VoicemeeterPlugin.csproj @@ -2,17 +2,18 @@ 2 - 0 + 1 0 $([System.DateTime]::UtcNow.ToString("yy"))$([System.DateTime]::UtcNow.DayOfYear.ToString("000")).$([System.DateTime]::UtcNow.ToString("HHmm")) - E:\Code\source\repos\MD\Macro-Deck\MacroDeck\bin\Debug\net7.0-windows10.0.22000.0\win-x64\Data\plugins + + false + net8.0-windows + AnyCPU;x64 + latest Voicemeeter Plugin - net7.0-windows - AnyCPU;x86;x64 - default true true PW.$(MSBuildProjectName.Replace(" ", "_")) @@ -31,13 +32,13 @@ - + - E:\Code\source\repos\MD\Macro-Deck\MacroDeck\bin\Debug\net7.0-windows10.0.22000.0\win-x64\Macro Deck 2.dll - false + C:\Program Files\Macro Deck\Macro Deck 2.dll + @@ -71,6 +72,9 @@ True Resources.resx + + UserControl + @@ -83,9 +87,10 @@ - + - + + \ No newline at end of file From af7a46aac56d19d8d489f758159456a4e745302c Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:12:59 +0100 Subject: [PATCH 07/13] add Macro Button action --- Actions/MacroButtonAction.cs | 71 ++++++++++++++ Models/Localization.cs | 5 + Models/MacroButtonActionConfigModel.cs | 55 +++++++++++ Services/Voicemeeter/AvailableValues.cs | 2 +- Services/Voicemeeter/Control.Connect.cs | 4 + Services/Voicemeeter/Control.Core.cs | 63 +++++++++++- .../MacroButtonActionConfigViewModel.cs | 59 ++++++++++++ Views/MacroButtonActionConfigView.Designer.cs | 96 +++++++++++++++++++ Views/MacroButtonActionConfigView.cs | 58 +++++++++++ Views/MacroButtonActionConfigView.resx | 60 ++++++++++++ 10 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 Actions/MacroButtonAction.cs create mode 100644 Models/MacroButtonActionConfigModel.cs create mode 100644 ViewModels/MacroButtonActionConfigViewModel.cs create mode 100644 Views/MacroButtonActionConfigView.Designer.cs create mode 100644 Views/MacroButtonActionConfigView.cs create mode 100644 Views/MacroButtonActionConfigView.resx diff --git a/Actions/MacroButtonAction.cs b/Actions/MacroButtonAction.cs new file mode 100644 index 0000000..45b70f5 --- /dev/null +++ b/Actions/MacroButtonAction.cs @@ -0,0 +1,71 @@ +using PW.VoicemeeterPlugin.Models; +using PW.VoicemeeterPlugin.Services; +using PW.VoicemeeterPlugin.ViewModels; +using SuchByte.MacroDeck.ActionButton; +using SuchByte.MacroDeck.GUI; +using SuchByte.MacroDeck.GUI.CustomControls; +using SuchByte.MacroDeck.Logging; +using SuchByte.MacroDeck.Plugins; +using SuchByte.MacroDeck.Variables; + +using System.Diagnostics; + +namespace PW.VoicemeeterPlugin.Actions; + +public class MacroButtonAction : PluginAction +{ + /// + /// Name of the action + /// + public override string Name => LocalizationManager.Instance.MacroButtonActionName; + + /// + /// A short description what this action does + /// + public override string Description => LocalizationManager.Instance.MacroButtonActionDescription; + + /// + /// Set true if the plugin can be configured. + /// + public override bool CanConfigure => true; + + /// + /// Return the ActionConfigControl for this action. + /// + /// + public override ActionConfigControl GetActionConfigControl(ActionConfigurator actionConfigurator) + { + return new Views.MacroButtonActionConfigView(new MacroButtonActionConfigViewModel(this)); + } + + /// + /// Gets called when the button with this action gets pressed. + /// + /// Returns the client id + /// Returns the pressed action button + public override void Trigger(string clientId, ActionButton actionButton) + { + if (string.IsNullOrWhiteSpace(Configuration)) + { + return; + } + var config = MacroButtonActionConfigModel.Deserialize(Configuration); + var value = VariableManager.GetVariable(PluginInstance.Plugin, config.AsVariable()); + if (value is null) + { + MacroDeckLogger.Info(PluginInstance.Plugin, typeof(DeviceToggleAction), $"Please report a bug to the developer of the plugin. Expected value: {Configuration}"); + return; + } + PluginInstance.VoicemeeterControl.SetButtonState(config.ButtonId, value.Value.Equals(bool.FalseString), config.ButtonType); + //if (config.ButtonType == ButtonType.Push) + //{ + // var sw = Stopwatch.StartNew(); + // var elapsed = sw.ElapsedMilliseconds; + // while (elapsed < 100) + // { + // elapsed = sw.ElapsedMilliseconds; + // } + // PluginInstance.VoicemeeterControl.SetButtonState(config.ButtonId, value.Value.Equals(bool.FalseString)); + //} + } +} \ No newline at end of file diff --git a/Models/Localization.cs b/Models/Localization.cs index 9e11f42..c874f4d 100644 --- a/Models/Localization.cs +++ b/Models/Localization.cs @@ -29,4 +29,9 @@ internal sealed class Localization public string SliderValue { get; set; } = "Change slider by amount"; public string ErrorZeroSliderValue { get; set; } = "Amount cannot be zero"; public string ErrorNoDeviceSelected { get; set; } = "No device selected"; + public string MacroButtonActionName { get; set; } = "Use Macro Button"; + public string MacroButtonActionDescription { get; set; } = "Use a Voicemeeter Macro Button"; + public string MacroButtonButtonId { get; set; } = "Button ID"; + public string MacroButtonButtonType { get; set; } = "Button Type"; + public string MacroButtonIdError { get; set; } = "Button Id is not valid. Valid values are 0 to 79"; } \ No newline at end of file diff --git a/Models/MacroButtonActionConfigModel.cs b/Models/MacroButtonActionConfigModel.cs new file mode 100644 index 0000000..6499516 --- /dev/null +++ b/Models/MacroButtonActionConfigModel.cs @@ -0,0 +1,55 @@ +using System.Diagnostics; +using System.Text.Json; + +namespace PW.VoicemeeterPlugin.Models; + +public enum ButtonType +{ + Push, + TwoPositions, +} + +[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] +internal class MacroButtonActionConfigModel : ISerializableConfiguration +{ + public int ButtonId { get; set; } = -1; + + public ButtonType ButtonType { get; set; } + + public string Serialize() + { + return JsonSerializer.Serialize(this); + } + + public static MacroButtonActionConfigModel Deserialize(string config) + { + return ISerializableConfiguration.Deserialize(config); + } + + public override string ToString() + { + if (ButtonType == ButtonType.Push) + { + return $"Button {ButtonId}: Press"; + } + else + { + return $"Button {ButtonId}: Toggle"; + } + } + + private string GetDebuggerDisplay() + { + return ToString(); + } + + public string AsVariable() + { + return GetVariable(ButtonId); + } + + public static string GetVariable(int buttonId) + { + return $"vm_macrobutton{buttonId:d2}_pressed"; + } +} diff --git a/Services/Voicemeeter/AvailableValues.cs b/Services/Voicemeeter/AvailableValues.cs index de6a7a8..300cd0a 100644 --- a/Services/Voicemeeter/AvailableValues.cs +++ b/Services/Voicemeeter/AvailableValues.cs @@ -55,7 +55,7 @@ internal static void Reset() public static List? IoInfo { get; private set; } - internal static void InitIoInfo(AtgDev.Voicemeeter.RemoteApiExtender vmrApi) + internal static void InitIoInfo(AtgDev.Voicemeeter.RemoteApiWrapper vmrApi) { void AddChannel(ICollection ioInfo, VmIoType type, int num) { diff --git a/Services/Voicemeeter/Control.Connect.cs b/Services/Voicemeeter/Control.Connect.cs index 51ee893..b45ea70 100644 --- a/Services/Voicemeeter/Control.Connect.cs +++ b/Services/Voicemeeter/Control.Connect.cs @@ -119,6 +119,10 @@ private void Poll(object? sender, ElapsedEventArgs e) { UpdateVariables(); } + if (_connected && VmrApi.MacroButtonIsDirty() > 0) + { + UpdateButtonStates(); + } } internal static event EventHandler? Polling; diff --git a/Services/Voicemeeter/Control.Core.cs b/Services/Voicemeeter/Control.Core.cs index dd76b8e..1830375 100644 --- a/Services/Voicemeeter/Control.Core.cs +++ b/Services/Voicemeeter/Control.Core.cs @@ -44,6 +44,7 @@ private void InitAvailableValues() if (initValues) { AvailableValues.InitIoInfo(VmrApi!); + UpdateButtonStates(); } if (initValues && AvailableValues.IoInfo != null) { @@ -56,7 +57,7 @@ private static void RemoveUnavailableVariables() { try { - bool IsUnavailableVariable(Variable v) => !AvailableValues.IoOptions!.Any(o => o.AsVariable.Equals(v.Name)); + bool IsUnavailableVariable(Variable v) => !v.Name.StartsWith("vm_macrobutton") && !AvailableValues.IoOptions!.Any(o => o.AsVariable.Equals(v.Name)); var variablesNotFound = VariableManager.GetVariables(PluginInstance.Plugin).Where(IsUnavailableVariable).Select(v => v.Name); foreach (var variable in variablesNotFound) @@ -77,10 +78,10 @@ private void UpdateVariables() return; } _connected = CheckConnected(out _); - if (!_connected) - { - break; - } + if (!_connected) + { + return; + } foreach (var option in AvailableValues.IoOptions) { SetVariable(option.AsParameter, option.AsVariable, option.Type); @@ -119,4 +120,56 @@ public bool TryGetValue(string parameter, VariableType type, out object val, boo } return ok; } + + private void UpdateButtonStates() + { + _connected = CheckConnected(out _); + if (!_connected) + { + return; + } + foreach (var btnId in Enumerable.Range(0, 80)) + { + SetButtonStateVariable(btnId); + } + } + + private void SetButtonStateVariable(int btnId) + { + var variable = MacroButtonActionConfigModel.GetVariable(btnId); + var isOn = GetButtonState(btnId); + VariableManager.SetValue(variable, isOn, VariableType.Bool, PluginInstance.Plugin, Array.Empty()); + } + + private bool GetButtonState(int btnId) + { + var val = false; + if (VmrApi!.MacroButtonGetStatus(btnId, out bool state, MacrobuttonMode.Default) == 0) + { + val = state; + } + return val; + } + + public void SetButtonState(int btnId, bool state, ButtonType buttonType) + { + _connected = CheckConnected(out _); + if (!_connected) + { + return; + } + if (VmrApi!.MacroButtonSetStatus(btnId, state,MacrobuttonMode.Default) != 0) + { + MacroDeckLogger.Warning(PluginInstance.Plugin, $"Failed to set button state: {btnId}, {state}"); + } + + if (buttonType == ButtonType.Push) + { + VmrApi.WaitForMacroNewParams(); + if (VmrApi!.MacroButtonSetStatus(btnId, !state, MacrobuttonMode.Default) != 0) + { + MacroDeckLogger.Warning(PluginInstance.Plugin, $"Failed to set button state: {btnId}, {state}"); + } + } + } } \ No newline at end of file diff --git a/ViewModels/MacroButtonActionConfigViewModel.cs b/ViewModels/MacroButtonActionConfigViewModel.cs new file mode 100644 index 0000000..2060052 --- /dev/null +++ b/ViewModels/MacroButtonActionConfigViewModel.cs @@ -0,0 +1,59 @@ +using PW.VoicemeeterPlugin.Models; + +using SuchByte.MacroDeck.Logging; +using SuchByte.MacroDeck.Plugins; + +using System; + +namespace PW.VoicemeeterPlugin.ViewModels; +public class MacroButtonActionConfigViewModel : ISavableConfigViewModel +{ + private readonly PluginAction _action; + private readonly MacroButtonActionConfigModel _configuration; + + ISerializableConfiguration ISavableConfigViewModel.SerializableConfiguration => _configuration; + + public MacroButtonActionConfigViewModel(PluginAction action) + { + _action = action; + _configuration = MacroButtonActionConfigModel.Deserialize(action.Configuration); + + ButtonId = _configuration.ButtonId; + ButtonType = _configuration.ButtonType; + } + + public bool ValidConfig { get; private set; } + + public int ButtonId { get; set; } + + public ButtonType ButtonType { get; set; } + + public void SetConfig() + { + //check button id is between 0 and 80 + ValidConfig = ButtonId >= 0 || ButtonId < 80; + if (!ValidConfig) + { + return; + } + _configuration.ButtonId = ButtonId; + _configuration.ButtonType = ButtonType; + _action.BindableVariable = _configuration.AsVariable(); //note: this only does something when the user creates a new button, not when they edit an existing one + _action.ConfigurationSummary = _configuration.ToString(); + _action.Configuration = _configuration.Serialize(); + } + + public void SaveConfig() + { + try + { + SetConfig(); + } + catch (Exception ex) + { + MacroDeckLogger.Warning(PluginInstance.Plugin, ex.Message); + MacroDeckLogger.Trace(PluginInstance.Plugin, ex.StackTrace ?? "No stack"); + } + } + +} diff --git a/Views/MacroButtonActionConfigView.Designer.cs b/Views/MacroButtonActionConfigView.Designer.cs new file mode 100644 index 0000000..bdd468f --- /dev/null +++ b/Views/MacroButtonActionConfigView.Designer.cs @@ -0,0 +1,96 @@ +using System; + +namespace PW.VoicemeeterPlugin.Views +{ + partial class MacroButtonActionConfigView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.labelButtonId = new System.Windows.Forms.Label(); + this.buttonIdBox = new SuchByte.MacroDeck.GUI.CustomControls.RoundedComboBox(); + this.labelButtonType = new System.Windows.Forms.Label(); + this.buttonTypeBox = new SuchByte.MacroDeck.GUI.CustomControls.RoundedComboBox(); + this.SuspendLayout(); + // + // labelButtonId + // + this.labelButtonId.AutoSize = true; + this.labelButtonId.Location = new System.Drawing.Point(40, 45); + this.labelButtonId.Name = "labelButtonId"; + this.labelButtonId.Size = new System.Drawing.Size(84, 23); + this.labelButtonId.TabIndex = 0; + this.labelButtonId.Text = "ButtonId"; + this.labelButtonId.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // buttonIdBox + // + this.buttonIdBox.Location = new System.Drawing.Point(60, 79); + this.buttonIdBox.Name = "buttonIdBox"; + this.buttonIdBox.Size = new System.Drawing.Size(543, 31); + this.buttonIdBox.TabIndex = 0; + this.buttonIdBox.SelectedIndexChanged += DeviceSelectorBox_SelectedIndexChanged; + // + // labelButtonType + // + this.labelButtonType.AutoSize = true; + this.labelButtonType.Location = new System.Drawing.Point(40, 120); + this.labelButtonType.Name = "labelButtonType"; + this.labelButtonType.Size = new System.Drawing.Size(84, 23); + this.labelButtonType.TabIndex = 0; + this.labelButtonType.Text = "ButtonType"; + this.labelButtonType.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // buttonTypeBox + // + this.buttonTypeBox.Location = new System.Drawing.Point(60, 154); + this.buttonTypeBox.Name = "buttonTypeBox"; + this.buttonTypeBox.Size = new System.Drawing.Size(543, 31); + this.buttonTypeBox.TabIndex = 0; + this.buttonTypeBox.SelectedIndexChanged += TypeSelectorBox_SelectedIndexChanged; + // + // MacroButtonActionConfigView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.labelButtonId); + this.Controls.Add(this.buttonIdBox); + this.Controls.Add(this.labelButtonType); + this.Controls.Add(this.buttonTypeBox); + this.Name = "MacroButtonActionConfigView"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label labelButtonId; + private SuchByte.MacroDeck.GUI.CustomControls.RoundedComboBox buttonIdBox; + private System.Windows.Forms.Label labelButtonType; + private SuchByte.MacroDeck.GUI.CustomControls.RoundedComboBox buttonTypeBox; + } +} \ No newline at end of file diff --git a/Views/MacroButtonActionConfigView.cs b/Views/MacroButtonActionConfigView.cs new file mode 100644 index 0000000..2a49026 --- /dev/null +++ b/Views/MacroButtonActionConfigView.cs @@ -0,0 +1,58 @@ +using PW.VoicemeeterPlugin.Models; +using PW.VoicemeeterPlugin.Services; +using PW.VoicemeeterPlugin.ViewModels; +using SuchByte.MacroDeck.GUI.CustomControls; +using System; +using System.Linq; +using System.Windows.Forms; +using MessageBox = SuchByte.MacroDeck.GUI.CustomControls.MessageBox; + +namespace PW.VoicemeeterPlugin.Views; + +public partial class MacroButtonActionConfigView : ActionConfigControl +{ + private readonly MacroButtonActionConfigViewModel _viewModel; + + public MacroButtonActionConfigView(MacroButtonActionConfigViewModel viewModel) + { + _viewModel = viewModel; + + InitializeComponent(); + ApplyLocalization(); + + buttonIdBox.Items.AddRange(Enumerable.Range(0, 80).Select(x => x.ToString()).ToArray()); + buttonTypeBox.Items.AddRange(Enum.GetNames()); + buttonIdBox.SelectedIndex = _viewModel.ButtonId; // zero-based index and id + buttonIdBox.SelectedIndex = (int)_viewModel.ButtonType; + } + + private void ApplyLocalization() + { + labelButtonId.Text = LocalizationManager.Instance.MacroButtonButtonId; + labelButtonType.Text = LocalizationManager.Instance.MacroButtonButtonType; + } + + public override bool OnActionSave() + { + _viewModel.SaveConfig(); + + if (!_viewModel.ValidConfig) + { + using var msgBox = new MessageBox(); + msgBox.ShowDialog(SuchByte.MacroDeck.Language.LanguageManager.Strings.Info, LocalizationManager.Instance.MacroButtonIdError, MessageBoxButtons.OK); + } + + return _viewModel.ValidConfig; + } + + private void DeviceSelectorBox_SelectedIndexChanged(object sender, EventArgs e) + { + _viewModel.ButtonId = buttonIdBox.SelectedIndex; + } + + private void TypeSelectorBox_SelectedIndexChanged(object sender, EventArgs e) + { + _viewModel.ButtonType = (ButtonType)buttonTypeBox.SelectedIndex; + } + +} \ No newline at end of file diff --git a/Views/MacroButtonActionConfigView.resx b/Views/MacroButtonActionConfigView.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/Views/MacroButtonActionConfigView.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file From 0c2d81c4040bd4da52bfdc50baff6c63e8ae3a4c Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:32 +0100 Subject: [PATCH 08/13] Crowdin: Edited English --- Languages/English.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Languages/English.json b/Languages/English.json index 315a956..2d15f41 100644 --- a/Languages/English.json +++ b/Languages/English.json @@ -24,5 +24,10 @@ "SliderActionName": "Slider control", "SliderActionDescription": "Change a slider by a given amount", "SliderValue": "Change slider by amount", - "ErrorZeroSliderValue": "Amount cannot be zero." + "ErrorZeroSliderValue": "Amount cannot be zero.", + "MacroButtonActionName": "Use Macro Button", + "MacroButtonActionDescription": "Use a Voicemeeter Macro Button", + "MacroButtonButtonId": "Logical ID", + "MacroButtonButtonType": "Button Type", + "MacroButtonIdError": "Button Id is not valid. Valid values are 0 to 79" } \ No newline at end of file From c39a485c03a4e65815b97edeaf1919ab0a50b803 Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:33 +0100 Subject: [PATCH 09/13] Crowdin: Edited Spanish --- Languages/Spanish.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Languages/Spanish.json b/Languages/Spanish.json index d0210d6..cb7384b 100644 --- a/Languages/Spanish.json +++ b/Languages/Spanish.json @@ -24,5 +24,10 @@ "SliderActionName": "Slider control", "SliderActionDescription": "Change a slider by a given amount", "SliderValue": "Change slider by amount", - "ErrorZeroSliderValue": "Amount cannot be zero." + "ErrorZeroSliderValue": "Amount cannot be zero.", + "MacroButtonActionName": "Use Macro Button", + "MacroButtonActionDescription": "Use a Voicemeeter Macro Button", + "MacroButtonButtonId": "Logical ID", + "MacroButtonButtonType": "Button Type", + "MacroButtonIdError": "Button Id is not valid. Valid values are 0 to 79" } \ No newline at end of file From 809abdda74e70a56272ffc93006b5e6b93e0223e Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:34 +0100 Subject: [PATCH 10/13] Crowdin: Edited Italian --- Languages/Italian.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Languages/Italian.json b/Languages/Italian.json index 37e1b15..eb625a5 100644 --- a/Languages/Italian.json +++ b/Languages/Italian.json @@ -24,5 +24,10 @@ "SliderActionName": "Controllo slider", "SliderActionDescription": "Varia slider per una determinata quantità", "SliderValue": "Variazione", - "ErrorZeroSliderValue": "La quantità non può essere pari a zero." + "ErrorZeroSliderValue": "La quantità non può essere pari a zero.", + "MacroButtonActionName": "Usa Macro Button", + "MacroButtonActionDescription": "Usa una macro di Voicemeeter", + "MacroButtonButtonId": "ID Logico", + "MacroButtonButtonType": "Tipo Bottone", + "MacroButtonIdError": "L'ID non è valido. I valori validi sono da 0 a 79" } \ No newline at end of file From bd68aff72dc2734a7708ac8b7ea5e8023036b17b Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:35 +0100 Subject: [PATCH 11/13] Crowdin: Edited Polish --- Languages/Polish.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Languages/Polish.json b/Languages/Polish.json index 8cb785c..51db907 100644 --- a/Languages/Polish.json +++ b/Languages/Polish.json @@ -24,5 +24,10 @@ "SliderActionName": "Sterowanie suwakiem", "SliderActionDescription": "Przesuń suwak o daną wartość", "SliderValue": "Przesuń suwak o wartość", - "ErrorZeroSliderValue": "Wartość nie może być równa zeru." + "ErrorZeroSliderValue": "Wartość nie może być równa zeru.", + "MacroButtonActionName": "Use Macro Button", + "MacroButtonActionDescription": "Use a Voicemeeter Macro Button", + "MacroButtonButtonId": "Logical ID", + "MacroButtonButtonType": "Button Type", + "MacroButtonIdError": "Button Id is not valid. Valid values are 0 to 79" } \ No newline at end of file From 50863d4eb346a2250abf95c2260b83702124ce18 Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:24:36 +0100 Subject: [PATCH 12/13] Crowdin: Edited Chinese --- Languages/Chinese.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Languages/Chinese.json b/Languages/Chinese.json index 4b026ed..2737dbd 100644 --- a/Languages/Chinese.json +++ b/Languages/Chinese.json @@ -24,5 +24,10 @@ "SliderActionName": "Slider control", "SliderActionDescription": "Change a slider by a given amount", "SliderValue": "Change slider by amount", - "ErrorZeroSliderValue": "Amount cannot be zero." + "ErrorZeroSliderValue": "Amount cannot be zero.", + "MacroButtonActionName": "Use Macro Button", + "MacroButtonActionDescription": "Use a Voicemeeter Macro Button", + "MacroButtonButtonId": "Logical ID", + "MacroButtonButtonType": "Button Type", + "MacroButtonIdError": "Button Id is not valid. Valid values are 0 to 79" } \ No newline at end of file From 31b8f6a3dd1a8901c5342ee934783a71bb84c7b4 Mon Sep 17 00:00:00 2001 From: Will <54237626+PhoenixWyllow@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:31:01 +0100 Subject: [PATCH 13/13] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d502373..dbddf8f 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Currently available languages: - Italian - Spanish (by Danivar) - Chinese (by PENC) +- Polish (by szopen) *** ## Third party licenses