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. 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/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 0fbff08..0a8fb74 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 { @@ -100,6 +100,7 @@ public static PluginSettings CreateDefaultSettings() #region Private members private const string LOGICAL_AND = " AND "; private const string LOGICAL_OR = " OR "; + private readonly string[] LOGICAL_COMP_OPERATORS = { "==", "!=" }; 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 = IsMode1LogicTrue(true, true); 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) && IsMode1LogicTrue(false, false)) { await Connection.SetImageAsync(Tools.FileToBase64(settings.UserImage1.ToString(), true)); } @@ -192,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"); ; @@ -209,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}"); } } @@ -233,8 +233,14 @@ public async override void ReceivedSettings(ReceivedSettingsPayload payload) #region Private Methods - private bool IsMode1(bool shouldLog) + private string RemoveQuotes(string str) + { + return str.Trim().Trim('"'); + } + + private bool IsMode1LogicTrue(bool shouldLog, bool keyPressed) { + if (String.IsNullOrEmpty(settings.Mode1Param)) { return false; @@ -257,7 +263,7 @@ private bool IsMode1(bool shouldLog) 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) { @@ -274,7 +280,7 @@ private bool IsMode1(bool shouldLog) 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) { @@ -287,7 +293,7 @@ private bool IsMode1(bool shouldLog) } else // Only one clause { - return VMManager.Instance.GetParamBool(settings.Mode1Param); + return EvaluateClause(settings.Mode1Param); } } @@ -312,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 } } diff --git a/VoiceMeeter/Actions/VMGainAdjustDialAction.cs b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs new file mode 100644 index 0000000..dfe7391 --- /dev/null +++ b/VoiceMeeter/Actions/VMGainAdjustDialAction.cs @@ -0,0 +1,251 @@ +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 = "stepSize")] + public string StepSize { 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 const double DEFAULT_STEP_SIZE = 1; + + 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; + private double stepSize = DEFAULT_STEP_SIZE; + + #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); + InitializeSettings(); + } + + #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); + double increment = payload.Ticks * stepSize; + if (payload.IsDialPressed) + { + increment = 10 * (payload.Ticks > 0 ? 1 : -1); + } + double outputVolume = 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); + InitializeSettings(); + 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)); + } + + private void InitializeSettings() + { + if (!double.TryParse(settings.StepSize, out stepSize)) + { + stepSize = DEFAULT_STEP_SIZE; + SaveSettings(); + } + } + + #endregion + } +} 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 } } diff --git a/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs new file mode 100644 index 0000000..7b151cf --- /dev/null +++ b/VoiceMeeter/Actions/VMSettingAdjustDialAction.cs @@ -0,0 +1,245 @@ +using BarRaider.SdTools; +using BarRaider.SdTools.Payloads; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel; +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, + StepSize = DEFAULT_STEP_SIZE.ToString(), + 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 = "stepSize")] + public string StepSize { 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 const double DEFAULT_STEP_SIZE = 1; + + 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; + private double stepSize = DEFAULT_STEP_SIZE; + + #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); + InitializeSettings(); + } + + #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 * stepSize; + 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); + InitializeSettings(); + 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); + } + + private void InitializeSettings() + { + if (!double.TryParse(settings.StepSize, out stepSize)) + { + stepSize = DEFAULT_STEP_SIZE; + SaveSettings(); + } + } + + #endregion + } +} diff --git a/VoiceMeeter/Images/muteEnabled.png b/VoiceMeeter/Images/muteEnabled.png new file mode 100644 index 0000000..e5f8371 Binary files /dev/null and b/VoiceMeeter/Images/muteEnabled.png differ diff --git a/VoiceMeeter/Images/settingAdjustAction.png b/VoiceMeeter/Images/settingAdjustAction.png new file mode 100644 index 0000000..f719bcb Binary files /dev/null and b/VoiceMeeter/Images/settingAdjustAction.png differ diff --git a/VoiceMeeter/Images/settingAdjustAction@2x.png b/VoiceMeeter/Images/settingAdjustAction@2x.png new file mode 100644 index 0000000..3e4bbf3 Binary files /dev/null and b/VoiceMeeter/Images/settingAdjustAction@2x.png differ diff --git a/VoiceMeeter/Images/settingAdjustIcon.png b/VoiceMeeter/Images/settingAdjustIcon.png new file mode 100644 index 0000000..07dd429 Binary files /dev/null and b/VoiceMeeter/Images/settingAdjustIcon.png differ diff --git a/VoiceMeeter/Images/settingAdjustIcon@2x.png b/VoiceMeeter/Images/settingAdjustIcon@2x.png new file mode 100644 index 0000000..755cbe8 Binary files /dev/null and b/VoiceMeeter/Images/settingAdjustIcon@2x.png differ diff --git a/VoiceMeeter/Images/volumeAction.png b/VoiceMeeter/Images/volumeAction.png new file mode 100644 index 0000000..40ccae4 Binary files /dev/null and b/VoiceMeeter/Images/volumeAction.png differ diff --git a/VoiceMeeter/Images/volumeAction@2x.png b/VoiceMeeter/Images/volumeAction@2x.png new file mode 100644 index 0000000..4413ff7 Binary files /dev/null and b/VoiceMeeter/Images/volumeAction@2x.png differ diff --git a/VoiceMeeter/Images/volumeIcon.png b/VoiceMeeter/Images/volumeIcon.png new file mode 100644 index 0000000..d2cdc28 Binary files /dev/null and b/VoiceMeeter/Images/volumeIcon.png differ diff --git a/VoiceMeeter/Images/volumeIcon@2x.png b/VoiceMeeter/Images/volumeIcon@2x.png new file mode 100644 index 0000000..fdf7183 Binary files /dev/null and b/VoiceMeeter/Images/volumeIcon@2x.png differ 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/PropertyInspector/VoiceMeeter/AdjustSettingDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html new file mode 100644 index 0000000..144e578 --- /dev/null +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdjustSettingDial.html @@ -0,0 +1,67 @@ + + + + + + + + Gain Adjust + + + + +
+
+ For feedback/suggestions contact me at https://BarRaider.com +
+ +
+
Strip Num
+ +
+
+
Setting
+ +
+
+
Step Size
+ +
+
+
Title (optional)
+ +
+
+ + 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
diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html new file mode 100644 index 0000000..1ca8771 --- /dev/null +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/GainAdjustDial.html @@ -0,0 +1,55 @@ + + + + + + + + Gain Adjust + + + + +
+
+ For feedback/suggestions contact me at https://BarRaider.com +
+
+
Strip/Bus
+ +
+
+
Strip/Bus Num
+ +
+
+
Step Size
+ +
+
+
Title (optional)
+ +
+
+ + 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/VoiceMeeter.csproj b/VoiceMeeter/VoiceMeeter.csproj index 08dab48..1e167c8 100644 --- a/VoiceMeeter/VoiceMeeter.csproj +++ b/VoiceMeeter/VoiceMeeter.csproj @@ -1,204 +1,235 @@ - - - - - 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 + + + 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/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 87d8ca4..1a15d17 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VmClient.cs @@ -1,5 +1,8 @@ using Microsoft.Win32; using System; +using System.Text; +using BarRaider.SdTools; +using VoiceMeeter; namespace VoiceMeeterWrapper { @@ -7,24 +10,37 @@ 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() { //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) { @@ -33,6 +49,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..0eed003 100644 --- a/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs +++ b/VoiceMeeter/VoiceMeeterWrapper/VoiceMeeterRemote.cs @@ -1,43 +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")] - public static extern int GetParameter(string szParamName, ref string value); + [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")] 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 164784b..c92f00d 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", + "Version": "2.1", "CodePath": "com.barraider.voicemeeter.exe", "Category": "VoiceMeeter [BarRaider]", "CategoryIcon": "Images/categoryIcon", @@ -114,6 +170,6 @@ ], "SDKVersion": 2, "Software": { - "MinimumVersion": "5.0" + "MinimumVersion": "6.0" } } 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 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