From 25eb40d2085148866ae7f21e4d5f575061f731b5 Mon Sep 17 00:00:00 2001 From: Stephen Yager Date: Wed, 9 Dec 2020 20:20:57 -0800 Subject: [PATCH 01/14] Added support for advanced logic in Advanced Toggle --- VoiceMeeter/Actions/VMAdvancedToggle.cs | 104 +++++++++++++++++- VoiceMeeter/VMManager.cs | 5 + VoiceMeeter/VoiceMeeterWrapper/VmClient.cs | 11 ++ .../VoiceMeeterWrapper/VoiceMeeterRemote.cs | 3 +- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/VoiceMeeter/Actions/VMAdvancedToggle.cs b/VoiceMeeter/Actions/VMAdvancedToggle.cs index 0fbff08..ebf092c 100644 --- a/VoiceMeeter/Actions/VMAdvancedToggle.cs +++ b/VoiceMeeter/Actions/VMAdvancedToggle.cs @@ -100,6 +100,7 @@ public static PluginSettings CreateDefaultSettings() #region Private members private const string LOGICAL_AND = " AND "; private const string LOGICAL_OR = " OR "; + private const string LOGICAL_CUST = "ADV "; private readonly PluginSettings settings; private bool didSetNotConnected = false; @@ -136,7 +137,7 @@ public async override void KeyPressed(KeyPayload payload) return; } - bool isMode1 = IsMode1(true); + bool isMode1 = IsMode1(true, this.GetType().ToString()); if (isMode1) // Currently in Mode1, so run Mode2 commands { if (!String.IsNullOrEmpty(settings.Mode2Value)) @@ -182,7 +183,7 @@ public async override void OnTick() } // Set the image - if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1(false)) + if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1(false, "") ) { await Connection.SetImageAsync(Tools.FileToBase64(settings.UserImage1.ToString(), true)); } @@ -233,8 +234,21 @@ public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payloa #region Private Methods - private bool IsMode1(bool shouldLog) + private string RemoveQuotes(string thatString) { + // + // Remove whites spaces, then remove leading and ending quotation marks... + // + thatString = thatString.Trim(); + thatString = thatString.TrimStart('"'); + thatString = thatString.TrimEnd('"'); + + return thatString; + } + + private bool IsMode1(bool shouldLog, string pressType) + { + if (String.IsNullOrEmpty(settings.Mode1Param)) { return false; @@ -284,6 +298,90 @@ private bool IsMode1(bool shouldLog) } } return false; + } + // + // Only fire for advance toggle... + // + else if(pressType == "VoiceMeeter.VMAdvancedToggleAction" && settings.Mode1Param.Contains(LOGICAL_CUST)) + { + + // + // Let's figure out which logical operator we are using... + // Then split the string based on that + // + if( settings.Mode1Param.Contains("==") ) + { + string[] separator = { "==" }; + string[] eq_bits = settings.Mode1Param.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); + + // + // Clean up the remote value... + // + string remoteParam = eq_bits[0].Substring(LOGICAL_CUST.Length).Trim(); + + // + // Get the value from VM... + // + string remoteValue = VMManager.Instance.GetParamString(remoteParam); + + // + // Remove quotes from comparison operator... + // + string compVal = RemoveQuotes(eq_bits[1]); + + if (shouldLog) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"Mode1 returned: {remoteValue} == {compVal}"); + } + + return remoteValue == compVal; + + } + + // + // Let's figure out which logical operator we are using... + // Then split the string based on that + // + if (settings.Mode1Param.Contains("!=")) + { + string[] separator = { "!=" }; + string[] eq_bits = settings.Mode1Param.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); + + // + // Clean up the remote value... + // + string remoteParam = eq_bits[0].Substring(LOGICAL_CUST.Length).Trim(); + + // + // Get the value from VM... + // + string remoteValue = VMManager.Instance.GetParamString(remoteParam); + + // + // Remove quotes from comparison operator... + // + string compVal = RemoveQuotes(eq_bits[1]); + + if (shouldLog) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"Mode1 returned: {remoteValue} != {compVal}"); + } + + return remoteValue != compVal; + + } + + // + // If you made it here, then there was no matching operator... + // + if (shouldLog) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"No matching comparison operator: only supports == and !="); + } + + return true; + + } else // Only one clause { diff --git a/VoiceMeeter/VMManager.cs b/VoiceMeeter/VMManager.cs index e7397c9..76f7fca 100644 --- a/VoiceMeeter/VMManager.cs +++ b/VoiceMeeter/VMManager.cs @@ -75,6 +75,11 @@ public string GetParam(string paramName) return client.GetParam(paramName).ToString("0.##"); } + public string GetParamString(string paramName) + { + return client.GetParamString(paramName); + } + public void SetParam(string paramName, float value) { client.SetParam(paramName, value); diff --git a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs index 87d8ca4..bbdc4b1 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs @@ -1,5 +1,7 @@ using Microsoft.Win32; using System; +using System.Text; +using BarRaider.SdTools; namespace VoiceMeeterWrapper { @@ -33,6 +35,15 @@ public float GetParam(string n) return output; } + public string GetParamString( string n) + { + //string output = ""; + StringBuilder output = new StringBuilder(512); + VoiceMeeterRemote.GetParameter(n, output); + + return output.ToString(); + } + public void SetParam(string n, float v) { VoiceMeeterRemote.SetParameter(n, v); diff --git a/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs b/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs index c66b737..971b6d8 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.Text; namespace VoiceMeeterWrapper { @@ -20,7 +21,7 @@ public static class VoiceMeeterRemote public static extern int GetParameter(string szParamName, ref float value); [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_GetParameterStringA")] - public static extern int GetParameter(string szParamName, ref string value); + public static extern int GetParameter(string szParamName, StringBuilder value); [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_IsParametersDirty")] public static extern int IsParametersDirty(); From d4addcca69279aa007b418337798e5bb6e7c8f30 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:04:43 +0200 Subject: [PATCH 02/14] Migrate project to new VS format --- VoiceMeeter.sln | 20 +- VoiceMeeter/Properties/AssemblyInfo.cs | 25 -- VoiceMeeter/VoiceMeeter.csproj | 382 ++++++++++++------------- VoiceMeeter/packages.config | 11 - 4 files changed, 192 insertions(+), 246 deletions(-) delete mode 100644 VoiceMeeter/packages.config diff --git a/VoiceMeeter.sln b/VoiceMeeter.sln index b29433f..37bc8eb 100644 --- a/VoiceMeeter.sln +++ b/VoiceMeeter.sln @@ -1,20 +1,26 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.106 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VoiceMeeter", "VoiceMeeter\VoiceMeeter.csproj", "{A35FB18D-7867-4F77-A9F3-863BF6F18C6F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceMeeter", "VoiceMeeter\VoiceMeeter.csproj", "{0D702B80-1750-4153-9DCC-1594D2CD46CC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A35FB18D-7867-4F77-A9F3-863BF6F18C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A35FB18D-7867-4F77-A9F3-863BF6F18C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A35FB18D-7867-4F77-A9F3-863BF6F18C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A35FB18D-7867-4F77-A9F3-863BF6F18C6F}.Release|Any CPU.Build.0 = Release|Any CPU + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Debug|x64.ActiveCfg = Debug|x64 + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Debug|x64.Build.0 = Debug|x64 + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Release|Any CPU.Build.0 = Release|Any CPU + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Release|x64.ActiveCfg = Release|x64 + {0D702B80-1750-4153-9DCC-1594D2CD46CC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/VoiceMeeter/Properties/AssemblyInfo.cs b/VoiceMeeter/Properties/AssemblyInfo.cs index 2a91563..e38634d 100644 --- a/VoiceMeeter/Properties/AssemblyInfo.cs +++ b/VoiceMeeter/Properties/AssemblyInfo.cs @@ -2,18 +2,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("com.barraider.voicemeeter")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("com.barraider.voicemeeter")] -[assembly: AssemblyCopyright("Copyright © 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. @@ -21,16 +9,3 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a35fb18d-7867-4f77-a9f3-863bf6f18c6f")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/VoiceMeeter/VoiceMeeter.csproj b/VoiceMeeter/VoiceMeeter.csproj index 08dab48..3553658 100644 --- a/VoiceMeeter/VoiceMeeter.csproj +++ b/VoiceMeeter/VoiceMeeter.csproj @@ -1,204 +1,180 @@ - - - - - Debug - AnyCPU - {0D702B80-1750-4153-9DCC-1594D2CD46CC} - Exe - VoiceMeeter - com.barraider.voicemeeter - v4.8 - 512 - true - true - - - - - - AnyCPU - true - full - false - bin\Debug\com.barraider.voicemeeter.sdPlugin\ - DEBUG;TRACE - prompt - 4 - 8.0 - - - AnyCPU - pdbonly - true - bin\Release\com.barraider.voicemeeter.sdPlugin\ - TRACE - prompt - 4 - 8.0 - - - brlogo.ico - - - - ..\..\..\DotNet\StreamDeck\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll - - - ..\..\..\DotNet\StreamDeck\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - - ..\..\..\DotNet\StreamDeck\packages\NLog.4.7.10\lib\net45\NLog.dll - - - ..\..\..\DotNet\StreamDeck\packages\RtMidi.Core.1.0.51\lib\netstandard2.0\RtMidi.Core.dll - - - ..\..\..\DotNet\StreamDeck\packages\Serilog.2.10.0\lib\net46\Serilog.dll - - - ..\..\..\DotNet\StreamDeck\packages\streamdeck-client-csharp.4.3.0\lib\netstandard2.0\streamdeck-client-csharp.dll - - - ..\..\..\DotNet\StreamDeck\packages\StreamDeck-Tools.3.2.0\lib\net472\StreamDeckTools.dll - - - - - - - ..\..\..\DotNet\StreamDeck\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll - - - - - - - - - - - - - False - ..\..\InputSimulatorPlus\WindowsInput\bin\Release\WindowsInput.dll - - - - - - - - True - True - Plugin.settings - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - SettingsSingleFileGenerator - Plugin1.Designer.cs - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + {0D702B80-1750-4153-9DCC-1594D2CD46CC} + Exe + com.barraider.voicemeeter + net48 + true + com.barraider.voicemeeter + com.barraider.voicemeeter + Copyright © 2020 + 8.0 + false + bin\$(Configuration)\com.barraider.voicemeeter.sdPlugin\ + AnyCPU;x64 + + + full + + + full + + + pdbonly + + + pdbonly + + + brlogo.ico + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + + + + ..\..\..\DotNet\StreamDeck\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll + + + ..\..\..\DotNet\StreamDeck\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\..\..\DotNet\StreamDeck\packages\NLog.4.7.10\lib\net45\NLog.dll + + + ..\..\..\DotNet\StreamDeck\packages\RtMidi.Core.1.0.51\lib\netstandard2.0\RtMidi.Core.dll + + + ..\..\..\DotNet\StreamDeck\packages\Serilog.2.10.0\lib\net46\Serilog.dll + + + ..\..\..\DotNet\StreamDeck\packages\streamdeck-client-csharp.4.3.0\lib\netstandard2.0\streamdeck-client-csharp.dll + + + ..\..\..\DotNet\StreamDeck\packages\StreamDeck-Tools.3.2.0\lib\net472\StreamDeckTools.dll + + + + ..\..\..\DotNet\StreamDeck\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll + + + + + + + + + ..\..\InputSimulatorPlus\WindowsInput\bin\Release\WindowsInput.dll + False + + + + + True + True + Plugin.settings + + + + + PreserveNewest + + + SettingsSingleFileGenerator + Plugin1.Designer.cs + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + \ No newline at end of file diff --git a/VoiceMeeter/packages.config b/VoiceMeeter/packages.config deleted file mode 100644 index ac155d4..0000000 --- a/VoiceMeeter/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file From a993a29cdea13c049252489129ec65ef95452390 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:05:43 +0200 Subject: [PATCH 03/14] Port to 64bit support (VoiceMeeterRemote64.dll) --- VoiceMeeter/VoiceMeeterWrapper/Constants.cs | 1 + VoiceMeeter/VoiceMeeterWrapper/VmClient.cs | 33 ++++++++++++++----- .../VoiceMeeterWrapper/VoiceMeeterRemote.cs | 25 +++++++------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/VoiceMeeter/VoiceMeeterWrapper/Constants.cs b/VoiceMeeter/VoiceMeeterWrapper/Constants.cs index f2e29af..dcd5972 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/Constants.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/Constants.cs @@ -10,5 +10,6 @@ public static class Constants { public const string ENABLED_VALUE = "1"; public const string DISABLED_VALUE = "0"; + public const string VOICEMEETER_REMOTE_DLL = "VoicemeeterRemote64.dll"; } } diff --git a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs index bbdc4b1..00f7794 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs @@ -2,6 +2,7 @@ using System; using System.Text; using BarRaider.SdTools; +using VoiceMeeter; namespace VoiceMeeterWrapper { @@ -9,24 +10,38 @@ public class VmClient : IDisposable { public VbLoginResponse LoginResponse { get; private set; } private Action _onClose = null; + + private readonly string[] VM_UNINSTALL_REG_LOCATIONS = { @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", + @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"}; + const string VM_UNINSTALL_KEY = "VB:Voicemeeter {17359A74-1236-5467}"; + + private string GetVoicemeeterDir() { - const string regKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; - const string uninstKey = "VB:Voicemeeter {17359A74-1236-5467}"; - var key = $"{regKey}\\{uninstKey}"; - var k = Registry.GetValue(key, "UninstallString", null); - if (k == null) + foreach (var regKey in VM_UNINSTALL_REG_LOCATIONS) { - throw new Exception("Voicemeeter not found"); + var key = $"{regKey}\\{VM_UNINSTALL_KEY}"; + var k = Registry.GetValue(key, "UninstallString", null); + if (k == null) + { + continue; + } + return System.IO.Path.GetDirectoryName(k.ToString()); } - return System.IO.Path.GetDirectoryName(k.ToString()); + + Logger.Instance.LogMessage(TracingLevel.FATAL, $"{this.GetType()} Voicemeeter not found"); + return null; } public VmClient() { + System.Threading.Thread.Sleep(15000); //Find Voicemeeter dir. var vmDir = GetVoicemeeterDir(); - VoiceMeeterRemote.LoadDll(System.IO.Path.Combine(vmDir, "VoicemeeterRemote.dll")); - LoginResponse = VoiceMeeterRemote.Login(); + if (vmDir != null) + { + VoiceMeeterRemote.LoadDll(System.IO.Path.Combine(vmDir, Constants.VOICEMEETER_REMOTE_DLL)); + LoginResponse = VoiceMeeterRemote.Login(); + } } public float GetParam(string n) { diff --git a/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs b/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs index 971b6d8..0eed003 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs @@ -1,44 +1,45 @@ using System; using System.Runtime.InteropServices; using System.Text; +using VoiceMeeter; namespace VoiceMeeterWrapper { public static class VoiceMeeterRemote { - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_Login")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_Login")] public static extern VbLoginResponse Login(); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_Logout")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_Logout")] public static extern VbLoginResponse Logout(); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_SetParameterFloat")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_SetParameterFloat")] public static extern int SetParameter(string szParamName, float value); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_SetParameterStringA")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_SetParameterStringA")] public static extern int SetParameter(string szParamName, string value); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_GetParameterFloat")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_GetParameterFloat")] public static extern int GetParameter(string szParamName, ref float value); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_GetParameterStringA")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_GetParameterStringA")] public static extern int GetParameter(string szParamName, StringBuilder value); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_IsParametersDirty")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_IsParametersDirty")] public static extern int IsParametersDirty(); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_MacroButton_IsDirty")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_MacroButton_IsDirty")] public static extern int IsMacrosDirty(); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_GetVoicemeeterVersion")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_GetVoicemeeterVersion")] public static extern int GetVoicemeeterVersion(ref long value); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_SetParameters")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_SetParameters")] public static extern int SetParameters(string szParameters); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_MacroButton_GetStatus")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_MacroButton_GetStatus")] public static extern int GetMacroStatus(int buttonId, ref float value, int bitmode); - [DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_MacroButton_SetStatus")] + [DllImport(Constants.VOICEMEETER_REMOTE_DLL, EntryPoint = "VBVMR_MacroButton_SetStatus")] public static extern int SetMacroStatus(int buttonId, float value, int bitmode); [DllImport("kernel32.dll")] From b7798f9e362f1cb5baee70086b09dc7bd273e0ae Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:06:13 +0200 Subject: [PATCH 04/14] Update to support v6 --- VoiceMeeter/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VoiceMeeter/manifest.json b/VoiceMeeter/manifest.json index 164784b..c9ee02a 100644 --- a/VoiceMeeter/manifest.json +++ b/VoiceMeeter/manifest.json @@ -102,7 +102,7 @@ "Name": "VoiceMeeter Integration", "Icon": "Images/pluginIcon", "URL": "https://BarRaider.com/", - "Version": "2.0", + "Version": "2.0.1", "CodePath": "com.barraider.voicemeeter.exe", "Category": "VoiceMeeter [BarRaider]", "CategoryIcon": "Images/categoryIcon", @@ -114,6 +114,6 @@ ], "SDKVersion": 2, "Software": { - "MinimumVersion": "5.0" + "MinimumVersion": "6.0" } } From f8f39ac338541644d749a15a7f7b20e8a0325fce Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 08:51:40 +0200 Subject: [PATCH 05/14] Initial refactoring of AdvancedToggle logic --- VoiceMeeter/Actions/VMAdvancedToggle.cs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/VoiceMeeter/Actions/VMAdvancedToggle.cs b/VoiceMeeter/Actions/VMAdvancedToggle.cs index ebf092c..40c429c 100644 --- a/VoiceMeeter/Actions/VMAdvancedToggle.cs +++ b/VoiceMeeter/Actions/VMAdvancedToggle.cs @@ -137,7 +137,7 @@ public async override void KeyPressed(KeyPayload payload) return; } - bool isMode1 = IsMode1(true, this.GetType().ToString()); + bool isMode1 = IsMode1LogicTrue(true, true); if (isMode1) // Currently in Mode1, so run Mode2 commands { if (!String.IsNullOrEmpty(settings.Mode2Value)) @@ -183,7 +183,7 @@ public async override void OnTick() } // Set the image - if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1(false, "") ) + if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1LogicTrue(false, false) ) { await Connection.SetImageAsync(Tools.FileToBase64(settings.UserImage1.ToString(), true)); } @@ -234,19 +234,12 @@ public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payloa #region Private Methods - private string RemoveQuotes(string thatString) + private string RemoveQuotes(string str) { - // - // Remove whites spaces, then remove leading and ending quotation marks... - // - thatString = thatString.Trim(); - thatString = thatString.TrimStart('"'); - thatString = thatString.TrimEnd('"'); - - return thatString; + return str.Trim().Trim('"'); } - private bool IsMode1(bool shouldLog, string pressType) + private bool IsMode1LogicTrue(bool shouldLog, bool keyPressed) { if (String.IsNullOrEmpty(settings.Mode1Param)) @@ -302,9 +295,8 @@ private bool IsMode1(bool shouldLog, string pressType) // // Only fire for advance toggle... // - else if(pressType == "VoiceMeeter.VMAdvancedToggleAction" && settings.Mode1Param.Contains(LOGICAL_CUST)) + else if(keyPressed && settings.Mode1Param.Contains(LOGICAL_CUST)) { - // // Let's figure out which logical operator we are using... // Then split the string based on that From c5187f69451773c5a769c2960fe8a587de289297 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:21:41 +0200 Subject: [PATCH 06/14] Title support --- VoiceMeeter/Actions/VMAdvancedPTTAction.cs | 18 +++++++++--------- VoiceMeeter/Actions/VMAdvancedPressAction.cs | 17 ++++++++--------- VoiceMeeter/Actions/VMAdvancedToggle.cs | 17 ++++++++--------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/VoiceMeeter/Actions/VMAdvancedPTTAction.cs b/VoiceMeeter/Actions/VMAdvancedPTTAction.cs index beb5869..038adea 100644 --- a/VoiceMeeter/Actions/VMAdvancedPTTAction.cs +++ b/VoiceMeeter/Actions/VMAdvancedPTTAction.cs @@ -12,7 +12,7 @@ namespace VoiceMeeter { [PluginActionId("com.barraider.vmadvancedptt")] - public class VMAdvancedPTTAction : PluginBase + public class VMAdvancedPTTAction : KeypadBase { private class PluginSettings { @@ -139,15 +139,12 @@ public async override void OnTick() await Connection.SetImageAsync((String)null); } + + string titlePrefix = settings.TitlePrefix?.Replace(@"\n", "\n"); + string value = String.Empty; if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) { - string prefix = String.Empty; - if (!String.IsNullOrEmpty(settings.TitlePrefix)) - { - prefix = settings.TitlePrefix.Replace(@"\n", "\n"); - } - - string value = VMManager.Instance.GetParam(settings.TitleParam); + value = VMManager.Instance.GetParam(settings.TitleParam); if (!String.IsNullOrEmpty(settings.EnabledText) && !String.IsNullOrEmpty(value) && value == Constants.ENABLED_VALUE) { value = settings.EnabledText; @@ -156,8 +153,11 @@ public async override void OnTick() { value = settings.DisabledText; } + } - await Connection.SetTitleAsync($"{prefix}{value}"); + if (!String.IsNullOrEmpty($"{titlePrefix}{value}")) + { + await Connection.SetTitleAsync($"{titlePrefix}{value}"); } } diff --git a/VoiceMeeter/Actions/VMAdvancedPressAction.cs b/VoiceMeeter/Actions/VMAdvancedPressAction.cs index 2e46e71..acbe25e 100644 --- a/VoiceMeeter/Actions/VMAdvancedPressAction.cs +++ b/VoiceMeeter/Actions/VMAdvancedPressAction.cs @@ -12,7 +12,7 @@ namespace VoiceMeeter { [PluginActionId("com.barraider.vmadvanced")] - class VMAdvancedPressAction : PluginBase + class VMAdvancedPressAction : KeypadBase { private class PluginSettings { @@ -183,15 +183,11 @@ public async override void OnTick() await Connection.SetImageAsync((String)null); } + string titlePrefix = settings.TitlePrefix?.Replace(@"\n", "\n"); + string value = String.Empty; if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) { - string prefix = String.Empty; - if (!String.IsNullOrEmpty(settings.TitlePrefix)) - { - prefix = settings.TitlePrefix.Replace(@"\n", "\n"); - } - - string value = VMManager.Instance.GetParam(settings.TitleParam); + value = VMManager.Instance.GetParam(settings.TitleParam); if (!String.IsNullOrEmpty(settings.EnabledText) && !String.IsNullOrEmpty(value) && value == Constants.ENABLED_VALUE) { value = settings.EnabledText; @@ -200,8 +196,11 @@ public async override void OnTick() { value = settings.DisabledText; } + } - await Connection.SetTitleAsync($"{prefix}{value}"); + if (!String.IsNullOrEmpty($"{titlePrefix}{value}")) + { + await Connection.SetTitleAsync($"{titlePrefix}{value}"); } } diff --git a/VoiceMeeter/Actions/VMAdvancedToggle.cs b/VoiceMeeter/Actions/VMAdvancedToggle.cs index 40c429c..0d7ddfb 100644 --- a/VoiceMeeter/Actions/VMAdvancedToggle.cs +++ b/VoiceMeeter/Actions/VMAdvancedToggle.cs @@ -25,7 +25,7 @@ namespace VoiceMeeter // Subscriber: brandoncc2 x2 Gifted subs //--------------------------------------------------- [PluginActionId("com.barraider.vmadvancedtoggle")] - class VMAdvancedToggleAction : PluginBase + class VMAdvancedToggleAction : KeypadBase { private class PluginSettings { @@ -193,15 +193,11 @@ public async override void OnTick() } // Set the title + string titlePrefix = settings.TitlePrefix?.Replace(@"\n", "\n"); + string value = String.Empty; if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) { - string prefix = String.Empty; - if (!String.IsNullOrEmpty(settings.TitlePrefix)) - { - prefix = settings.TitlePrefix.Replace(@"\n", "\n"); - } - - string value = VMManager.Instance.GetParam(settings.TitleParam); + value = VMManager.Instance.GetParam(settings.TitleParam); if (!String.IsNullOrEmpty(settings.EnabledText) && !String.IsNullOrEmpty(value) && value == Constants.ENABLED_VALUE) { value = settings.EnabledText.Replace(@"\n", "\n"); ; @@ -210,8 +206,11 @@ public async override void OnTick() { value = settings.DisabledText.Replace(@"\n", "\n"); ; } + } - await Connection.SetTitleAsync($"{prefix}{value}"); + if (!String.IsNullOrEmpty($"{titlePrefix}{value}")) + { + await Connection.SetTitleAsync($"{titlePrefix}{value}"); } } From 646f7672373b9f522709241c24f34b7505610c32 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:22:36 +0200 Subject: [PATCH 07/14] SD+ dials support --- VoiceMeeter/Actions/VMGainAdjustDialAction.cs | 235 ++++++++++++++++++ .../Actions/VMSettingAdjustDialAction.cs | 228 +++++++++++++++++ VoiceMeeter/Images/muteEnabled.png | Bin 0 -> 4058 bytes VoiceMeeter/Images/settingAdjustAction.png | Bin 0 -> 5385 bytes VoiceMeeter/Images/settingAdjustAction@2x.png | Bin 0 -> 10421 bytes VoiceMeeter/Images/settingAdjustIcon.png | Bin 0 -> 346 bytes VoiceMeeter/Images/settingAdjustIcon@2x.png | Bin 0 -> 629 bytes VoiceMeeter/Images/volumeAction.png | Bin 0 -> 2960 bytes VoiceMeeter/Images/volumeAction@2x.png | Bin 0 -> 3413 bytes VoiceMeeter/Images/volumeIcon.png | Bin 0 -> 479 bytes VoiceMeeter/Images/volumeIcon@2x.png | Bin 0 -> 926 bytes .../VoiceMeeter/AdjustSettingDial.html | 55 ++++ .../VoiceMeeter/GainAdjustDial.html | 43 ++++ VoiceMeeter/VoiceMeeter.csproj | 69 ++++- VoiceMeeter/gainAdjustDialLayout.json | 40 +++ VoiceMeeter/manifest.json | 60 ++++- VoiceMeeter/settingAdjustDialLayout.json | 38 +++ 17 files changed, 759 insertions(+), 9 deletions(-) create mode 100644 VoiceMeeter/Actions/VMGainAdjustDialAction.cs create mode 100644 VoiceMeeter/Actions/VMSettingAdjustDialAction.cs create mode 100644 VoiceMeeter/Images/muteEnabled.png create mode 100644 VoiceMeeter/Images/settingAdjustAction.png create mode 100644 VoiceMeeter/Images/settingAdjustAction@2x.png create mode 100644 VoiceMeeter/Images/settingAdjustIcon.png create mode 100644 VoiceMeeter/Images/settingAdjustIcon@2x.png create mode 100644 VoiceMeeter/Images/volumeAction.png create mode 100644 VoiceMeeter/Images/volumeAction@2x.png create mode 100644 VoiceMeeter/Images/volumeIcon.png create mode 100644 VoiceMeeter/Images/volumeIcon@2x.png create mode 100644 VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html create mode 100644 VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html create mode 100644 VoiceMeeter/gainAdjustDialLayout.json create mode 100644 VoiceMeeter/settingAdjustDialLayout.json diff --git a/VoiceMeeter/Actions/VMGainAdjustDialAction.cs b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs new file mode 100644 index 0000000..76b44a8 --- /dev/null +++ b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs @@ -0,0 +1,235 @@ +using BarRaider.SdTools; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VoiceMeeter +{ + [PluginActionId("com.barraider.gainadjust")] + class VMGainAdjustDialAction : EncoderBase + { + private enum StripBusType + { + Strip = 0, + Bus = 1 + } + + private class PluginSettings + { + public static PluginSettings CreateDefaultSettings() + { + PluginSettings instance = new PluginSettings + { + Strip = StripBusType.Strip, + StripNum = 0, + Title = String.Empty + }; + + return instance; + } + + [JsonProperty(PropertyName = "strip")] + public StripBusType Strip { get; set; } + + [JsonProperty(PropertyName = "stripNum")] + public int StripNum { get; set; } + + [JsonProperty(PropertyName = "title")] + public string Title { get; set; } + } + + #region Private members + + private readonly PluginSettings settings; + private const float MIN_DB_VALUE = -60f; + private const float MAX_DB_VALUE = 12f; + + private readonly string[] DEFAULT_IMAGES = new string[] + { + @"images\muteEnabled.png", + @"images\volumeIcon@2x.png" + }; + private string mutedImageStr; + private string unmutedImageStr; + private bool didSetNotConnected = false; + private bool dialWasRotated = false; + + #endregion + + #region Public Methods + + public VMGainAdjustDialAction(SDConnection connection, InitialPayload payload) : base(connection, payload) + { + if (payload.Settings == null || payload.Settings.Count == 0) + { + this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); + } + else + { + this.settings = payload.Settings.ToObject(); + } + PrefetchImages(DEFAULT_IMAGES); + } + + #endregion + + #region PluginBase + + public async override void DialRotate(DialRotatePayload payload) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Rotate"); + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Dial Rotate but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + dialWasRotated = true; + float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float volume); + int increment = payload.Ticks; + if (payload.IsDialPressed) + { + increment = 10 * (payload.Ticks > 0 ? 1 : -1); + } + double outputVolume = Math.Round(volume + increment); + outputVolume = Math.Max(MIN_DB_VALUE, outputVolume); + outputVolume = Math.Min(MAX_DB_VALUE, outputVolume); + + VMManager.Instance.SetParam(BuildDeviceName(), (float)outputVolume); + } + + public async override void DialPress(DialPressPayload payload) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Pressed"); + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Dial Pressed but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + if (payload.IsDialPressed) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Pressed"); + dialWasRotated = false; + return; + } + + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Released"); + if (dialWasRotated) + { + return; + } + + // Run only on release and if dial wasn't rotated + ToggleMute(); + } + + public async override void TouchPress(TouchpadPressPayload payload) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Touch Pressed"); + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Touch Pressed but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + ToggleMute(); + } + + public async override void OnTick() + { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetFeedbackAsync("icon", Properties.Plugin.Default.VMNotRunning); + return; + } + else if (didSetNotConnected) + { + didSetNotConnected = false; + await Connection.SetFeedbackAsync("icon", ""); + } + + string deviceName = BuildDeviceName(); + Dictionary dkv = new Dictionary(); + if (VMManager.Instance.GetParamBool(BuildMuteName())) // Is Muted + { + dkv["icon"] = mutedImageStr; + dkv["title"] = String.IsNullOrEmpty(settings.Title) ? deviceName : settings.Title; + dkv["value"] = "Muted"; + dkv["indicator"] = "0"; + await Connection.SetFeedbackAsync(dkv); + } + else + { + _= float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float gainValue); + dkv["icon"] = unmutedImageStr; + dkv["title"] = String.IsNullOrEmpty(settings.Title) ? deviceName : settings.Title; + dkv["value"] = $"{gainValue} db"; + dkv["indicator"] = Tools.RangeToPercentage((int)gainValue, (int)MIN_DB_VALUE, (int)MAX_DB_VALUE).ToString(); + await Connection.SetFeedbackAsync(dkv); + } + } + + public override void Dispose() + { + Logger.Instance.LogMessage(TracingLevel.INFO, "Destructor called"); + } + + public async override void ReceivedSettings(ReceivedSettingsPayload payload) + { + Tools.AutoPopulateSettings(settings, payload.Settings); + await SaveSettings(); + } + + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + + #endregion + + #region Private Methods + + private string BuildDeviceName() + { + return $"{settings.Strip}[{settings.StripNum}].gain"; + } + + private string BuildMuteName() + { + return $"{settings.Strip}[{settings.StripNum}].mute"; + } + + private Task SaveSettings() + { + return Connection.SetSettingsAsync(JObject.FromObject(settings)); + } + + private void PrefetchImages(string[] defaultImages) + { + if (defaultImages.Length < 2) + { + Logger.Instance.LogMessage(TracingLevel.WARN, $"{this.GetType()} PrefetchImages: Invalid default images list"); + return; + } + + mutedImageStr = Tools.ImageToBase64(Image.FromFile(defaultImages[0]), true); + unmutedImageStr = Tools.ImageToBase64(Image.FromFile(defaultImages[1]), true); + } + + private void ToggleMute() + { + bool isMuted = VMManager.Instance.GetParamBool(BuildMuteName()); + VMManager.Instance.SetParam(BuildMuteName(), ((!isMuted) ? 1 : 0)); + } + + #endregion + } +} diff --git a/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs new file mode 100644 index 0000000..03e2f72 --- /dev/null +++ b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs @@ -0,0 +1,228 @@ +using BarRaider.SdTools; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VoiceMeeter +{ + [PluginActionId("com.barraider.settingadjust")] + class VMSettingAdjustDialAction : EncoderBase + { + private enum ParamTypeEnum + { + comp = 0, + denoiser = 1, + delay = 2, + fx1 = 3, + fx2 = 4, + gate = 5, + reverb = 6, + + } + + private enum StripBusType + { + Strip = 0, + Bus = 1 + } + + private class PluginSettings + { + public static PluginSettings CreateDefaultSettings() + { + PluginSettings instance = new PluginSettings + { + ParamType = ParamTypeEnum.comp, + Strip = StripBusType.Strip, + StripNum = 0, + Title = String.Empty + }; + + return instance; + } + + [JsonProperty(PropertyName = "paramType")] + public ParamTypeEnum ParamType { get; set; } + + [JsonProperty(PropertyName = "strip")] + public StripBusType Strip { get; set; } + + [JsonProperty(PropertyName = "stripNum")] + public int StripNum { get; set; } + + [JsonProperty(PropertyName = "title")] + public string Title { get; set; } + } + + #region Private members + + private const float MIN_VALUE = 0; + private const float MAX_VALUE = 10; + + private readonly PluginSettings settings; + private readonly string[] DEFAULT_IMAGES = new string[] + { + @"images\settingAdjustAction@2x.png" + }; + private string mainImageStr; + private bool didSetNotConnected = false; + private bool dialWasRotated = false; + + #endregion + + #region Public Methods + + public VMSettingAdjustDialAction(SDConnection connection, InitialPayload payload) : base(connection, payload) + { + if (payload.Settings == null || payload.Settings.Count == 0) + { + this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); + } + else + { + this.settings = payload.Settings.ToObject(); + } + PrefetchImages(DEFAULT_IMAGES); + } + + #endregion + + #region PluginBase + + public async override void DialRotate(DialRotatePayload payload) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Rotate"); + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Dial Rotate but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + dialWasRotated = true; + float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float value); + double increment = payload.Ticks * 0.5; + double outputValue = value + increment; + outputValue = Math.Max(MIN_VALUE, outputValue); + outputValue = Math.Min(MAX_VALUE, outputValue); + + VMManager.Instance.SetParam(BuildDeviceName(), (float)outputValue); + } + + public async override void DialPress(DialPressPayload payload) + { + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Dial Pressed but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + if (payload.IsDialPressed) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Pressed"); + dialWasRotated = false; + return; + } + + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Dial Released"); + if (dialWasRotated) + { + return; + } + + // Run only on release and if dial wasn't rotated + DisableSetting(); + } + + public async override void TouchPress(TouchpadPressPayload payload) + { + Logger.Instance.LogMessage(TracingLevel.INFO, $"{this.GetType()} Touch Pressed"); + if (!VMManager.Instance.IsConnected) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"Touch Pressed but VM is not connected!"); + await Connection.ShowAlert(); + return; + } + + DisableSetting(); + } + + public async override void OnTick() + { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetFeedbackAsync("icon", Properties.Plugin.Default.VMNotRunning); + return; + } + else if (didSetNotConnected) + { + didSetNotConnected = false; + await Connection.SetFeedbackAsync("icon", ""); + } + + string deviceName = BuildDeviceName(); + Dictionary dkv = new Dictionary(); + + _= float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float value); + dkv["icon"] = mainImageStr; + dkv["title"] = String.IsNullOrEmpty(settings.Title) ? deviceName : settings.Title; + dkv["value"] = $"{value}"; + dkv["indicator"] = Tools.RangeToPercentage((int)value, (int)MIN_VALUE, (int)MAX_VALUE).ToString(); + await Connection.SetFeedbackAsync(dkv); + } + + + public override void Dispose() + { + Logger.Instance.LogMessage(TracingLevel.INFO, "Destructor called"); + } + + public async override void ReceivedSettings(ReceivedSettingsPayload payload) + { + Tools.AutoPopulateSettings(settings, payload.Settings); + await SaveSettings(); + } + + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + + #endregion + + #region Private Methods + + private string BuildDeviceName() + { + return $"{settings.Strip}[{settings.StripNum}].{settings.ParamType}"; + } + + private Task SaveSettings() + { + return Connection.SetSettingsAsync(JObject.FromObject(settings)); + } + + private void PrefetchImages(string[] defaultImages) + { + if (defaultImages.Length < 1) + { + Logger.Instance.LogMessage(TracingLevel.WARN, $"{this.GetType()} PrefetchImages: Invalid default images list"); + return; + } + + mainImageStr = Tools.ImageToBase64(Image.FromFile(defaultImages[0]), true); + } + + private void DisableSetting() + { + VMManager.Instance.SetParam(BuildDeviceName(), 0); + } + + #endregion + } +} diff --git a/VoiceMeeter/Images/muteEnabled.png b/VoiceMeeter/Images/muteEnabled.png new file mode 100644 index 0000000000000000000000000000000000000000..e5f8371e6b7ba39835f84d9845b50f2aa4015408 GIT binary patch literal 4058 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 literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/settingAdjustAction.png b/VoiceMeeter/Images/settingAdjustAction.png new file mode 100644 index 0000000000000000000000000000000000000000..42db26a8baf0a7c84e8ec01b373841f7a56982b6 GIT binary patch literal 5385 zcmV+k753_hP)o15H(yh%s`5(EV0p&|_UF4bZm4r;O0+D_4mZMC2sTZgHBt@fca zqkhFHb=s;(`8rx3Q*5;&R2~HZd4)$HA>={cxv%8D&e`*=z3$29zXRr(u}(ZrPHKbpiEE-0cw)$%I4w*2-T{A7FZ3?>;;K5BY? zSW>RPsWfIBLB(Zu<+gjqo#C*Nv&KNEuFp+ta}=1*blB047-RGU00%f9g-?m{0MQ@G zW6($3e~PhAXTI26@&e$|*b)w<2bo|%lVfHa=+t^;HISkJx(*EX1Ca<|G6H!{*_Sw= zY5)R5fY&XrbL@c44rp4`Fbd!q0Q^4LrZB*41)PO|s>-&Ju~fh_2n-LAKLPUcWt$$= z+h72KL13VZj3I4gG6DI8fY|~Bh9hZu4Eb~n_GLB>>6McM*d2hDe(UH4qMMCIuxYa_ zU@GAEa74mVxtPGDycU*9)}0?+5KSsU%7j8dS36KS4ydRGib`l4pzSz#>oG7}E>Ktm zgu`*emsSEMGp&5WJPa_XKvxI2(Ev7UE>KbdC@Rp|27c@yEkYO$r!h^BJg17%DwR2$ z2D$SPpUY-v+&!S7yCa-^mq{G7KDmW~)lA4$zVk=}tEtbE+n95?Y8**}krHGKgE4Mc zGGy#XQ6KHHBmuZID)G|qB)Hg>T<#oFoURk+qj_jo(|FXMNV*0L4{NYk3{VZJK2-xxdM<{oEyWwNBlVTVmLl5V|-(4W>S z^`|@OSVw848IiD=U3CLjmfR`Y^w-uvf9~gCt}()aD&Kn?j3P>47}^iM1-@%PlsoT& z^6ABr|2I!Tf8|B6apR>^Ru#ZK09dS0Rz3xmUjPjGp+E37@U~Vc-~TDt)S2Maw&E*5 zBnb7<{{r$H;5}W?SKcihyggSsa1m_{fXxw6=3ZUR7A&kvQKL&=_v|5U^)C#p*q$6L z6a_5L3Ep(TJ4%>OchV!4?dk}ky(P$11C%98>=VBz_U>`W`gcw~k2N(d@wAt}C>Ngvs&_60XPtQ8y9W1$%W#Hu;N#HK&{tRtK zQzqu@2oR81Yc#lV%3Lf8&iK1;0$*GzXRK=g$_!8%rb(XGW8lq4#CaBgju15Qk-xF# z#F28M$b`6!2{?z+i&XU(OWt`#qdZcH?wGE9UV*aFdMr~}gX^!Zg}&-hfc&Nsy^)S6 z8J~_iA^cJDvC$1g_vPl3BLjLm#qnX|CxKmc4NzVwSoED+fQ}PD?F8_)7NDbLLbyAAvJK6#^<1(n zn*n%5@?7QwO3LG((hB(@|22wna7qIS4klKR@7ye}4Mr%JUMaUIImsRG%FJ8W!JLKQ zoBtd)(B;=bx%oC=_Yt7A8T|bnfISDwvWMcfApV|{vJx=jzc^sEgZKEsw{MEdp+LFg zUceKOfn)bh@InWa&n}U3Ih=sUt!WS3b4>sBlZWZxiVpVx_~8NI*x}?)QzOt8-|gAx z)IgAgwdaEG+ybs?U}}6Y{r$fJC);Ech(*2kw?lvB1t{0wEKY&?=r5tyoe#e84S{=< zr7OT1E|3ER1JKw0LT>d)y02nRRrD)2eNF0d-D0^5Uswf3SZdk#!K&+}n0t1Bzx5i} z7<-aWVUsU{de`khzB3sInJl6d!?(T-1jDj)lED|99#SSh)O-Z|uN%QGo*!@DzKMXU zL0NEJ9&dc#i^2XdTt(1-xB_hUe6V5}@|CZC8=y}LMZUKbICcmaKM80(8ut;R9;JyR z#T)lPd+dIw%YP)huD$`FPYP*|u8_{30z*QiW0zhI_0r2Tszp#k`^*gK0!#%Nc}{)A z$f_1An0pY|zA?EkFzhh?asQ?2gFl;LSoQK8Hs^{$kWqYYXkWfoI`B+GdIq6AxD9S{hfekEX}ypsKi zSJqS`nTr9#ejwikW%_Ipa=G8f)O#Pk3{IDIgfA4-BDY?(0e){poNt3cIBIgFh^8di z`yTYSUk48F2gZ(z8X_W&8=Eo%ta2>4&j(Jvjpnr4WYx006_|brlzCSJlcveB_V0xL z)@#6_eLzjURCd@0IE$c6oe{4kWg_ycLq6d;5D7`xk$%KHr)MBU%bVozI!}m6l?hWU z>g&7arLt+?Ub2}V*zFZji%P9sZ4&)P!zm98fss=u#7)A-R*zP*y#?Ie4@SaP)yg(9 zo;Z3;7>Ngm!6-7(y>v7Pe~l@X(MS~OjG|pY4rBwHGE-pWhX`(@mVXlrbo$vk0l*wP zSp6hrrbBb_;S866mutO2Zbg`i4{MB<9ga4fP+aoWXf~FrPN$%1iE`&mSWOxjQ_Nd3lx=b@zBhHyB2 zDYOWO$*e$8(lRJz<&)FMSwl69o+$pkNue{!DHwEX&;0mUHfan-1d>~(%*w;^pG<+P zB$m^hK9P^9tjYU0?H^WE>rjk}f~QtCW$HJ)iK3*2DT}^ZS^Qy)^D(CI{LfUr^VHLs z4Ri~ekks(q6F%QPpCs#0%0J)>q}m}yh6*H?QC@uhx2?ryRlRA^H8ROL%gOEW1%LR& zs(q<;h!LA{%}p7B;;iXY9CbC7wooXn>j`L-{Bk%P;jV&w(;c^bcA}yvBhHwD*y=Se zHFo#(2aQGp)3c%2j{q4AQoaqFcl7o2^@p-ZlpQE;+quV0pKSfAsnlCb)Iot8jHrH{zMW6iXD~Sz5zs{$`Nx}aS@oJKxGVlgC2OP z3=!>%Xj=GB8@IMzId}HR)67lVcAng{ZP#$3h%|ChVF7Y;Z5SBzV8HFkm_D)STQ-%Y zB?yH==pS@LI{9>M#Cup~6UxRE(?Y@P_h%SW=Fy~hNl_s=W%LjBV`w;##2Xtu9Ere@ zXGgKCfPzrGf9AC}uOAKspeQPq-}z;f6c>ttTiZ|KNB^-J7PA>{uMd}BHWN23x>l^1 z$!NstH810pSKh!2s~@kqWX4pd&o|`Rcc>`|Y>{xP!tmrT*WjJaJMg*duEb&*L({}+ zCash~mU6$d@@JSbu@1N2@;M9-r=WbHHiy-0#@>TXc<8aGvGhw{z!mc@MlcjgN`i^c zY&4;#uOE*-`7C<-2XNQzi!t+p$&xo=UNYFWoqMtF)i*Hc_M*IW3?i~ZAfs3)6cOc{ zMb|7qPEOp%uKM*l^z;viRl52gF2#a5vqt*Xzx^)UZ5?=L%Z}zxT|TEdKQ9**Wnt*U9jLDviv^d?%*ZpcVhT_*wi5GZUpOk?#QIt+yKg0?P8yH-m&_QI_s38F z3f;W}m^=GITzuiwj67G)osA`*Ux@3!bPsy_2T|b26-Qkdh>i?x@s92u)KyoG+|)3> z7Qg@F2IM<(QRvK18ZbbmO{)dN!8pL$cd!X=uNUTMIfDESo&T<$J{a>I@cD;Ecz5qV zh@qhXOeUjPc!R2r_`kM}E;JrFCRUS<()7s^O&mRb0v#v2;mmhn*WN~$>^69PLnHF+ zX*?{IijGBcbocf{<2r0PR-rf%$+73)VW><&FcikrNfY46wPS2W86LR%b}U`-F!J;4 zf;Y)YD^Xt*42FbY6Y8qb`|B>uyX<1IQiwWhC5!i5@yHX{xMc?_%Sxm}r{tC1?aZ;j zRa6+Sj&R`4`yavnLq}0wQar*-{dsm9cJ4iZFMRc0v~~8Nrm7UX{9l@H_HyVTvxs@%BzE{`zvdR*PK)N>@)`SdCU9O(p|!ZSi*m4jyg6^-Go^C&wyu z9amj}HBUc=>dI1KtRj~a!+{`jtmfn}Z-~A=w{6!Rxq~Lu0udcfXC4aj9b#2`diygP zhsf_Ag3sfF$LojJ>qq~fN30g@Bw^Ax<`(T9w-+9-Px219g;2Em6d1_8Tg)byjPW<< zNIr|n423CzPYxxOCcpOeTW_q|`r31I-(365yp{JY9pBSGC>*}zjV~(KIRe3u=p#S1 z_3h2ry7PTGhtUA5)e0GjS>_#d=;(3GoqZ9eoL3LKs{pP7`Ltorfy1b)slu3|!i)y0 zEH6QAZ8gfqxS(@#=4OP#QV>1SDKjRvGYLr%hIS=6{!l24U^paTL9Q)F9Ge_#+$UPh zM%Zmu*ljuBF-Q`n0;i*3`CYe-Ap6COuC38@J^19aFCHHr3J7^(M8`fa&yF06S;(;P zhHG%eWzxUWLUncZqO7!7jGts69t{H>Cwp<^*m2a48;gScJWM zB9XL8?io;|sK_aBO`?IU77NawP?t1nTW1$mzwi=Xe0e=;s>+3NkY2QxjUJB=&8_X? z_fKCj4^`!*0y`W&+9IMsHu}L6&j`osa^^>mm@TzY*s50*ZqM<{tYedohZzA z2!l{G>+0=;tH1$+!I-tKpN;%O!#q{C{fCb7XJ32;rDKZVABsm%imK~g{sXSQaxUtt zs}K%{asMMv;Q8l%hnbgL1jZD^DiIm8d6U72{RbuHyY&(gmH1T}9wC1_WdDKW}Zso&!fvQCd90OVMtOs}Q>W!HWUI!C)lSKuyi9 z+JQq&$hF%sG!zH;Ep45+`|f{6MZX?Bmk}@eg%vs)NjRwG1UkEW#Ujx?fnX5z zHB~4nc16PBh{I?!*pWcpz5PQ64j&z~S}ZC>rSY|6MZlp5+IXayo$Ts~_yd8S#hZN-FTy^7NrD5h5KMB@ z+=FhfwqVZ8qUn<-x)Mh_a_qSK)jw_Ma5@}{)nZ{(H=AKh#MldO36Hl#)`L{E{(>Utt(a)$lNJqE^y?f0W@2V)tERR@nw5mD6U&_ z+gbCiXM-W_+lC@3JySYs3>1k((wKspWN( zzxe;5!2dO!?CuS9boF@Z$5q+G;jq5ur9U+N>bI}7pW(Dx)@SqQNRlD?orZIN9w(i| npU3&A33ZOn(K-4A5rF>%>4p0lgaU!a00000NkvXXu0mjf#9&V; literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/settingAdjustAction@2x.png b/VoiceMeeter/Images/settingAdjustAction@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..718f9957a59d0df5cb68bfcb7050bd96294c7094 GIT binary patch literal 10421 zcmZu%bxa+=lSazJ-QC@-Sn-FuLveR4&;mss?(iP&?(Qw_?poZfxI0{bm)s?n+#fqT zlT0$3&Fs$3w_k*+vJ5H`5fT&>6snvoQ2pN=|DS;X_$LpgcYZ-ZQ9#K7#WjCqp83Kj zk`5%@>#Va+wvWiA=MwIRQ%OhwO<|wK0p{j6=H|6aV`iRbhOuWJ1~uQ`cKx#)+j|0H zunAD?F?8*tD1v2S5cex*WT|7MiIkMt!>WZ_$~VWyYBt+3iT2*nE}-Vt7g{Qu)?3S* zdV&$L=@e5rOnd*2QV$BLHogp4R-hY1$bl0JTK4&;pmVB%mVK~siuUpLC#RGx6|}WM zj40Nt!Ydd4ewh~=f;{2~p1O2Um~`p^tOiDveosPCs%pZ5J@gB|=LeYW8i&XQ3*YWV zSLnpLyp#ax0VWB=r+@kUTTw#T^qKx9#;tSXL+2C=Kgqs46&nd`eoB=Ey!;VXI!>iW z+J|jXk&^Nl^w~g7Md6uI-BMo;w8YgbE3afU!8bJc*;&R%Xp`TgRaw8I-bPuD6O!fp z_g@GT6D0H8988y(oc}g$whZ1ks5uCSfkyu=2-?UQ*13f7t};~gcl7Aq#W@gwy#@X{ ztN{yx_m-5nhQAvnr{+}V0$dQnfqR>{aJ7G+wB(9epQH2cad#hzd-++Cesjl{!{lLb zOw$X|*df?pc5i<&psf#z#wa<0sqMl?c!rir8W(e%>BdhPc_^}r5{$DCuqJRpS2$o~ zgv$(Z>&}JlgAJI1=O+AO1aKe3xAu}aRmnwiF#9ngEqnm+BgGe1+w{G88mX&TrV^|7t)EeHLSx1 z%m~va<~eD=vYNzrF$VXo6Ldlx&8%i@M8S!U$5S(kC20zz@!E;sMp*je`=K(etb)}oW7K|C6+Z<5Xxj4bwglN*8S1qegJ?nh}>c^iQ zLduMP`B#s8!Dm2Sh9|0HKn^rm4%f=>7Q!!8b^CY4)9?}Jdq(X&((RKA5h83 zWbpb$Hwo8k8sqF!CA5?r!>Rg$*mb}49X$>9`?iG7^U}mh)RgD`GbnW8$NS_7EV#zs ziuuB3YC*@ujH>Sh%@*C?Kj*#zUO6^obnVN`r!WmuWOha?TFcMp9`-o$sv_?6x_$G% zRv(w=rpaE|O=^upiSw^g#KeMG|5#oGxgGOfO+9vOP1V3|*1<8be0nvKzY4xCd+9Ex z8`xZ(K|32U9=^{~UMI{LwVx<#;Uj>u8V{;JkTk(vxN^+00X3hucf$MjrOq2pE!ie(1ag1{KRt+70{Qr zcK6$>b~)xpLELr2S;xxY-DjSQ!&-saAvBq?gpc}V0vPBR@-^Y$jp|Gfa?n z!IloeZbm$yD|;OA&z@NtKt2?mXg&I$7c^CEP9pM?vricvP`}QT_nIHtVb8k!;W7o; zcC~Q0(?w)LDX_ZZNMZc{;&XkH5MRQ1hz=a%|C2%7lVOE)5Cd0H3flF#_Qa7x zcRd3*Jb1c?kqk_blOydB9%*}S;dtWRt6aU^5=vBH==CjbNxb*E3Ka*Fb~aH&rfW*L z8J2}W$C!5;PK*m!rY&-aa*8%Q!ht*Xz?^mP8u!~eS3pI1TAwCQDif-itQEGV=1ejf zu6Bmnf`pVfX6=un=<>NjY%GQ|WatV*KkG5dTpb;+#JZA@{UI@6z!UJEX zss?wL90%K}e>_Q~=wY>DkoG@I(@?AZ* z9m__P@&n@%nzyO@bwF;y)>>Iy$WQkGn$ys^4nI)IPBwc#@nFVR+CQRaSu)zb3H0C@ zBtzvo3n^rvQvoX~6GBOg>A@Fy6UkA9i*(6MaRhLN5}3Q;7rd8G3Xk^7GY{ens{s8N ztID(Mr#(^H?`*ih`?xsfV8y(U{cEkIPtBK*z2^H1C}pw)c?WmGJmn%_L;oHt zBz_R-)ZM+q;+|#XyD~(E#T3vM?4IlH#X}JLZc#T`F=gqzoWRRG_Oei759jA+AL12g=I{(wr3tBkCYg3<~fEhy&;+nj7c zWXa=aVOy3uNnT-B)M2Gh+=VJX#!?fLt2=le?7=K@*2m$*ea&Rv=yKTgx^Do#M&O*h z*i<}i7Jbcuv4J=k1?Y_-+77SV%yiE6B&(}O`=~0);mG!<5>8GD<3=sAMD=pd*o>{% zyF?Ix*k9CdQkK1ddzwjS5t>mjaxT3RWo3LFZem7;0>rosQ`@R1@=E`n6LP<{>vj#f zZMNWK(yaJNuGdD@B9!n#Ei_mn2tQ#|FDR&7^lLbvm2oQ)PTmog;K>8)-`x(kv5bb( z?{HTfOCi--qd$qKA6+EYy3n|+ju;(J<7R&AP3A9A$$T!?~>2ht%5RdqSHl+3@FMFM)7Ux{UoL8 z1jGxTN9klTh)AP*ax%TKd23wwt5!KUqK5w6C$xK=kxIy7B|(FI^|bf_Vkr-+a*p&6 zFZ;fHPyyfWB^&N%@tC zrH)>cr~^Az^C?3B{qM?Z?$2{#)y^NHgt%2;_;(L_(Z-S#$j0_f==7!#3ai9B_hYw-BBadnnc_VhY)Bhu}}N*VU5U-|p%* z!kwAw3i?NxQ2qG~nKs`5T`%!X3Si;ootDdJ z*8B^Hp8~-#0UQ1gfA+`sEV+~s<_U%!5@e$GEPV?MwIA;0RqSj<5Ij)xotAHywW3cs z(tbKIXdng3*YLV^fa|XeX1-yPdccl4&XcjL2bSU58DR4YZ|mj=i(%}$bj@m3BR9{o z2WHX5>;tsMq1Y+O!mUe4uEOTn!4*;*{{cI+y^XxzMDr5VEWM-WKwbj{~SM_&Q`s~OKUfJu?a;5?l@LW?)5oa?oH!7E>=;(CDAHvg5xs7tR!TG zg(>9v>7^&x82T4u`wXMG?H+cXzlP$dFoNfmrE2G;BL`9ijr#fuil0kLqWWIZXC_c; zq67_yXOv$yp#Cor!-Joc{FfC{p=svh>))ViG?QHT)6lp`%~YCrp2A0}^_+>wsPp8a zdpUaU>jIx+89%<5!vWb{eTxJ@W+fp+?AA05$a=Unf0anS=Tf*R9Gs$$OFH@BfbA_o zm8f?Eauy9A4%&p>ymnI*hsDk13c{}mbi2^$DV|hx65UGaF{-QSqTqukTUS_3Rv`_$ zKQ&jyV^b+U3)P2a{dCrIG2*xxS-tc-5+s!()^VsXV$0Ew?d$|2>Y=!if?6mx=gT9& zkW3#bXK`IV#1%zH$PkzM=aDPEQ#dV01aV^n@R|1aH)*jE^Ou$rk!+q(=C_#SW*1$$ zk_kNo%bwZ2m%k-Q%%REHF>W5-K0q``y+x5lD^Dk>~6 zt$js&JQXKPoxev2Etk&cIm91=8fpLcY(C#;#*aYdzS%>BtO zSf_R+E{$eXp7|1skeQCQW-%XE(&=SNLlX1uM=lIkt`OePu-vl^q^3^Js)9(L(4Oy( zNoI*wZmrAYN*?^nFlWb`0bs^#J1fB~69sKCTENW-u0ZAS98RM~)il{_HOeAnL>R9N zfs^DZX|kF6I>EX3dIG+XGakw<8wvhzk`p5-HwvD=SBGpR_H$;wm9^dI9~#kBf?Tk7 ztyfTXFV|-P(I$+>{6}K7;2OHaDhuwMdv2P-hOrVg+-lL<{jvA{eV6UO(1R)pX7UwM zXxD>x)7d}xxlw6)8Dn^0Z{in9gl`SEwwwr$CbX+*&mP=tW56^kuhu}aXCtda%6$P# zGqQA=I8l~LZZ&U97M^_=@d}VU6CuZB$l#rOgb4bN8|dv!^AN^l^lA|Kib9sH+QrAR zL7k%@k#R2V<#aaLndrG_e;oPD9DJI==y0#k&f#>WCu8%_R_LMiO0F6g?!=dJOB$@% zYl2c6Gs2m-@dB#O+@A-`WjDd~Gj|34fZ=lLDIQi%ufeiD(#yhbm&~16p z+uKTB3JfTYORIQyJ=Scd%r(_9$jY43`en=ur&Tz9i_2HY_I6`A(inF4kiQU8iM|pw zCI<~7AV20hZUhWJbRj7#Dvf?ADjVcdxqUe%O{1}|&P^p{O{#AkPfaKIC_wvl3$I3h zW9^5m+@p$hrRJ(g8=~AO_S^lAM_Y*2cW+kDM(g}(wsKhKg3QBlu3Jk5OM%*dhw_ro z2&V1W(-s9zukmC$C+qxgjOe>f=Zw^t1@fM&3qsV->fv;s+VL)aFs z=T9nj(6Hn}OenCPW{o>0&@ViOTB_ve@|r`4yAc(u;WIj93)4oC%oHDG!ZD|Riru46 zI7HF5IZOTM7H&numlS%-ViAmDy{X5K9iB!w`$}2leT7t8*5K}soU$7B6>XG7x$YAl zL6pkOL*(T!KW+Vt5ZoN`IJ3$iLG#@nj67lZx;%_9uDf~%F0JKqMcwKt6O;XprDtPr zsw}bAUK{{q0AF&~ZDnSR5hTSNOIW^1u+Cu;rBXvWA~|~fh4F2iQpwJ&QFyic!70k# zD4Z2%h}uO@B9{Y39s{fOi+YF7C^XyU`zk zzabIcz;2(Tqd)^w?Q%Xaz9N7Nj6aDEYa7~P4|#Xn9{oUuIt&AqW_E!as*jRYFG=8o z759QNlWdwzY%%qH%~31T_k!`Zr`_f;A}B|hNmW^w({!}b+=e!lhwjL)`_4I1R97kSE6%9B{#Es4p5zu<+%2Q|~{YZ(I%s6aRW_NL)B`jH?^BJcNX^0cY!7P7U_htr~`V|8i ztN}i2v0SYN9e#EYG>v@>i6Zqu^L*c_D_M=clsr|b%5a?_mG=JU@djH&E0rqh;~n|T zK({gW=l6x`Qp&~0$h#PYb&Y>KtMNA^zqy$S$Fk8{K3o)3bX0OSw>b=imVureT524p_o2p{tqneX#e8le7E&_jj8#Gp zq(u3~B0-HN_b5w!qK{S~1Nl}K6iRcCl5y{OmM4=PL22jQuJ0~JR=k|Cd71`;CTbIX z=X6|?$gsz22}zu(Usz@=auHE9YB>HUA(pVL*c9*)CNt11uBSslH@#xMut9m@AOmZN zrgqf8UL#_3qhIcLsJhhyC9xyLKmo~m$6HUH>G$0HN*;66L=y&_<1=y;BCz%sIB~*b zj_^O8Lkf0uv+4eAWkcpHXT^Vq6lKT6wq1bEEMNq*L7Y-M?`_;go=fn0F3W)&S_q?y zPdm7!`QpsL=PZfv8@%U*=xoQgi#(n~#OUS`pjBohFiMnU=9at8q~AV%o(uT57AA>F zPgRhQ6iL$OWym024lOpnl!`VDpgYGCEc5p^+O+#lM2Q4FKVfAe!iY((tO8#;jXeT% z)Mw*y_2UfzaL0r{)BKBO+_WWcSc}03FGEA+LnY|o?g`@(-a}@Rff5`Z3R6N#D<$IkmYLU?nz?v{dh_r5Xw!CrIaCbgbIx7Gfg2KB%n$M(6t@Oa&@3J|q!b5iW%v_0A@%Z5o9u=ZCq!A``>RD;f zjM2oaWk$hcn64VeWWw)#jpKPC#6l2ggoo=x({$>|e4yyupP4pi>Ghdqe#k8aZLPOUD*wKL% z6IQSSi<`^7P^?uYpMv<5%K8S75A$lL390X50{?A$fI0n2o%xjCBaAqsBLPQ|6Bb*# z5VlkZ9Kt7VYrif%pkzIp_)YOs zYj2{&#(kkm&iO#sZHMnFj|m@_DsBGHXEit-7uUF&SkWQW;Jo_TogN|4YiRQzvm`0x z_p_3?t>G(dRo*0bdd=jN9oQwKkftT0Bw!dOM%B9S+v9@_*^p4%Ll76{`TJv)O=S*C zq4c6&7HE-N_?01+L?~k89opcx8{_-AMoV6CxtqlZClZ~6Fw@3LCw!RxS5E#@a1uBz z5bm4R*0-EaQmy@cv*U?Rbrv&fjYi~ORXlQ^beTQRgn|!^)}lc@rim$dC0PL@g7G2^ zm7go#yO1go^?#-RwO2cJGqw4{&Gd03X5}yDehZ0`R@tljx@&CORQjy;^=Bd5->rE5 zYw1d@WQT#A+3w@UAaO$56r-JJfj4?Enxx~3V;~r4OCg2%fvR>tycHp0yOj$Y;qeys z%qM@S+tty191voMdfZgYpLJl-6x~Q%Bt#q;WuVLBT>R+!IQUS|T29jSxGs2$iW1?f zL>n>7cChW@T_-~>SXD>nnSq<OoU1Lz%{lHQAL~2+?(tWyyKU zl95o9u|+o^Ppd<7p~^?9F`=qPj7T%~DKCY=XKw2?Rgz!l41B)nY!d~Y)ELpaZ_5%P zCMJWp*r3b`$GCC4cBtB7bUrjNV2C_PawmQg^89n@R?b zei|8zDMprdVo$5U$#;rA3E!RrpXX-MW$Blab{#g!eJ9BgieDECJt=J?r=O_3iNU3~ zseqi;w;(E!tgFf zHWjs}vuA;^UxRr*zo55-r^%X=Dstbn`-vK@*OLw@H|OQ%QWVJV)hKxIVN;xF>Hb1O2}?^#&_ON)9<9M?+~(yR3b4KIiDr$h^(1p104h%3 z;s#=^eM^avF}QhXiyigDISK*Eyv8glQcDbo>@OUy_AtTN%JFxQDJ80IXpm2tAq1~c zaSUyrz|99}Ei)zr@tYUrS1ikun@TW})Sl^%CsvlRvB=GvOP8yQ@lD3-jI39$Sjey6 z0QIOc;KaTF-TtWSd$tJ|e8cC;(!FJT$_zKd_g^z0d$vh1*_i2gv#b$c;XI{YlvkTsGnlR_Vg!( zCx3Ty!kWYC>+guNX0ww(5s&SOTXQ!YSSSCuoG%XjTIc!iKtJ<2FK=937WngHqJ-=a zjs!e8w|rGq-@`(3t;>IXiI60Gg0M(N5h1 zp@jsR70JQ%q09?$gg0arSX!{)Y5h=(_!jZo04}67ysldorKRb6_wLQ%;OF@?xvQ_KRi6H)>RADI%2-D4V69wsSAh;#79o73V5 z3p*>Dal>={@^ST4`l7Di=KA;X_YD8nMd+_aQBg<(kW5r)Uqos|ARb}TcZgOf@9izB znY0G1Hmh-FWBY7Vb|-XRE&Y9(=|5zz?}$d2fq0{W%_#?NLV}Yfp1sNGxmGAdg z4m3hQZ{nfBr>K(8i@Spl#vuEecAn$k_puEUQ`ES$n4yH>?IndaOYz@8@ge(8q(+nH$U2sPS1%GxTE;rnll!+LhaeeBj#u%QZH%Jcu=QAx)8hk@8NEL zcewnX&B+#)big&KG`TS3gC+P zIbGFL^?qhAD<<2(A?0faiir0kjgz{IEj3e?!mrn;hqUoiVT(Px^fKsOMS6P z*9YzMZiEN%WiPXHJC`%N&*z#7qiE`P=8>Lf|4jlG+v+N-9PtIZQ*tgM~JRSm(7{De5j8iPW-d#0iS8sq)c>yu@r|CQFfrcR;K z9ZpH8y@4R-2;%6bAtB9X)?4QxUk3;^V*_I`gG zFRZ2Egnaemvfz!|=g%{IIOyY_ol&^s`CVs@aX|gltn|SVN;Y<-1Cg}Is#->ZZtQx- zeSKt!NCRU>#5>~E_p-rm@wmq=vWJy(MFkMLa&lz1WUvSBOz(8B-m4guXOZ&Wg2JtW z=GUuW3`wTOtmAHSOmRjPNO5yy^oQ`%mWiDxlM5n0!^MtWPw9Cv(@Bsw`-q)gK@?3>i&+LcBTGU zdF)IT8mfz@ewltK+g710n1;ICric1{1=akaf56yYZx~S07K}VsJWt=`v#wRaQB0(vM+lp`K%4SM$cDIhU%%y?{D0Xt;4toeu969Ln-DU^MX!dQnJY9M`!!2MK&_> zfK*5`Zw0&lkH79R>QqZ-d^J7!PYqI^sIqE5kBZ&v%@G}@N@21YC{~x{o3k*Pi^kvf z@aoh^7&*e6Eb+v zO!UKrJs2N5o&(kM0zfmf5Sn<`vqdFW4 zmLLSObIzpQrOpzX$;yQZN4J@rr-*Ww^(uP3)@VQ)&2L$F45Va`5n~2N$pXYl2TYp6O`ZU z1nxdSHr=6RC%rnLAbtXJjEv##zGV>uGdT7O-7GE6Sl7>OZUt)|-o4Im)BVArLnp|7 z?-`q$o!>U|Oh?P8I>Isw_V$Z+2mR+7w6#^HJc^w zZ@ba++U;aCEKknn?%&-^WJ};W)>{{#zrVH5tSB%4*Wn39|5AL04|^5yr^shQimEy0 zPPY-2(CU3;i0nNo1og32!HW7F%^cjvPIeFffxoSbD?5=?v}jny#y(FVWQhDz)ev50 zFDsfcQI3Phk1MZ3I7J52eVgrT2%pNHtQ%11`A($JWBDc>{h zq>6l0DAob%05@qd; zLW=aNX)VFpsA=fZ&s1j(_0W|2>qf@9+>f>F7~!hbkf9UQ!?}l%Db6(5q=3waLE`1{ z6x3@x>W3_1>I;J=q3jE=Ax%z8shc?0b>@JXY1)Q;HdJ(M!%_2Y8T!3M@NK`lTrp&OPB=y7aV9K4*&oF literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/settingAdjustIcon.png b/VoiceMeeter/Images/settingAdjustIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..07dd42900dbaa273d5659ee8e07e41c7b24b4f42 GIT binary patch literal 346 zcmV-g0j2(lP)DmZg>P8>Ib6}pVQY;Towe0c$OhX4^ zNt?1S*T5C^kT8{o|I_j?Dbdi|`!G;O-K6^`X}Hs^uc!#waEa6oi` s8jw?G3)4e)%8S6EbXE8>FfRb$2RqmV?Hp%lsQ>@~07*qoM6N<$g3JS!lK=n! literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/settingAdjustIcon@2x.png b/VoiceMeeter/Images/settingAdjustIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..755cbe876b41b10794cb691dac00c58a58ac71f5 GIT binary patch literal 629 zcmV-*0*d{KP)XW8Vr3UB>`aluB8?DISXo%5iKT`90|6nyUIekRNh4TD zNQ6`pqJm41phw{V9)JG4=p(8#oPY zO4I|0lQrATT=sfq=7)e);12Md)ERsVTnZrSPw>(?ca~I$XIn&^;7MYn-{K2EN1*Tu zwt+t2fzICoV@8XN4PYT=nvrw%L+vHtd=@EYn&~!nUmiJzz#4E*!+R@_9IMpjqS+8v zBYC^frZ<2Oz-yqh1Wmh8MBsJ3o&wI5q-zI71lEk5*&2mJCAIcd;D{zvuVcX6p8(zj zJdd*OcisK<4#(v=- z@EMp+)+>^zrOO#sTYlME87^!lQQLZXCYYiAbc8c!y=>=|6t&(2oBigT`BAZ&=GbDjZ?0$YJZN{HJ`b&{I@-rScE{{%8U zSeD~X0`Jsb-m6QG`4&54w>RL`5j)Ln=8;47b&VjoZydM?+>fyTzXaeH3>uI>?`E@a P00000NkvXXu0mjf_7NNP literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/volumeAction.png b/VoiceMeeter/Images/volumeAction.png new file mode 100644 index 0000000000000000000000000000000000000000..40ccae419bb13d6fc192ccce93e66b209caa8a93 GIT binary patch literal 2960 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 literal 0 HcmV?d00001 diff --git a/VoiceMeeter/Images/volumeIcon.png b/VoiceMeeter/Images/volumeIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..d2cdc28a3fd1149d424efc29c0e2810ea633beed GIT binary patch literal 479 zcmV<50U-W~P)m_b!L8LmhaCM23dDw-#D75@EN=fni;XS8&1gwl ztdyM8S`&TZh(I3{>jt!UtjEraY=W9`C?2N zYE>0Wx+CcgaI=bxDndzz>NJc@)14-Qlu`U2d&7g3e z>yYqwsybr(o$cQAzX$r$c5BH;sahF+#fU^%*}vNv(~va8@fdB^Y(KL-Z2Ok&8@6L~ zUY8K|N!qqO1-u2GW&#_wr&IOS=;2PT6WETK`XXtr;O@!&CC9I1&Gw36vls_mZD^Wkqt6}3^EGRLK7h~tOij*1kqJ-6cG_PlO_ob97uztNm29=$xGLY zprnU32Pg}NCj=Aef-D2Pz32Szxqm13gTq;S?X~y$_F8AJz0P3@ zQ#j91di>zv;3x}8wQOvk)y{TFU&_Emz>B~W!1rSc=s;>Q*n)=R}T&VU6Pvz+zspno+~@rdhkNvDPTjJK@GyYO41u4&jn@zCur%xG;pt^ z#^e&Q8giEcw*$k#=FokS2}xQ6+?tKE2QLTaWTOiHOns7e083je2+kzmw9_5$)>=tV z7tc>g{YT(s;E~Wj2K)kiJuM~~lC%#vSaThgw7)I6!gH%7jR0Gs{TA?dJoh?adDwqj zEb0Gk>hsE~M$+sQ{%C&OSAr3`CWmlNZ*!_0lkUM(8y2cjxrTGzG*?zTx~vD! zLP26y5z_r}zZjSi@{yE~LtapwXR_1Gyi{T5hVAVku1Um0J|OA$AbfSd38vF$er`xQ z65?eA>D)I{(%eFSsviW`7xEDBO~}g%{kGUl3!@+*&f(X{@1X66W2MLAP%n{m>1=!k zSlp7}I@=!sThnJj=x&g7OJOsdZ42EGlD?wYBqMc6vVz-y4}lw-FpDZCE%td}XK{=@ zUYPBQQY+!wthAl-;b?+y6XK(`SK1zII-uCSzoke-+SvyD6hvN){)Lk6Du$+t=hwCb ziEyv6y}p6%|KUC0D%)#;-La5O+1B=_z$dxgYaZMl-@ZdVj!)s9IYa3Z^S zr|spo4~%8h@NdGCwy(GSEbx9;1QR|77UfWlMX~|Wv`HTYe(sVWQ6}AK`|S?qItN`Q z>U(YHue2HC*fvJj#lj}r;M1& literal 0 HcmV?d00001 diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html new file mode 100644 index 0000000..3c2ee13 --- /dev/null +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html @@ -0,0 +1,55 @@ + + + + + + + + Gain Adjust + + + + +
+
+ For feedback/suggestions contact me at https://BarRaider.com +
+ +
+
Strip Num
+ +
+
+
Setting
+ +
+
+
Title (optional)
+ +
+
+ + diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html new file mode 100644 index 0000000..69b1c47 --- /dev/null +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html @@ -0,0 +1,43 @@ + + + + + + + + Gain Adjust + + + + +
+
+ For feedback/suggestions contact me at https://BarRaider.com +
+
+
Strip/Bus
+ +
+
+
Strip/Bus Num
+ +
+
+
Title (optional)
+ +
+
+ + diff --git a/VoiceMeeter/VoiceMeeter.csproj b/VoiceMeeter/VoiceMeeter.csproj index 3553658..1e167c8 100644 --- a/VoiceMeeter/VoiceMeeter.csproj +++ b/VoiceMeeter/VoiceMeeter.csproj @@ -36,14 +36,9 @@ - - - - - - - + + @@ -160,6 +155,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -169,6 +167,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -177,4 +178,58 @@ + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + +
\ No newline at end of file diff --git a/VoiceMeeter/gainAdjustDialLayout.json b/VoiceMeeter/gainAdjustDialLayout.json new file mode 100644 index 0000000..40fffd8 --- /dev/null +++ b/VoiceMeeter/gainAdjustDialLayout.json @@ -0,0 +1,40 @@ +{ + "id": "inputVolumeDial", + "items": [ + { + "key": "title", + "type": "text", + "rect": [ 16, 10, 136, 24 ], + "font": { + "size": 16, + "weight": 600 + }, + "alignment": "left" + }, + { + "key": "icon", + "type": "pixmap", + "rect": [ 16, 40, 48, 48 ] + }, + { + "key": "value", + "type": "text", + "rect": [ 76, 40, 108, 32 ], + "font": { + "size": 24, + "weight": 600 + }, + "alignment": "right" + }, + { + "key": "indicator", + "type": "gbar", + "rect": [ 76, 74, 108, 20 ], + "value": 0, + "subtype": 4, + "bar_h": 12, + "border_w": 0, + "bar_bg_c": "0:#427018,0.83:#427018,0.84:#702735,1:#702735" + } + ] +} \ No newline at end of file diff --git a/VoiceMeeter/manifest.json b/VoiceMeeter/manifest.json index c9ee02a..c24eb11 100644 --- a/VoiceMeeter/manifest.json +++ b/VoiceMeeter/manifest.json @@ -12,6 +12,7 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "Modify Setting - Allows you to easily modify various VoiceMeeter settings.", "UUID": "com.barraider.vmmodify", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/Modify.html" @@ -28,6 +29,7 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "Mute/Unmute - See a live indication of the current status on Stream Deck (never forget your microphone on again!).", "UUID": "com.barraider.vmmicrophone", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/Microphone.html" @@ -44,6 +46,7 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "Advanced Press/Long-Press - Directly access any setting through text. Supports both Press and Long-Press (allows you to change between two preset list of settings).", "UUID": "com.barraider.vmadvanced", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/AdvancedKeypress.html" @@ -60,6 +63,7 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "Advanced Toggle - Focused on toggling between two modes (versus click and long click in the VoiceMeeter Advanced Click/Long-Click).", "UUID": "com.barraider.vmadvancedtoggle", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/AdvancedToggle.html" @@ -76,6 +80,7 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "Advanced PTT - Enable settings until you release the button", "UUID": "com.barraider.vmadvancedptt", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/AdvancedPTT.html" @@ -92,17 +97,68 @@ ], "SupportedInMultiActions": true, "DisableCaching": true, + "UserTitleEnabled": false, "Tooltip": "MacroButton Toggle - Enable/Disable a VoiceMeeter MacroButton", "UUID": "com.barraider.vmmacrotoggle", "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/MacroToggle.html" + }, + { + "Icon": "Images/volumeIcon", + "Name": "Gain Adjust", + "States": [ + { + "Image": "Images/volumeAction", + "TitleAlignment": "middle", + "FontSize": "13" + } + ], + "Controllers": [ "Encoder" ], + "Encoder": { + "layout": "gainAdjustDialLayout.json", + "TriggerDescription": { + "Rotate": "Increase/Decrease", + "Push": "Toggle mute" + } + }, + "SupportedInMultiActions": false, + "DisableCaching": true, + "UserTitleEnabled": false, + "Tooltip": "Adjusts the gain of a Strip/Bus", + "UUID": "com.barraider.gainadjust", + "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/GainAdjustDial.html" + }, + { + "Icon": "Images/settingAdjustIcon", + "Name": "Setting Adjust", + "States": [ + { + "Image": "Images/settingAdjustAction", + "TitleAlignment": "middle", + "FontSize": "13" + } + ], + "Controllers": [ "Encoder" ], + "Encoder": { + "layout": "settingAdjustDialLayout.json", + "TriggerDescription": { + "Rotate": "Increase/Decrease", + "Push": "Disable" + } + }, + "SupportedInMultiActions": false, + "DisableCaching": true, + "UserTitleEnabled": false, + "Tooltip": "Adjusts one of the knobs of a Strip/Bus", + "UUID": "com.barraider.settingadjust", + "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/AdjustSettingDial.html" } ], "Author": "BarRaider", - "Description": "VoiceMeeter integration and live feedback. Five different actions all supporting a variety of customized features. Control VoiceMeeter and get live feedback all on your Stream Deck.\nNEW: Hotkey support and MIDI Support", + "Description": "VoiceMeeter integration and live feedback. Includes Dials, Hotkey and MIDI Support", "Name": "VoiceMeeter Integration", "Icon": "Images/pluginIcon", "URL": "https://BarRaider.com/", - "Version": "2.0.1", + "Version": "2.0.2", "CodePath": "com.barraider.voicemeeter.exe", "Category": "VoiceMeeter [BarRaider]", "CategoryIcon": "Images/categoryIcon", diff --git a/VoiceMeeter/settingAdjustDialLayout.json b/VoiceMeeter/settingAdjustDialLayout.json new file mode 100644 index 0000000..9b0c1f1 --- /dev/null +++ b/VoiceMeeter/settingAdjustDialLayout.json @@ -0,0 +1,38 @@ +{ + "id": "inputVolumeDial", + "items": [ + { + "key": "title", + "type": "text", + "rect": [ 16, 10, 136, 24 ], + "font": { + "size": 16, + "weight": 600 + }, + "alignment": "left" + }, + { + "key": "icon", + "type": "pixmap", + "rect": [ 16, 40, 48, 48 ] + }, + { + "key": "value", + "type": "text", + "rect": [ 76, 40, 108, 32 ], + "font": { + "size": 24, + "weight": 600 + }, + "alignment": "right" + }, + { + "key": "indicator", + "type": "bar", + "rect": [ 76, 74, 108, 20 ], + "value": 0, + "subtype": 4, + "border_w": 0 + } + ] +} \ No newline at end of file From e28a2d63af0dcbc85b3102f21643090877548baf Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:22:52 +0200 Subject: [PATCH 08/14] Remove debugging clause --- VoiceMeeter/VoiceMeeterWrapper/VmClient.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs index 00f7794..1a15d17 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs @@ -34,7 +34,6 @@ private string GetVoicemeeterDir() } public VmClient() { - System.Threading.Thread.Sleep(15000); //Find Voicemeeter dir. var vmDir = GetVoicemeeterDir(); if (vmDir != null) From 95f1bff866f0559d7b41156b7fb522949db80f50 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:23:18 +0200 Subject: [PATCH 09/14] Cleanup and support Titles --- VoiceMeeter/Actions/VMMacroToggle.cs | 34 ++++++----------------- VoiceMeeter/Actions/VMMicrophoneAction.cs | 13 +++++---- VoiceMeeter/Actions/VMModifyAction.cs | 25 +++++++++++------ 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/VoiceMeeter/Actions/VMMacroToggle.cs b/VoiceMeeter/Actions/VMMacroToggle.cs index 8479917..9b61e02 100644 --- a/VoiceMeeter/Actions/VMMacroToggle.cs +++ b/VoiceMeeter/Actions/VMMacroToggle.cs @@ -32,7 +32,7 @@ namespace VoiceMeeter // 17 Bits: TwilightLinkable //--------------------------------------------------- [PluginActionId("com.barraider.vmmacrotoggle")] - class VMMacroToggle : PluginBase + class VMMacroToggle : KeypadBase { private enum MacroToggleMode { @@ -89,8 +89,8 @@ public static PluginSettings CreateDefaultSettings() private int buttonId = DEFAULT_BUTTON_ID; private bool isButtonEnabled = false; private bool startingToggleMode = false; - private Image enabledImage = null; - private Image disabledImage = null; + private string enabledImageStr; + private string disabledImageStr; private bool didSetNotConnected = false; #endregion @@ -185,19 +185,15 @@ public async override void OnTick() // Set the image if (isButtonEnabled) { - await Connection.SetImageAsync(enabledImage); + await Connection.SetImageAsync(enabledImageStr); } else { - await Connection.SetImageAsync(disabledImage); + await Connection.SetImageAsync(disabledImageStr); } // Set the title - string prefix = String.Empty; - if (!String.IsNullOrEmpty(settings.TitlePrefix)) - { - prefix = settings.TitlePrefix.Replace(@"\n", "\n"); - } + string titlePrefix = settings.TitlePrefix?.Replace(@"\n", "\n"); string value = isButtonEnabled ? "1" : "0"; if (!String.IsNullOrEmpty(settings.EnabledText) && !String.IsNullOrEmpty(value) && value == Constants.ENABLED_VALUE) @@ -209,7 +205,7 @@ public async override void OnTick() value = settings.DisabledText.Replace(@"\n", "\n"); ; } - await Connection.SetTitleAsync($"{prefix}{value}"); + await Connection.SetTitleAsync($"{titlePrefix}{value}"); } @@ -253,20 +249,8 @@ private void PrefetchImages() { try { - if (enabledImage != null) - { - enabledImage.Dispose(); - enabledImage = null; - } - - if (disabledImage != null) - { - disabledImage.Dispose(); - disabledImage = null; - } - - enabledImage = Image.FromFile(TryGetCustomFile(settings.EnabledImage, DEFAULT_BUTTON_IMAGES[1])); - disabledImage = Image.FromFile(TryGetCustomFile(settings.DisabledImage, DEFAULT_BUTTON_IMAGES[0])); + enabledImageStr = Image.FromFile(TryGetCustomFile(settings.EnabledImage, DEFAULT_BUTTON_IMAGES[1])).ToBase64(true); + disabledImageStr = Image.FromFile(TryGetCustomFile(settings.DisabledImage, DEFAULT_BUTTON_IMAGES[0])).ToBase64(true); } catch (Exception ex) { diff --git a/VoiceMeeter/Actions/VMMicrophoneAction.cs b/VoiceMeeter/Actions/VMMicrophoneAction.cs index 9ffc01f..752eec6 100644 --- a/VoiceMeeter/Actions/VMMicrophoneAction.cs +++ b/VoiceMeeter/Actions/VMMicrophoneAction.cs @@ -3,11 +3,12 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Threading.Tasks; namespace VoiceMeeter { [PluginActionId("com.barraider.vmmicrophone")] - class VMMicrophoneAction : PluginBase + class VMMicrophoneAction : KeypadBase { private enum MicTypeEnum { @@ -186,11 +187,8 @@ public override void Dispose() public async override void ReceivedSettings(ReceivedSettingsPayload payload) { - // New in StreamDeck-Tools v2.0: Tools.AutoPopulateSettings(settings, payload.Settings); - - // Used to return the correct filename back to the Property Inspector - await Connection.SetSettingsAsync(JObject.FromObject(settings)); + await SaveSettings(); } public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } @@ -243,6 +241,11 @@ private string BuildDeviceName() { return $"{settings.Strip}[{settings.StripNum}].Mute"; } + + private Task SaveSettings() + { + return Connection.SetSettingsAsync(JObject.FromObject(settings)); + } #endregion } } diff --git a/VoiceMeeter/Actions/VMModifyAction.cs b/VoiceMeeter/Actions/VMModifyAction.cs index 34763fd..7fb46b7 100644 --- a/VoiceMeeter/Actions/VMModifyAction.cs +++ b/VoiceMeeter/Actions/VMModifyAction.cs @@ -10,7 +10,7 @@ namespace VoiceMeeter { [PluginActionId("com.barraider.vmmodify")] - class VMModifyAction : PluginBase + class VMModifyAction : KeypadBase { private enum ParamTypeEnum { @@ -162,15 +162,17 @@ public async override void OnTick() LongKeyPressed(); } + // Set the title + string titlePrefix = settings.TitlePrefix?.Replace(@"\n", "\n"); + string value = String.Empty; if (settings.TitleType == TitleTypeEnum.VMLive) { - string prefix = String.Empty; - if (!String.IsNullOrEmpty(settings.TitlePrefix)) - { - prefix = settings.TitlePrefix.Replace(@"\n", "\n"); - } + value = VMManager.Instance.GetParam(BuildDeviceName()); + } - await Connection.SetTitleAsync($"{prefix}{VMManager.Instance.GetParam(BuildDeviceName())}"); + if (!String.IsNullOrEmpty($"{titlePrefix}{value}")) + { + await Connection.SetTitleAsync($"{titlePrefix}{value}"); } } @@ -179,10 +181,10 @@ public override void Dispose() Logger.Instance.LogMessage(TracingLevel.INFO, "Destructor called"); } - public override void ReceivedSettings(ReceivedSettingsPayload payload) + public async override void ReceivedSettings(ReceivedSettingsPayload payload) { - // New in StreamDeck-Tools v2.0: Tools.AutoPopulateSettings(settings, payload.Settings); + await SaveSettings(); } public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } @@ -196,6 +198,11 @@ private string BuildDeviceName() return $"{settings.Strip}[{settings.StripNum}].{settings.ParamType}"; } + private Task SaveSettings() + { + return Connection.SetSettingsAsync(JObject.FromObject(settings)); + } + #endregion } } From c07059cea44f82f4d0e39b24bc60ff7cc503f45e Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:23:25 +0200 Subject: [PATCH 10/14] Fix CSS --- .../VoiceMeeter/AdvancedKeypress.html | 22 ++++++++++--------- .../VoiceMeeter/AdvancedPTT.html | 20 +++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedKeypress.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedKeypress.html index 61ea719..074b782 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedKeypress.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedKeypress.html @@ -18,16 +18,18 @@
For feedback/suggestions contact me at https://BarRaider.com
-
- This plugins allows you to send a list of commands directly to VoiceMeeter (click this text for more info...) -

- Example: "Strip[0].mono=1;Bus[2].Gain=-20;" -

-

- For feedback/suggestions contact me at https://BarRaider.com - -

-
+
+
+ This plugins allows you to send a list of commands directly to VoiceMeeter (click this text for more info...) +

+ Example: "Strip[0].mono=1;Bus[2].Gain=-20;" + +

+

+ More examples at https://docs.BarRaider.com +

+
+
Keypress
diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html index ff6dfe5..92cbf67 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html @@ -17,15 +17,17 @@
For feedback/suggestions contact me at https://BarRaider.com
-
- This plugins allows you to send a list of commands directly to VoiceMeeter (click this text for more info...) -

- Example: "Strip[0].mono=1;Bus[2].Gain=-20;" -

-

- For feedback/suggestions contact me at https://BarRaider.com -

-
+
+
+ This plugins allows you to send a list of commands directly to VoiceMeeter (click this text for more info...) +

+ Example: "Strip[0].mono=1;Bus[2].Gain=-20;" +

+

+ More examples at https://docs.BarRaider.com +

+
+
Keypress
From 494ea044466eeef2ef1fc2798af31cc7bf4f8f5a Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Fri, 23 Dec 2022 00:24:34 +0200 Subject: [PATCH 11/14] Refactor and Cleanup Logical Operators code --- VoiceMeeter/Actions/VMAdvancedToggle.cs | 160 +++++++++++------------- 1 file changed, 72 insertions(+), 88 deletions(-) diff --git a/VoiceMeeter/Actions/VMAdvancedToggle.cs b/VoiceMeeter/Actions/VMAdvancedToggle.cs index 0d7ddfb..0a8fb74 100644 --- a/VoiceMeeter/Actions/VMAdvancedToggle.cs +++ b/VoiceMeeter/Actions/VMAdvancedToggle.cs @@ -100,7 +100,7 @@ public static PluginSettings CreateDefaultSettings() #region Private members private const string LOGICAL_AND = " AND "; private const string LOGICAL_OR = " OR "; - private const string LOGICAL_CUST = "ADV "; + private readonly string[] LOGICAL_COMP_OPERATORS = { "==", "!=" }; private readonly PluginSettings settings; private bool didSetNotConnected = false; @@ -183,7 +183,7 @@ public async override void OnTick() } // Set the image - if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1LogicTrue(false, false) ) + if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1LogicTrue(false, false)) { await Connection.SetImageAsync(Tools.FileToBase64(settings.UserImage1.ToString(), true)); } @@ -263,7 +263,7 @@ private bool IsMode1LogicTrue(bool shouldLog, bool keyPressed) foreach (string clause in clauses) { // If even one of them is false, that's enough to return a false - if (!VMManager.Instance.GetParamBool(clause.Trim())) + if (!EvaluateClause(clause.Trim())) { if (shouldLog) { @@ -280,7 +280,7 @@ private bool IsMode1LogicTrue(bool shouldLog, bool keyPressed) foreach (string clause in clauses) { // If ANY clause is true, that's enough to return a true - if (VMManager.Instance.GetParamBool(clause.Trim())) + if (EvaluateClause(clause.Trim())) { if (shouldLog) { @@ -290,93 +290,10 @@ private bool IsMode1LogicTrue(bool shouldLog, bool keyPressed) } } return false; - } - // - // Only fire for advance toggle... - // - else if(keyPressed && settings.Mode1Param.Contains(LOGICAL_CUST)) - { - // - // Let's figure out which logical operator we are using... - // Then split the string based on that - // - if( settings.Mode1Param.Contains("==") ) - { - string[] separator = { "==" }; - string[] eq_bits = settings.Mode1Param.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); - - // - // Clean up the remote value... - // - string remoteParam = eq_bits[0].Substring(LOGICAL_CUST.Length).Trim(); - - // - // Get the value from VM... - // - string remoteValue = VMManager.Instance.GetParamString(remoteParam); - - // - // Remove quotes from comparison operator... - // - string compVal = RemoveQuotes(eq_bits[1]); - - if (shouldLog) - { - Logger.Instance.LogMessage(TracingLevel.INFO, $"Mode1 returned: {remoteValue} == {compVal}"); - } - - return remoteValue == compVal; - - } - - // - // Let's figure out which logical operator we are using... - // Then split the string based on that - // - if (settings.Mode1Param.Contains("!=")) - { - string[] separator = { "!=" }; - string[] eq_bits = settings.Mode1Param.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); - - // - // Clean up the remote value... - // - string remoteParam = eq_bits[0].Substring(LOGICAL_CUST.Length).Trim(); - - // - // Get the value from VM... - // - string remoteValue = VMManager.Instance.GetParamString(remoteParam); - - // - // Remove quotes from comparison operator... - // - string compVal = RemoveQuotes(eq_bits[1]); - - if (shouldLog) - { - Logger.Instance.LogMessage(TracingLevel.INFO, $"Mode1 returned: {remoteValue} != {compVal}"); - } - - return remoteValue != compVal; - - } - - // - // If you made it here, then there was no matching operator... - // - if (shouldLog) - { - Logger.Instance.LogMessage(TracingLevel.ERROR, $"No matching comparison operator: only supports == and !="); - } - - return true; - - } else // Only one clause { - return VMManager.Instance.GetParamBool(settings.Mode1Param); + return EvaluateClause(settings.Mode1Param); } } @@ -401,6 +318,73 @@ private void InitializeSettings() } } + private bool EvaluateClause(string clause) + { + // TODO: Check if clause starts with $ and if so, split it and compare to clauses + if (LOGICAL_COMP_OPERATORS.Any(op => clause.Contains(op))) + { + return HandleEQClause(clause); + } + return VMManager.Instance.GetParamBool(clause); + } + + private bool EvaluateEQClause(string splitSeperator, string clause) + { + var splitClause = clause.Split(new string[] { splitSeperator }, StringSplitOptions.RemoveEmptyEntries); + + if (splitClause.Length != 2) + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"{this.GetType()} Invalid clause: {clause} with logical operator: {splitSeperator}"); + return false; + } + + string termValue = VMManager.Instance.GetParamString(splitClause[0].Trim()); + string compareValue = RemoveQuotes(splitClause[1]); + + // Normal string comparison + if (termValue == compareValue) + { + return true; + } + + // Numeric comparison + if (double.TryParse(termValue, out double value1) && double.TryParse(compareValue, out double value2) && value1 == value2) + { + return true; + } + + // Substring comparison + if (termValue.Length >= compareValue.Length && termValue.Substring(0,compareValue.Length) == compareValue) + { + return true; + } + + return false; + } + + private bool HandleEQClause(string clause) + { + // Figure out which logical operator we are using... + // Then split the string based on that + + if (clause.Contains("==")) + { + bool result = EvaluateEQClause("==", clause); + return (result == true); + } + else if (clause.Contains("!=")) + { + bool result = EvaluateEQClause("!=", clause); + return (result == false); + } + else + { + Logger.Instance.LogMessage(TracingLevel.ERROR, $"No matching comparison operator: only supports == and !="); + } + + return false; + } + #endregion } } From c4f17781cb6d72ac3d98e0f470c38cc1dd0379a6 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Sun, 22 Jan 2023 16:49:03 +0200 Subject: [PATCH 12/14] Support step sizes --- VoiceMeeter/Actions/VMGainAdjustDialAction.cs | 20 ++++++++++++++++-- .../Actions/VMSettingAdjustDialAction.cs | 21 +++++++++++++++++-- .../VoiceMeeter/AdjustSettingDial.html | 12 +++++++++++ .../VoiceMeeter/GainAdjustDial.html | 12 +++++++++++ VoiceMeeter/manifest.json | 2 +- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/VoiceMeeter/Actions/VMGainAdjustDialAction.cs b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs index 76b44a8..dfe7391 100644 --- a/VoiceMeeter/Actions/VMGainAdjustDialAction.cs +++ b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs @@ -40,6 +40,9 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "stripNum")] public int StripNum { get; set; } + [JsonProperty(PropertyName = "stepSize")] + public string StepSize { get; set; } + [JsonProperty(PropertyName = "title")] public string Title { get; set; } } @@ -49,6 +52,7 @@ public static PluginSettings CreateDefaultSettings() private readonly PluginSettings settings; private const float MIN_DB_VALUE = -60f; private const float MAX_DB_VALUE = 12f; + private const double DEFAULT_STEP_SIZE = 1; private readonly string[] DEFAULT_IMAGES = new string[] { @@ -59,6 +63,7 @@ public static PluginSettings CreateDefaultSettings() private string unmutedImageStr; private bool didSetNotConnected = false; private bool dialWasRotated = false; + private double stepSize = DEFAULT_STEP_SIZE; #endregion @@ -76,6 +81,7 @@ public VMGainAdjustDialAction(SDConnection connection, InitialPayload payload) : this.settings = payload.Settings.ToObject(); } PrefetchImages(DEFAULT_IMAGES); + InitializeSettings(); } #endregion @@ -94,12 +100,12 @@ public async override void DialRotate(DialRotatePayload payload) dialWasRotated = true; float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float volume); - int increment = payload.Ticks; + double increment = payload.Ticks * stepSize; if (payload.IsDialPressed) { increment = 10 * (payload.Ticks > 0 ? 1 : -1); } - double outputVolume = Math.Round(volume + increment); + double outputVolume = volume + increment; outputVolume = Math.Max(MIN_DB_VALUE, outputVolume); outputVolume = Math.Min(MAX_DB_VALUE, outputVolume); @@ -188,6 +194,7 @@ public override void Dispose() public async override void ReceivedSettings(ReceivedSettingsPayload payload) { Tools.AutoPopulateSettings(settings, payload.Settings); + InitializeSettings(); await SaveSettings(); } @@ -230,6 +237,15 @@ private void ToggleMute() VMManager.Instance.SetParam(BuildMuteName(), ((!isMuted) ? 1 : 0)); } + private void InitializeSettings() + { + if (!double.TryParse(settings.StepSize, out stepSize)) + { + stepSize = DEFAULT_STEP_SIZE; + SaveSettings(); + } + } + #endregion } } diff --git a/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs index 03e2f72..7b151cf 100644 --- a/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs +++ b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; @@ -23,7 +24,6 @@ private enum ParamTypeEnum fx2 = 4, gate = 5, reverb = 6, - } private enum StripBusType @@ -41,6 +41,7 @@ public static PluginSettings CreateDefaultSettings() ParamType = ParamTypeEnum.comp, Strip = StripBusType.Strip, StripNum = 0, + StepSize = DEFAULT_STEP_SIZE.ToString(), Title = String.Empty }; @@ -56,6 +57,9 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "stripNum")] public int StripNum { get; set; } + [JsonProperty(PropertyName = "stepSize")] + public string StepSize { get; set; } + [JsonProperty(PropertyName = "title")] public string Title { get; set; } } @@ -64,6 +68,7 @@ public static PluginSettings CreateDefaultSettings() private const float MIN_VALUE = 0; private const float MAX_VALUE = 10; + private const double DEFAULT_STEP_SIZE = 1; private readonly PluginSettings settings; private readonly string[] DEFAULT_IMAGES = new string[] @@ -73,6 +78,7 @@ public static PluginSettings CreateDefaultSettings() private string mainImageStr; private bool didSetNotConnected = false; private bool dialWasRotated = false; + private double stepSize = DEFAULT_STEP_SIZE; #endregion @@ -90,6 +96,7 @@ public VMSettingAdjustDialAction(SDConnection connection, InitialPayload payload this.settings = payload.Settings.ToObject(); } PrefetchImages(DEFAULT_IMAGES); + InitializeSettings(); } #endregion @@ -108,7 +115,7 @@ public async override void DialRotate(DialRotatePayload payload) dialWasRotated = true; float.TryParse(VMManager.Instance.GetParam(BuildDeviceName()), out float value); - double increment = payload.Ticks * 0.5; + double increment = payload.Ticks * stepSize; double outputValue = value + increment; outputValue = Math.Max(MIN_VALUE, outputValue); outputValue = Math.Min(MAX_VALUE, outputValue); @@ -188,6 +195,7 @@ public override void Dispose() public async override void ReceivedSettings(ReceivedSettingsPayload payload) { Tools.AutoPopulateSettings(settings, payload.Settings); + InitializeSettings(); await SaveSettings(); } @@ -223,6 +231,15 @@ private void DisableSetting() VMManager.Instance.SetParam(BuildDeviceName(), 0); } + private void InitializeSettings() + { + if (!double.TryParse(settings.StepSize, out stepSize)) + { + stepSize = DEFAULT_STEP_SIZE; + SaveSettings(); + } + } + #endregion } } diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html index 3c2ee13..144e578 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html @@ -46,6 +46,18 @@
+
+
Step Size
+ +
Title (optional)
diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html index 69b1c47..1ca8771 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html @@ -34,6 +34,18 @@
+
+
Step Size
+ +
Title (optional)
diff --git a/VoiceMeeter/manifest.json b/VoiceMeeter/manifest.json index c24eb11..c92f00d 100644 --- a/VoiceMeeter/manifest.json +++ b/VoiceMeeter/manifest.json @@ -158,7 +158,7 @@ "Name": "VoiceMeeter Integration", "Icon": "Images/pluginIcon", "URL": "https://BarRaider.com/", - "Version": "2.0.2", + "Version": "2.1", "CodePath": "com.barraider.voicemeeter.exe", "Category": "VoiceMeeter [BarRaider]", "CategoryIcon": "Images/categoryIcon", From 5ca5ab8cc3dce3a79611b77d3b004885c56895c7 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Sun, 22 Jan 2023 16:50:26 +0200 Subject: [PATCH 13/14] Updated images --- VoiceMeeter/Images/settingAdjustAction.png | Bin 5385 -> 3103 bytes VoiceMeeter/Images/settingAdjustAction@2x.png | Bin 10421 -> 5200 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/VoiceMeeter/Images/settingAdjustAction.png b/VoiceMeeter/Images/settingAdjustAction.png index 42db26a8baf0a7c84e8ec01b373841f7a56982b6..f719bcb392bc155eaf641a8a6d11116368f264ad 100644 GIT binary patch delta 3099 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 diff --git a/VoiceMeeter/Images/settingAdjustAction@2x.png b/VoiceMeeter/Images/settingAdjustAction@2x.png index 718f9957a59d0df5cb68bfcb7050bd96294c7094..3e4bbf3ab612cf70ec9e7fc7b6cd50cfe0909606 100644 GIT binary patch literal 5200 zcmZ`-Wl$83w`J*&+=V3t>28n(U65G15m7+tkd#om5$W#k5Lj4{?rvB?I;2|~7I5MH z{;%fEy!n5)_k6i==Fa_a=7ei$C=wIW5@KLr5GyOm>->Xp{|bEEfATJDqX`281Xh-R zrRR}#oK4_N|0{j)5pE4jbzelVZK*;n)`|KoM|fH31N&tR_!Et*WiX2Zcqeo;Qe?Qr ziS+XDmGODGVi4IieIB2>ct1-p2!=V>N1f<4{FzQ+d)v6P*Lc-F=X$euYE$HR;rH4& zBi#>ur{3w`dE(*pPKy&n7%H3p?;*$}917U=(b{f*Gf{;ZmtKRrK)-w6T8DQ*Os#4U zmIA)J9~PqR;v{NNH4U=rj@F8VGm5ftt+n%YQ$+_D?{ETh1JK}|d9+U?!>Kd$;fG0K+z_?0UpNpZR z6MTid{aF{V{3$>aFC!LUe2gI?zjcKH&6NOWz93eb#Ll4Y9wot0r0Y3k-t!ses#TD; zRIZHv5#AOd7vqaPGfjM)@V(p7ALF;=CFNuw+y_TVohaq3soQES^mY0Z{-YQzCPXb^+m9E9{fFS$AfWKGOtu!DbW$aE)PApDa&~$LK+PJz%!}X zL;sR$F`G2V_eQ1W(quMpPCp3#o&ZA zT&OShmUGa_0xloqZQHM%4}@!v{384f+5;;@pRfPQgdO00R<7Hf?&fc$ih1-QR8onl z$oPl%X1w(}T_Rq`xh2GFN@?9Kkc|yzLO=s{Rczf;y9KF%?8?uH`3cV#6#phkC8OB$ z80h*b+*P&;{oU*t-r_~j3&nJi?BXE_e)BYiy{ra5O$&cUe+2pUkNrXA0vn>OI{0Ls zSA9GRGDU@!WP0ki#e|xj?DF=`%f8Swah;&bK5DWS@+tQ2Zgfa+56Pf=w4>NxA;syR zULZj;+I45f$g-T}Ks0?SS8h*E9d1Eo)ffpnL4G=RBA4*axE%qPBnUEV-YyN}h=pk! zP%j#IvdoO#Ht9FSG3bvgIvaV@ z5yfj0nIhBrG2gl9N2qkrNv8Ua1JkrvG4*2d^P!t#j^Doaj@ z*kP}ZD>CytPb~~6+gj-FfTRS8^xI8|G<3;APcE?J!hCx?HXXNTng?uTc2w|*gk{Zv zB>~ansoU2l*fF!1o9Ep-zKVXcewZD*xfmIl0b};f%_~zMZuy$lJhOeN1Z^wG%N1mH zIU$U^-ccx{53MQ@94tgbV-(D@Z`v_u^a2tecRI>f3N31ZaV zbHx4ieAd~`yQS-}!m@=45j{w^J*0&Ez@%s)_=&Jy(T_)(lrm~3;Gx^FYnO468*Cu} z2scs|KwXcUfQ_F?DrI_;xCygNTL-E0Q1zo$kA)u#g=&tFfxVuGpDtRBrn{Cju-B}& z(>S=eGH74wcd&CbwT`wy6^}-_VNFE*Vk?8B$AHJJ6BQYP{{4^+JD&P#l68@q1ka72 zx*p~lUM{MRT|07yaBY*gQDqAp-y@N6KgFk$=9w(9bZtKtIDsv}0FHOK`kkRQxdEKW zH`?5?UgF=eD^X9F{Cxx~uWv(gS;z3)bu{1n)=EjV>wqfHbt7a`7UEE9KtKAuKS=_^ zG$lpM1dvL6aDE3$6{>APY#H{uaH#6JFroRmNaBm+b-*}`rFqSZwtF8nq@jpjxZ`l0I9$M&hPX?ZJnb1O?qoMX*bB zKIo(rZmqtIw45j@B;KbjsgIl|JRL60OY^Q9u4)^1o;2ks)n=jH|m9!Y~ z*Mv$Fg208zOAOEotxE-~*fG{{F}K1=mO}AJb7@9=SkknQn!I6Df}fw{qGk1}E&37Q;P8Sht6S+urV&!JTVUSGJrTs~YmCHSsk4hPs^j>S?;eZ(Mw%IFHmw0RWW75S2y}Xozk41( zQOg*C`N0o1Ryi$9GNMr1>PKZfcP>rn&&y;b-RwE#ceo%Ks`s<=SuT}<=z?+ZU?YJeU+ zEG+BxQtDY&>}U{Ns9fd#$F~-=>U%VVrE}9!d@-brJjPvznk`7jHiys=B zK~~bLn}XKJO%z0-Pe;GR&i#-PVZw zG3J8l6%pMi!&4bap(Sm>4YcA(0#^N33nzeULzX~^9GsFvR2V)#HEaN@Tg;EVmj!o- z;EvCi4|-w3@;Hgqgh}e{{kNI)eQX92E$+&7r~7z6Z{0-xg7KK!0wDAx67=s(qN4`s z9#HbBqg;~FRukpHzEGNOHn!$2GhWd=jkBMd@Aguu9}qLzR%j*QIn+2wJ;sQd3V;_j zcNknEB*EaDT6~e{yw?h0wnhYg*J96ZH}f)=S2Rk2cGAlM zZyxWc*t5h*RH@d&gdlG>=;O8UI|HL%& zM~yt}#KO%k8djPPyulkBjRK+!q1<){wGS8LD4Sk(q}BHrSPCWC%9Z)xR-TQ@Y3Z=5 zHC6aTkyJaA6Yoy8*z=kb57qC~d_z&=Y2q}S7v|D-rOrBm)Ky0OU@J;>Ien|6FxstH zx=7YiV5w&APp8bpgoL=1jGzs_5i3i}p2I6<4jy3tG@<(F)a}FV22l;`wn7_v)vJir z(3KTYPr&jjM7YYiK+y1MEI1U{D>A!Eb^*H&H@+ub&osX8|F%82@BI5P`@YXu{p+AP zFZB|__xbfxNz6AxPAkJN{w#ZwWv{e*psU=?it<{(Mqc^U0G^7&(}f)eXB%3tWyks- zjls49gs_RT^-?;d^HGy%FRp09%#7H#i+eP8>|n6_p|ql*Vc6;R`9Ms!B{*AP@ov-5 zvFlEtLsnB0ZdzRx%14#t?)I7BgXP5jbo`bJL2c4l&NIpK_Bvqt{tnAfs^RC~Uj)!N zXe3g}>`(z9^mqeqMhLVy(bv6}bQyo8r|L)wr zQ81>R{ypt0t0wq2Yfc~DKb!3xM_-%9JG}h_=nh{BPJM3}eLg~ai*{ptYV0Q9*uKvF zj9qdr{`{jV;SiOn&ufR588M9!MCsCwQG1sK<%!5yWFTT>PvQKP2h&f26*Z|KRYi$< z`Y43x$0AmaVLmp9^Na9MH6n-MVP$PU6$@Wbwl;a)MIuK3&eZSE-dehA)bom0E|Jv@ zjs5sDi?e|-)sCR8*#5pCpZc6VGN_iFp;X1k18rfcI}XV{)bzRFD{-WZO!DXO!#GlFS2IGUSCL#3j0Y0BTjI4$ zno#xm^{%}vHKQ-vY-Vd`JNlyZ4#F_CL(@z#Wa~9UPg??ndF0tEORFOD!-;kY8F(Tl3BzzNg@X|dhi9gx(eC5AO$y&YuTpzly&!=-I~cWT|bqG zsvL_-8;mnpeyW*fyMOGS$&OP_pG?ZPw7P(ktL!1I<>lqAz(n?rqnLv^A5F8r{`g_X zSwT#{%9GTo8m)v)qTf%2$?U3SJjba<7<&E?Y5eYeXx&a)roe`i|0A{k`z!w*=|H45 zYfj0I{(2?zmbJj>ng^a!)jf52IalE1@Uj?#6SSP92x&1aB49Qd*b4x{+*?*0r6pMz(JV%()u@)Qh0IQgsZua`vm|?| zo2a)!u~Nv{nDc|h8hd_`gUBP$xmejzS^Z2LLQP(kV>-}+_ApjFTvD!XUhhqj^yb;! zX?H2zr@VS2`Y$IA&rR3GQVS6+B%^Bvz*+Y1rjeXc=bWI>U*hzXg#ZQf=)su6jDM4b#&w7b7JNm8#Lsz`(8G# zeB(-F?>esiuLaHOUOD>QxnMI>^ad=0{ysB?1hdp zWJmAovNQ8BTg$uZxaD-iOlPcIP= z0bxD~Sib8Lz;l3=wQIhlMbD4~!O}mxLNWaJ_GQVN8;;XMjov5If7pz#a*6cI3t#E~P zAbp5f6WPSY-#2xf95X8`Jg{d$!kdjN&g!|U(lCnAmqw7{2@Dh182Tvj6DV9n+2%pO6$HVg+V-$@j}A6c=kzs-YP%zWFROxIAeJ{;kfuhXhx1vXdHXgKY)(YyQtjkYPQ6Be?z(pDSHgu<)K2lEjs zYMetrAY2p-(*_fSVar39DHb5s6BO*p=h~MmoX+!i4=Pt~F&(Ew-)NBG(p$wEB`ut> zz5Q4_#s#`u{&E^ZkVVk(H{r_@qPw9rEe@PfuRHs~$7R1Xp)U$%`L3`GN|!cU?gF@z zdSQmY#w4GMCfPB;@zGR6XoKmfxBqL+U26R0rm1{(`7!0tkS(`Q!$!C2DIf6Bd=G@* z1w5TeuXYBQ>kX#%(u4yPOk7^2W0eb)aB7q|umryQf098TG}V9y(h$byeoxV_tIA;E&fd8XY{#SIU{C9~$56+hV?1vY00f!h# zbVQiVXLl{#Gq(;VtR0QpaNgalzKhRMD{~?PvrlSr@*UD}Xu|3GNiYkL1AigTGC1ay zkJgi10Jc9_y?z6)x*(IdQ$Od(kCx1Gc_Um3C_kCv0cT5(wOOWygzb4KAiHcZQL&jT zeue`{O~9<+z~?7}bJQuVsjeN%L61Dgde%7RN7eHFgCX}aSr=5$PX;q= zA_@?EUpzY*>V*efC~nom!G0BCVMKn>90DLQ2Z2FW?KNv}XuxYZTWJb}W zDc6(JkP8=3yyh3z!4CF z&gnit-dDXnx~ZnOrt39TJE}99nGPeQ_Z}9+QI;)q8>o-H*V9)0_<1`$ zAVM~#eyu!gQQ^zo+_K`Ti2124#ITG061-mT35)13{kmG?yk~Udzqj5Zo&mZZu||rT WP~(FbHvc$<7|IG7^3}2?LH_~w82!uu literal 10421 zcmZu%bxa+=lSazJ-QC@-Sn-FuLveR4&;mss?(iP&?(Qw_?poZfxI0{bm)s?n+#fqT zlT0$3&Fs$3w_k*+vJ5H`5fT&>6snvoQ2pN=|DS;X_$LpgcYZ-ZQ9#K7#WjCqp83Kj zk`5%@>#Va+wvWiA=MwIRQ%OhwO<|wK0p{j6=H|6aV`iRbhOuWJ1~uQ`cKx#)+j|0H zunAD?F?8*tD1v2S5cex*WT|7MiIkMt!>WZ_$~VWyYBt+3iT2*nE}-Vt7g{Qu)?3S* zdV&$L=@e5rOnd*2QV$BLHogp4R-hY1$bl0JTK4&;pmVB%mVK~siuUpLC#RGx6|}WM zj40Nt!Ydd4ewh~=f;{2~p1O2Um~`p^tOiDveosPCs%pZ5J@gB|=LeYW8i&XQ3*YWV zSLnpLyp#ax0VWB=r+@kUTTw#T^qKx9#;tSXL+2C=Kgqs46&nd`eoB=Ey!;VXI!>iW z+J|jXk&^Nl^w~g7Md6uI-BMo;w8YgbE3afU!8bJc*;&R%Xp`TgRaw8I-bPuD6O!fp z_g@GT6D0H8988y(oc}g$whZ1ks5uCSfkyu=2-?UQ*13f7t};~gcl7Aq#W@gwy#@X{ ztN{yx_m-5nhQAvnr{+}V0$dQnfqR>{aJ7G+wB(9epQH2cad#hzd-++Cesjl{!{lLb zOw$X|*df?pc5i<&psf#z#wa<0sqMl?c!rir8W(e%>BdhPc_^}r5{$DCuqJRpS2$o~ zgv$(Z>&}JlgAJI1=O+AO1aKe3xAu}aRmnwiF#9ngEqnm+BgGe1+w{G88mX&TrV^|7t)EeHLSx1 z%m~va<~eD=vYNzrF$VXo6Ldlx&8%i@M8S!U$5S(kC20zz@!E;sMp*je`=K(etb)}oW7K|C6+Z<5Xxj4bwglN*8S1qegJ?nh}>c^iQ zLduMP`B#s8!Dm2Sh9|0HKn^rm4%f=>7Q!!8b^CY4)9?}Jdq(X&((RKA5h83 zWbpb$Hwo8k8sqF!CA5?r!>Rg$*mb}49X$>9`?iG7^U}mh)RgD`GbnW8$NS_7EV#zs ziuuB3YC*@ujH>Sh%@*C?Kj*#zUO6^obnVN`r!WmuWOha?TFcMp9`-o$sv_?6x_$G% zRv(w=rpaE|O=^upiSw^g#KeMG|5#oGxgGOfO+9vOP1V3|*1<8be0nvKzY4xCd+9Ex z8`xZ(K|32U9=^{~UMI{LwVx<#;Uj>u8V{;JkTk(vxN^+00X3hucf$MjrOq2pE!ie(1ag1{KRt+70{Qr zcK6$>b~)xpLELr2S;xxY-DjSQ!&-saAvBq?gpc}V0vPBR@-^Y$jp|Gfa?n z!IloeZbm$yD|;OA&z@NtKt2?mXg&I$7c^CEP9pM?vricvP`}QT_nIHtVb8k!;W7o; zcC~Q0(?w)LDX_ZZNMZc{;&XkH5MRQ1hz=a%|C2%7lVOE)5Cd0H3flF#_Qa7x zcRd3*Jb1c?kqk_blOydB9%*}S;dtWRt6aU^5=vBH==CjbNxb*E3Ka*Fb~aH&rfW*L z8J2}W$C!5;PK*m!rY&-aa*8%Q!ht*Xz?^mP8u!~eS3pI1TAwCQDif-itQEGV=1ejf zu6Bmnf`pVfX6=un=<>NjY%GQ|WatV*KkG5dTpb;+#JZA@{UI@6z!UJEX zss?wL90%K}e>_Q~=wY>DkoG@I(@?AZ* z9m__P@&n@%nzyO@bwF;y)>>Iy$WQkGn$ys^4nI)IPBwc#@nFVR+CQRaSu)zb3H0C@ zBtzvo3n^rvQvoX~6GBOg>A@Fy6UkA9i*(6MaRhLN5}3Q;7rd8G3Xk^7GY{ens{s8N ztID(Mr#(^H?`*ih`?xsfV8y(U{cEkIPtBK*z2^H1C}pw)c?WmGJmn%_L;oHt zBz_R-)ZM+q;+|#XyD~(E#T3vM?4IlH#X}JLZc#T`F=gqzoWRRG_Oei759jA+AL12g=I{(wr3tBkCYg3<~fEhy&;+nj7c zWXa=aVOy3uNnT-B)M2Gh+=VJX#!?fLt2=le?7=K@*2m$*ea&Rv=yKTgx^Do#M&O*h z*i<}i7Jbcuv4J=k1?Y_-+77SV%yiE6B&(}O`=~0);mG!<5>8GD<3=sAMD=pd*o>{% zyF?Ix*k9CdQkK1ddzwjS5t>mjaxT3RWo3LFZem7;0>rosQ`@R1@=E`n6LP<{>vj#f zZMNWK(yaJNuGdD@B9!n#Ei_mn2tQ#|FDR&7^lLbvm2oQ)PTmog;K>8)-`x(kv5bb( z?{HTfOCi--qd$qKA6+EYy3n|+ju;(J<7R&AP3A9A$$T!?~>2ht%5RdqSHl+3@FMFM)7Ux{UoL8 z1jGxTN9klTh)AP*ax%TKd23wwt5!KUqK5w6C$xK=kxIy7B|(FI^|bf_Vkr-+a*p&6 zFZ;fHPyyfWB^&N%@tC zrH)>cr~^Az^C?3B{qM?Z?$2{#)y^NHgt%2;_;(L_(Z-S#$j0_f==7!#3ai9B_hYw-BBadnnc_VhY)Bhu}}N*VU5U-|p%* z!kwAw3i?NxQ2qG~nKs`5T`%!X3Si;ootDdJ z*8B^Hp8~-#0UQ1gfA+`sEV+~s<_U%!5@e$GEPV?MwIA;0RqSj<5Ij)xotAHywW3cs z(tbKIXdng3*YLV^fa|XeX1-yPdccl4&XcjL2bSU58DR4YZ|mj=i(%}$bj@m3BR9{o z2WHX5>;tsMq1Y+O!mUe4uEOTn!4*;*{{cI+y^XxzMDr5VEWM-WKwbj{~SM_&Q`s~OKUfJu?a;5?l@LW?)5oa?oH!7E>=;(CDAHvg5xs7tR!TG zg(>9v>7^&x82T4u`wXMG?H+cXzlP$dFoNfmrE2G;BL`9ijr#fuil0kLqWWIZXC_c; zq67_yXOv$yp#Cor!-Joc{FfC{p=svh>))ViG?QHT)6lp`%~YCrp2A0}^_+>wsPp8a zdpUaU>jIx+89%<5!vWb{eTxJ@W+fp+?AA05$a=Unf0anS=Tf*R9Gs$$OFH@BfbA_o zm8f?Eauy9A4%&p>ymnI*hsDk13c{}mbi2^$DV|hx65UGaF{-QSqTqukTUS_3Rv`_$ zKQ&jyV^b+U3)P2a{dCrIG2*xxS-tc-5+s!()^VsXV$0Ew?d$|2>Y=!if?6mx=gT9& zkW3#bXK`IV#1%zH$PkzM=aDPEQ#dV01aV^n@R|1aH)*jE^Ou$rk!+q(=C_#SW*1$$ zk_kNo%bwZ2m%k-Q%%REHF>W5-K0q``y+x5lD^Dk>~6 zt$js&JQXKPoxev2Etk&cIm91=8fpLcY(C#;#*aYdzS%>BtO zSf_R+E{$eXp7|1skeQCQW-%XE(&=SNLlX1uM=lIkt`OePu-vl^q^3^Js)9(L(4Oy( zNoI*wZmrAYN*?^nFlWb`0bs^#J1fB~69sKCTENW-u0ZAS98RM~)il{_HOeAnL>R9N zfs^DZX|kF6I>EX3dIG+XGakw<8wvhzk`p5-HwvD=SBGpR_H$;wm9^dI9~#kBf?Tk7 ztyfTXFV|-P(I$+>{6}K7;2OHaDhuwMdv2P-hOrVg+-lL<{jvA{eV6UO(1R)pX7UwM zXxD>x)7d}xxlw6)8Dn^0Z{in9gl`SEwwwr$CbX+*&mP=tW56^kuhu}aXCtda%6$P# zGqQA=I8l~LZZ&U97M^_=@d}VU6CuZB$l#rOgb4bN8|dv!^AN^l^lA|Kib9sH+QrAR zL7k%@k#R2V<#aaLndrG_e;oPD9DJI==y0#k&f#>WCu8%_R_LMiO0F6g?!=dJOB$@% zYl2c6Gs2m-@dB#O+@A-`WjDd~Gj|34fZ=lLDIQi%ufeiD(#yhbm&~16p z+uKTB3JfTYORIQyJ=Scd%r(_9$jY43`en=ur&Tz9i_2HY_I6`A(inF4kiQU8iM|pw zCI<~7AV20hZUhWJbRj7#Dvf?ADjVcdxqUe%O{1}|&P^p{O{#AkPfaKIC_wvl3$I3h zW9^5m+@p$hrRJ(g8=~AO_S^lAM_Y*2cW+kDM(g}(wsKhKg3QBlu3Jk5OM%*dhw_ro z2&V1W(-s9zukmC$C+qxgjOe>f=Zw^t1@fM&3qsV->fv;s+VL)aFs z=T9nj(6Hn}OenCPW{o>0&@ViOTB_ve@|r`4yAc(u;WIj93)4oC%oHDG!ZD|Riru46 zI7HF5IZOTM7H&numlS%-ViAmDy{X5K9iB!w`$}2leT7t8*5K}soU$7B6>XG7x$YAl zL6pkOL*(T!KW+Vt5ZoN`IJ3$iLG#@nj67lZx;%_9uDf~%F0JKqMcwKt6O;XprDtPr zsw}bAUK{{q0AF&~ZDnSR5hTSNOIW^1u+Cu;rBXvWA~|~fh4F2iQpwJ&QFyic!70k# zD4Z2%h}uO@B9{Y39s{fOi+YF7C^XyU`zk zzabIcz;2(Tqd)^w?Q%Xaz9N7Nj6aDEYa7~P4|#Xn9{oUuIt&AqW_E!as*jRYFG=8o z759QNlWdwzY%%qH%~31T_k!`Zr`_f;A}B|hNmW^w({!}b+=e!lhwjL)`_4I1R97kSE6%9B{#Es4p5zu<+%2Q|~{YZ(I%s6aRW_NL)B`jH?^BJcNX^0cY!7P7U_htr~`V|8i ztN}i2v0SYN9e#EYG>v@>i6Zqu^L*c_D_M=clsr|b%5a?_mG=JU@djH&E0rqh;~n|T zK({gW=l6x`Qp&~0$h#PYb&Y>KtMNA^zqy$S$Fk8{K3o)3bX0OSw>b=imVureT524p_o2p{tqneX#e8le7E&_jj8#Gp zq(u3~B0-HN_b5w!qK{S~1Nl}K6iRcCl5y{OmM4=PL22jQuJ0~JR=k|Cd71`;CTbIX z=X6|?$gsz22}zu(Usz@=auHE9YB>HUA(pVL*c9*)CNt11uBSslH@#xMut9m@AOmZN zrgqf8UL#_3qhIcLsJhhyC9xyLKmo~m$6HUH>G$0HN*;66L=y&_<1=y;BCz%sIB~*b zj_^O8Lkf0uv+4eAWkcpHXT^Vq6lKT6wq1bEEMNq*L7Y-M?`_;go=fn0F3W)&S_q?y zPdm7!`QpsL=PZfv8@%U*=xoQgi#(n~#OUS`pjBohFiMnU=9at8q~AV%o(uT57AA>F zPgRhQ6iL$OWym024lOpnl!`VDpgYGCEc5p^+O+#lM2Q4FKVfAe!iY((tO8#;jXeT% z)Mw*y_2UfzaL0r{)BKBO+_WWcSc}03FGEA+LnY|o?g`@(-a}@Rff5`Z3R6N#D<$IkmYLU?nz?v{dh_r5Xw!CrIaCbgbIx7Gfg2KB%n$M(6t@Oa&@3J|q!b5iW%v_0A@%Z5o9u=ZCq!A``>RD;f zjM2oaWk$hcn64VeWWw)#jpKPC#6l2ggoo=x({$>|e4yyupP4pi>Ghdqe#k8aZLPOUD*wKL% z6IQSSi<`^7P^?uYpMv<5%K8S75A$lL390X50{?A$fI0n2o%xjCBaAqsBLPQ|6Bb*# z5VlkZ9Kt7VYrif%pkzIp_)YOs zYj2{&#(kkm&iO#sZHMnFj|m@_DsBGHXEit-7uUF&SkWQW;Jo_TogN|4YiRQzvm`0x z_p_3?t>G(dRo*0bdd=jN9oQwKkftT0Bw!dOM%B9S+v9@_*^p4%Ll76{`TJv)O=S*C zq4c6&7HE-N_?01+L?~k89opcx8{_-AMoV6CxtqlZClZ~6Fw@3LCw!RxS5E#@a1uBz z5bm4R*0-EaQmy@cv*U?Rbrv&fjYi~ORXlQ^beTQRgn|!^)}lc@rim$dC0PL@g7G2^ zm7go#yO1go^?#-RwO2cJGqw4{&Gd03X5}yDehZ0`R@tljx@&CORQjy;^=Bd5->rE5 zYw1d@WQT#A+3w@UAaO$56r-JJfj4?Enxx~3V;~r4OCg2%fvR>tycHp0yOj$Y;qeys z%qM@S+tty191voMdfZgYpLJl-6x~Q%Bt#q;WuVLBT>R+!IQUS|T29jSxGs2$iW1?f zL>n>7cChW@T_-~>SXD>nnSq<OoU1Lz%{lHQAL~2+?(tWyyKU zl95o9u|+o^Ppd<7p~^?9F`=qPj7T%~DKCY=XKw2?Rgz!l41B)nY!d~Y)ELpaZ_5%P zCMJWp*r3b`$GCC4cBtB7bUrjNV2C_PawmQg^89n@R?b zei|8zDMprdVo$5U$#;rA3E!RrpXX-MW$Blab{#g!eJ9BgieDECJt=J?r=O_3iNU3~ zseqi;w;(E!tgFf zHWjs}vuA;^UxRr*zo55-r^%X=Dstbn`-vK@*OLw@H|OQ%QWVJV)hKxIVN;xF>Hb1O2}?^#&_ON)9<9M?+~(yR3b4KIiDr$h^(1p104h%3 z;s#=^eM^avF}QhXiyigDISK*Eyv8glQcDbo>@OUy_AtTN%JFxQDJ80IXpm2tAq1~c zaSUyrz|99}Ei)zr@tYUrS1ikun@TW})Sl^%CsvlRvB=GvOP8yQ@lD3-jI39$Sjey6 z0QIOc;KaTF-TtWSd$tJ|e8cC;(!FJT$_zKd_g^z0d$vh1*_i2gv#b$c;XI{YlvkTsGnlR_Vg!( zCx3Ty!kWYC>+guNX0ww(5s&SOTXQ!YSSSCuoG%XjTIc!iKtJ<2FK=937WngHqJ-=a zjs!e8w|rGq-@`(3t;>IXiI60Gg0M(N5h1 zp@jsR70JQ%q09?$gg0arSX!{)Y5h=(_!jZo04}67ysldorKRb6_wLQ%;OF@?xvQ_KRi6H)>RADI%2-D4V69wsSAh;#79o73V5 z3p*>Dal>={@^ST4`l7Di=KA;X_YD8nMd+_aQBg<(kW5r)Uqos|ARb}TcZgOf@9izB znY0G1Hmh-FWBY7Vb|-XRE&Y9(=|5zz?}$d2fq0{W%_#?NLV}Yfp1sNGxmGAdg z4m3hQZ{nfBr>K(8i@Spl#vuEecAn$k_puEUQ`ES$n4yH>?IndaOYz@8@ge(8q(+nH$U2sPS1%GxTE;rnll!+LhaeeBj#u%QZH%Jcu=QAx)8hk@8NEL zcewnX&B+#)big&KG`TS3gC+P zIbGFL^?qhAD<<2(A?0faiir0kjgz{IEj3e?!mrn;hqUoiVT(Px^fKsOMS6P z*9YzMZiEN%WiPXHJC`%N&*z#7qiE`P=8>Lf|4jlG+v+N-9PtIZQ*tgM~JRSm(7{De5j8iPW-d#0iS8sq)c>yu@r|CQFfrcR;K z9ZpH8y@4R-2;%6bAtB9X)?4QxUk3;^V*_I`gG zFRZ2Egnaemvfz!|=g%{IIOyY_ol&^s`CVs@aX|gltn|SVN;Y<-1Cg}Is#->ZZtQx- zeSKt!NCRU>#5>~E_p-rm@wmq=vWJy(MFkMLa&lz1WUvSBOz(8B-m4guXOZ&Wg2JtW z=GUuW3`wTOtmAHSOmRjPNO5yy^oQ`%mWiDxlM5n0!^MtWPw9Cv(@Bsw`-q)gK@?3>i&+LcBTGU zdF)IT8mfz@ewltK+g710n1;ICric1{1=akaf56yYZx~S07K}VsJWt=`v#wRaQB0(vM+lp`K%4SM$cDIhU%%y?{D0Xt;4toeu969Ln-DU^MX!dQnJY9M`!!2MK&_> zfK*5`Zw0&lkH79R>QqZ-d^J7!PYqI^sIqE5kBZ&v%@G}@N@21YC{~x{o3k*Pi^kvf z@aoh^7&*e6Eb+v zO!UKrJs2N5o&(kM0zfmf5Sn<`vqdFW4 zmLLSObIzpQrOpzX$;yQZN4J@rr-*Ww^(uP3)@VQ)&2L$F45Va`5n~2N$pXYl2TYp6O`ZU z1nxdSHr=6RC%rnLAbtXJjEv##zGV>uGdT7O-7GE6Sl7>OZUt)|-o4Im)BVArLnp|7 z?-`q$o!>U|Oh?P8I>Isw_V$Z+2mR+7w6#^HJc^w zZ@ba++U;aCEKknn?%&-^WJ};W)>{{#zrVH5tSB%4*Wn39|5AL04|^5yr^shQimEy0 zPPY-2(CU3;i0nNo1og32!HW7F%^cjvPIeFffxoSbD?5=?v}jny#y(FVWQhDz)ev50 zFDsfcQI3Phk1MZ3I7J52eVgrT2%pNHtQ%11`A($JWBDc>{h zq>6l0DAob%05@qd; zLW=aNX)VFpsA=fZ&s1j(_0W|2>qf@9+>f>F7~!hbkf9UQ!?}l%Db6(5q=3waLE`1{ z6x3@x>W3_1>I;J=q3jE=Ax%z8shc?0b>@JXY1)Q;HdJ(M!%_2Y8T!3M@NK`lTrp&OPB=y7aV9K4*&oF From fbe91dc23524dc5331b6dc85e342742e469993d2 Mon Sep 17 00:00:00 2001 From: BarRaider <46548278+BarRaider@users.noreply.github.com> Date: Sun, 22 Jan 2023 16:51:58 +0200 Subject: [PATCH 14/14] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index dd6cfa7..8dc1361 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,16 @@ VoiceMeeter integration and live feedback for the Elgato Stream Deck device. **Author's website and contact information:** [https://barraider.com](https://barraider.com) +## New in v2.1 +- **Advanced Queries Support** - The `Advanced Toggle` action's `Mode1 Check` now supports performing complex queries with `==` and `!=` as well as `AND/OR`. + - Example: `Strip[0].Mute AND Strip[1].Gain==7.5 AND Strip[2].device.name!="Elgato Wave"` + +- 🆕 **Stream Deck+ Support ** + - New `Gain Adjust` action allows you to control the volume of a Strip/Bus from the Dials. Pressing the dial will toggle mute. + - New `Setting Adjust` action allows you to control the main dials (Comp/Gate/Denoiser/Reverb/Delay...) from the Dials. +- The topmost "Title" property has been disabled and you can now use the (lower) Title Property's `-User Defined-` logic to put a manual title using the ` Title Prefix` field +- 64bit runtime optimization with VoiceMeeter's DLLs + ## New in v1.9 - :new: **MacroButton support!** Toggle VoiceMeeter Macro Buttons from the Stream Deck - Supports both "Toggle" mode and "PTT" mode.