From 5a43112d07132f37a546c6606ae6b717676f9c4a Mon Sep 17 00:00:00 2001 From: BarRaider Date: Wed, 6 Mar 2019 21:35:53 -0800 Subject: [PATCH] v1.2 ## What's new in v1.2 - Brand new Action - The `Advanced PTT` action allows you to set a bunch of settings until you release the key. See the ***VoiceMeeter Advanced PTT*** section below - New `TitlePrefix` parameter allows you to add a prefix before showing the value on the key [ProTip: Type `\n` to make the title multi-line] - The *VoiceMeeter Advanced Click/Long-Click* is now called ***VoiceMeeter Advanced Press/Long-Press*** - Added support to additional file types in the image picker - Improved Stability and small bug fixes --- README.md | 50 ++-- VoiceMeeter/Program.cs | 9 +- .../PropertyInspector/AdvancedToggle.js | 243 ------------------ VoiceMeeter/PropertyInspector/Microphone.js | 241 ----------------- VoiceMeeter/PropertyInspector/Modify.js | 214 --------------- .../VoiceMeeter/Advanced.html | 19 +- .../VoiceMeeter/AdvancedPTT.html | 48 ++++ .../VoiceMeeter/AdvancedToggle.html | 32 ++- .../VoiceMeeter/Microphone.html | 28 +- .../PropertyInspector/VoiceMeeter/Modify.html | 22 +- VoiceMeeter/PropertyInspector/sdpi.css | 6 + .../{Advanced.js => sdtools.common.js} | 128 +++++---- VoiceMeeter/VMAdvancedPTT.cs | 121 +++++++++ .../{VMAdvanced.cs => VMAdvancedPress.cs} | 76 +++--- VoiceMeeter/VMAdvancedToggle.cs | 77 +++--- VoiceMeeter/VMManager.cs | 8 +- VoiceMeeter/VMMicrophone.cs | 69 +++-- VoiceMeeter/VMModify.cs | 70 +++-- VoiceMeeter/VoiceMeeter.csproj | 40 +-- VoiceMeeter/manifest.json | 33 ++- VoiceMeeter/packages.config | 6 +- 21 files changed, 534 insertions(+), 1006 deletions(-) delete mode 100644 VoiceMeeter/PropertyInspector/AdvancedToggle.js delete mode 100644 VoiceMeeter/PropertyInspector/Microphone.js delete mode 100644 VoiceMeeter/PropertyInspector/Modify.js create mode 100644 VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html rename VoiceMeeter/PropertyInspector/{Advanced.js => sdtools.common.js} (58%) create mode 100644 VoiceMeeter/VMAdvancedPTT.cs rename VoiceMeeter/{VMAdvanced.cs => VMAdvancedPress.cs} (63%) diff --git a/README.md b/README.md index 58eead5..410981b 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,12 @@ # streamdeck-voicemeeter VoiceMeeter integration and live feedback for the Elgato Stream Deck device. -## What's new in v1.0 -### 1. -VoiceMeeter Advanced Plugin is now called *VoiceMeeter Advanced Click/Long-Click* to differentiate from the brand new *VoiceMeeter Advanced Toggle* plugin - -### 2. -#### Changes to "VoiceMeeter Mute/Unmute": -- You can now choose your own images to display, instead of the 4 pre-defined icons - -### 3. -#### "VoiceMeeter Advanced Toogle" -- **Brand new plugin!** -- Read below for full features list +## What's new in v1.2 +- Brand new Action - The `Advanced PTT` action allows you to set a bunch of settings until you release the key. See the ***VoiceMeeter Advanced PTT*** section below +- New `TitlePrefix` parameter allows you to add a prefix before showing the value on the key [ProTip: Type `\n` to make the title multi-line] +- The *VoiceMeeter Advanced Click/Long-Click* is now called ***VoiceMeeter Advanced Press/Long-Press*** +- Added support to additional file types in the image picker +- Improved Stability and small bug fixes ## Current functionality ### 4 Plugins built into one: @@ -30,37 +24,39 @@ VoiceMeeter Advanced Plugin is now called *VoiceMeeter Advanced Click/Long-Click * Options include modifying the: Gain slider, gate, comp, mono button, solo button, audibility, color_x, color_y, eqgain1, eqgain2, eqgain3, fx_x, fx_y, mc,pan_x, pan_y (Valid values can be found starting on page 9 of VoiceMeeter API PDF: https://download.vb-audio.com/Download_CABLE/VoicemeeterRemoteAPI.pdf ) - Live feedback on the current value of that setting -- Supports both Click and Long Click (allows you to toggle between two preset values for this setting) -- Option to turn off the Live feedback and set the title to whatever you want +- Supports both Press and Long Press (allows you to toggle between two preset values for this setting) +- Option to turn off the Live feedback and set the title to whatever you want (including a prefix using the `TitlePrefix` parameter) + -#### VoiceMeeter Advanced Click/Long-Click +#### VoiceMeeter Advanced Press/Long-Press - **Note:** This is for advanced users (you better know what you're doing) - Allows you to directly modify a whole set of settings - Example: `Strip[0].mono=1;Strip[1].Mute=1;Bus[2].Gain=-20;` * Additional examples can be found on the VoiceMeeter forum: https://forum.vb-audio.com/viewtopic.php?f=8&t=346&sid=a773394396c10847fd6fd7e332a55e49#p495 and the VoiceMeeter API PDF: https://download.vb-audio.com/Download_CABLE/VoicemeeterRemoteAPI.pdf -- Supports both Click and Long Click (allows you to change between two preset list of settings) +- Supports both Press and Long Press (allows you to change between two preset list of settings) - Live feedback on whatever setting you choose -- Option to turn off the Live feedback and set the title to whatever you want +- Option to turn off the Live feedback and set the title to whatever you want (including a prefix using the `TitlePrefix` parameter) #### VoiceMeeter Advanced Toogle - **Note:** This is for advanced users (you better know what you're doing) -- Focused on toggling between two modes (versus click and long click in the VoiceMeeter Advanced Click/Long-Click) +- Focused on toggling between two modes (versus press and long press in the VoiceMeeter Advanced Press/Long-Press) - Example: `Strip[0].mono=1;Strip[1].Mute=1;Bus[2].Gain=-20;` * Additional examples can be found on the VoiceMeeter forum: https://forum.vb-audio.com/viewtopic.php?f=8&t=346&sid=a773394396c10847fd6fd7e332a55e49#p495 and the VoiceMeeter API PDF: https://download.vb-audio.com/Download_CABLE/VoicemeeterRemoteAPI.pdf - Supports toggling between two preset list of settings - Supports different user-defined icons for each preset - Live feedback on whatever setting you choose -- Option to turn off the Live feedback and set the title to whatever you want +- Option to turn off the Live feedback and set the title to whatever you want (including a prefix using the `TitlePrefix` parameter) -##### Advanced fields explained: +### Fields explained: - Mode1 Key Press - The configuration to set when we're toggling into Mode1 -> Use this to turn ON the setting e.g. `Strip[0].Mute=1` - Mode1 Check - True/False value to determine if we're in Mode1. Example: If you input: `Strip[0].Mute` the plugin will determine you're in Mode1 every time Strip0 is muted. - Mode1 Image - Customizable image that will be shown when you're in Mode1 - Mode2 Key Press - The configuration to set when we're toggling into Mode2 -> Use this to turn OFF the setting e.g. `Strip[0].Mute=0` - Mode2 Key Press - Customizable image that will be shown when you're in Mode1 - Title - Used to determine if you want a dynamic title (Based on the "Title Value" field) or a static title (Based on the "Title field" at the very top) +- Title Prefix - Text to add before displaying the `Title Value`. ProTip: Type `\n` to make the title multi-line - Title Value - Value to display in the title. Example: If you input: `Strip[0].Mono` it will display `1` when Mono is enabled on Strip0 and `0` otherwise. @@ -74,19 +70,21 @@ A: This means that VoiceMeeter is either not running or not properly installed. Q: What are the valid values for each setting? A: Valid values can be found starting on page 9 of VoiceMeeter API PDF: https://download.vb-audio.com/Download_CABLE/VoicemeeterRemoteAPI.pdf -## Dependencies -- This library uses the [streamdeck-client-csharp](https://github.com/TyrenDe/streamdeck-client-csharp) wrapper -- This library uses a modified version of [VoiceMeeterWrapper](https://github.com/tocklime/VoiceMeeterWrapper) and includes additional functionality +Q: Can I make the title multi-line? +A: Yes, write `\n` in the `Title Prefix` parameter to add lines -## Feature roadmap -Always open to more suggestions. +## Dependencies +This plugin uses the [StreamDeck-Tools](https://github.com/BarRaider/streamdeck-tools) v2.0 ## How do I get started using it? Install by clicking the com.barraider.voicemeeter.streamDeckPlugin file in the Releases folder: https://github.com/BarRaider/streamdeck-voicemeeter/releases ## I found a bug, who do I contact? -Just head over to the issues page and create a new issue. +For support please contact the developer. Contact information is available at https://barraider.github.io + +## I have a feature request, who do I contact? +Please contact the developer. Contact information is available at https://barraider.github.io ## License MIT License diff --git a/VoiceMeeter/Program.cs b/VoiceMeeter/Program.cs index 84d31d7..f1dec45 100644 --- a/VoiceMeeter/Program.cs +++ b/VoiceMeeter/Program.cs @@ -11,14 +11,7 @@ static void Main(string[] args) { // Uncomment this line of code to allow for debugging //while (!System.Diagnostics.Debugger.IsAttached) { System.Threading.Thread.Sleep(100); } - - List supportedActionIds = new List(); - supportedActionIds.Add(new PluginActionId("com.barraider.vmmicrophone", typeof(VMMicrophone))); - supportedActionIds.Add(new PluginActionId("com.barraider.vmmodify", typeof(VMModify))); - supportedActionIds.Add(new PluginActionId("com.barraider.vmadvanced", typeof(VMAdvanced))); - supportedActionIds.Add(new PluginActionId("com.barraider.vmadvancedtoggle", typeof(VMAdvancedToggle))); - - SDWrapper.Run(args, supportedActionIds.ToArray()); + SDWrapper.Run(args); } } } diff --git a/VoiceMeeter/PropertyInspector/AdvancedToggle.js b/VoiceMeeter/PropertyInspector/AdvancedToggle.js deleted file mode 100644 index 5c8c7ed..0000000 --- a/VoiceMeeter/PropertyInspector/AdvancedToggle.js +++ /dev/null @@ -1,243 +0,0 @@ -var websocket = null, - uuid = null, - actionInfo = {}, - inInfo = {}, - runningApps = [], - isQT = navigator.appVersion.includes('QtWebEngine'); - -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { - uuid = inUUID; - actionInfo = JSON.parse(inActionInfo); // cache the info - inInfo = JSON.parse(inInfo); - websocket = new WebSocket('ws://localhost:' + inPort); - - addDynamicStyles(inInfo.colors); - - websocket.onopen = function () { - var json = { - event: inRegisterEvent, - uuid: inUUID - }; - - websocket.send(JSON.stringify(json)); - - // Notify the plugin that we are connected - sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); - }; - - websocket.onmessage = function (evt) { - // Received message from Stream Deck - var jsonObj = JSON.parse(evt.data); - - if (jsonObj.event === 'sendToPropertyInspector') { - var payload = jsonObj.payload; - - var mode1Value = document.getElementById('mode1Value'); - mode1Value.value = payload['mode1Value']; - - var mode1Param = document.getElementById('mode1Param'); - mode1Param.value = payload['mode1Param']; - - var mode2Value = document.getElementById('mode2Value'); - mode2Value.value = payload['mode2Value']; - - var userImage1_filename = document.getElementById('userImage1_filename'); - userImage1_filename.innerText = payload['userImage1']; - if (!userImage1_filename.innerText) { - userImage1_filename.innerText = "No file..."; - } - - var userImage2_filename = document.getElementById('userImage2_filename'); - userImage2_filename.innerText = payload['userImage2']; - if (!userImage2_filename.innerText) { - userImage2_filename.innerText = "No file..."; - } - - var titleType = document.getElementById('titleType'); - titleType.value = payload['titleType']; - - var titleParam = document.getElementById('titleParam'); - titleParam.value = payload['titleParam']; - } - }; -} - -function updateSettings() { - var mode1Value = document.getElementById('mode1Value'); - var mode1Param = document.getElementById('mode1Param'); - var mode2Value = document.getElementById('mode2Value'); - var titleType = document.getElementById('titleType'); - var titleParam = document.getElementById('titleParam'); - var userImage1 = document.getElementById('userImage1'); - var userImage2 = document.getElementById('userImage2'); - var userImage1_filename = document.getElementById('userImage1_filename'); - var userImage2_filename = document.getElementById('userImage2_filename'); - - - var payload = {}; - - payload.property_inspector = 'updateSettings'; - payload.mode1Value = mode1Value.value; - payload.mode1Param = mode1Param.value; - payload.mode2Value = mode2Value.value; - payload.titleType = titleType.value; - payload.titleParam = titleParam.value; - payload.userImage1 = userImage1.value; - if (!userImage1.value) { - // Fetch innerText if file is empty (happens when we lose and regain focus to this key) - payload.userImage1 = userImage1_filename.innerText; - } - else { - // Set value on initial file selction - userImage1_filename.innerText = userImage1.value; - } - - payload.userImage2 = userImage2.value; - if (!userImage2.value) { - // Fetch innerText if file is empty (happens when we lose and regain focus to this key) - payload.userImage2 = userImage2_filename.innerText; - } - else { - // Set value on initial file selction - userImage2_filename.innerText = userImage2.value; - } - - - sendPayloadToPlugin(payload); -} - -function sendPayloadToPlugin(payload) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': payload - }; - websocket.send(JSON.stringify(json)); - } -} - -// our method to pass values to the plugin -function sendValueToPlugin(value, param) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': { - [param]: value - } - }; - websocket.send(JSON.stringify(json)); - } -} - -function openWebsite() { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'event': 'openUrl', - 'payload': { - 'url': 'https://BarRaider.github.io' - } - }; - websocket.send(JSON.stringify(json)); - } -} - -if (!isQT) { - document.addEventListener('DOMContentLoaded', function () { - initPropertyInspector(); - }); -} - -window.addEventListener('beforeunload', function (e) { - e.preventDefault(); - - // Notify the plugin we are about to leave - sendValueToPlugin('propertyInspectorWillDisappear', 'property_inspector'); - - // Don't set a returnValue to the event, otherwise Chromium with throw an error. -}); - -function initPropertyInspector() { - // Place to add functions -} - - -function addDynamicStyles(clrs) { - const node = document.getElementById('#sdpi-dynamic-styles') || document.createElement('style'); - if (!clrs.mouseDownColor) clrs.mouseDownColor = fadeColor(clrs.highlightColor, -100); - const clr = clrs.highlightColor.slice(0, 7); - const clr1 = fadeColor(clr, 100); - const clr2 = fadeColor(clr, 60); - const metersActiveColor = fadeColor(clr, -60); - - node.setAttribute('id', 'sdpi-dynamic-styles'); - node.innerHTML = ` - - input[type="radio"]:checked + label span, - input[type="checkbox"]:checked + label span { - background-color: ${clrs.highlightColor}; - } - - input[type="radio"]:active:checked + label span, - input[type="radio"]:active + label span, - input[type="checkbox"]:active:checked + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.mouseDownColor}; - } - - input[type="radio"]:active + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.buttonPressedBorderColor}; - } - - td.selected, - td.selected:hover, - li.selected:hover, - li.selected { - color: white; - background-color: ${clrs.highlightColor}; - } - - .sdpi-file-label > label:active, - .sdpi-file-label.file:active, - label.sdpi-file-label:active, - label.sdpi-file-info:active, - input[type="file"]::-webkit-file-upload-button:active, - button:active { - background-color: ${clrs.buttonPressedBackgroundColor}; - color: ${clrs.buttonPressedTextColor}; - border-color: ${clrs.buttonPressedBorderColor}; - } - - ::-webkit-progress-value, - meter::-webkit-meter-optimum-value { - background: linear-gradient(${clr2}, ${clr1} 20%, ${clr} 45%, ${clr} 55%, ${clr2}) - } - - ::-webkit-progress-value:active, - meter::-webkit-meter-optimum-value:active { - background: linear-gradient(${clr}, ${clr2} 20%, ${metersActiveColor} 45%, ${metersActiveColor} 55%, ${clr}) - } - `; - document.body.appendChild(node); -}; - -/** UTILITIES */ - -/* - Quick utility to lighten or darken a color (doesn't take color-drifting, etc. into account) - Usage: - fadeColor('#061261', 100); // will lighten the color - fadeColor('#200867'), -100); // will darken the color -*/ -function fadeColor(col, amt) { - const min = Math.min, max = Math.max; - const num = parseInt(col.replace(/#/g, ''), 16); - const r = min(255, max((num >> 16) + amt, 0)); - const g = min(255, max((num & 0x0000FF) + amt, 0)); - const b = min(255, max(((num >> 8) & 0x00FF) + amt, 0)); - return '#' + (g | (b << 8) | (r << 16)).toString(16).padStart(6, 0); -} diff --git a/VoiceMeeter/PropertyInspector/Microphone.js b/VoiceMeeter/PropertyInspector/Microphone.js deleted file mode 100644 index 1e590bb..0000000 --- a/VoiceMeeter/PropertyInspector/Microphone.js +++ /dev/null @@ -1,241 +0,0 @@ -var websocket = null, - uuid = null, - actionInfo = {}, - inInfo = {}, - runningApps = [], - isQT = navigator.appVersion.includes('QtWebEngine'); - -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { - uuid = inUUID; - actionInfo = JSON.parse(inActionInfo); // cache the info - inInfo = JSON.parse(inInfo); - websocket = new WebSocket('ws://localhost:' + inPort); - - addDynamicStyles(inInfo.colors); - - websocket.onopen = function () { - var json = { - event: inRegisterEvent, - uuid: inUUID - }; - - websocket.send(JSON.stringify(json)); - - // Notify the plugin that we are connected - sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); - }; - - websocket.onmessage = function (evt) { - // Received message from Stream Deck - var jsonObj = JSON.parse(evt.data); - - if (jsonObj.event === 'sendToPropertyInspector') { - var payload = jsonObj.payload; - - var micType = document.getElementById('micType'); - micType.value = payload['micType']; - - var strip = document.getElementById('strip'); - strip.value = payload['strip']; - - var stripNum = document.getElementById('stripNum'); - stripNum.value = payload['stripNum']; - - var imageType = document.getElementById('imageType'); - imageType.value = payload['imageType']; - - var singleValue = document.getElementById('singleValue'); - singleValue.value = payload['singleValue']; - - var userImage1_filename = document.getElementById('userImage1_filename'); - userImage1_filename.innerText = payload['userImage1']; - if (!userImage1_filename.innerText) { - userImage1_filename.innerText = "No file..."; - } - - var userImage2_filename = document.getElementById('userImage2_filename'); - userImage2_filename.innerText = payload['userImage2']; - if (!userImage2_filename.innerText) { - userImage2_filename.innerText = "No file..."; - } - } - }; -} - -function updateSettings() { - var micType = document.getElementById('micType'); - var strip = document.getElementById('strip'); - var stripNum = document.getElementById('stripNum'); - var imageType = document.getElementById('imageType'); - var singleValue = document.getElementById('singleValue'); - var userImage1 = document.getElementById('userImage1'); - var userImage2 = document.getElementById('userImage2'); - var userImage1_filename = document.getElementById('userImage1_filename'); - var userImage2_filename = document.getElementById('userImage2_filename'); - - var payload = {}; - - payload.property_inspector = 'updateSettings'; - payload.micType = micType.value; - payload.strip = strip.value; - payload.stripNum = stripNum.value; - payload.imageType = imageType.value; - payload.singleValue = singleValue.value; - payload.userImage1 = userImage1.value; - if (!userImage1.value) { - // Fetch innerText if file is empty (happens when we lose and regain focus to this key) - payload.userImage1 = userImage1_filename.innerText; - } - else { - // Set value on initial file selction - userImage1_filename.innerText = userImage1.value; - } - - payload.userImage2 = userImage2.value; - if (!userImage2.value) { - // Fetch innerText if file is empty (happens when we lose and regain focus to this key) - payload.userImage2 = userImage2_filename.innerText; - } - else { - // Set value on initial file selction - userImage2_filename.innerText = userImage2.value; - } - - sendPayloadToPlugin(payload); -} - -function sendPayloadToPlugin(payload) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': payload - }; - websocket.send(JSON.stringify(json)); - } -} - -// our method to pass values to the plugin -function sendValueToPlugin(value, param) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': { - [param]: value - } - }; - websocket.send(JSON.stringify(json)); - } -} - -function openWebsite() { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'event': 'openUrl', - 'payload': { - 'url': 'https://BarRaider.github.io' - } - }; - websocket.send(JSON.stringify(json)); - } -} - -if (!isQT) { - document.addEventListener('DOMContentLoaded', function () { - initPropertyInspector(); - }); -} - -window.addEventListener('beforeunload', function (e) { - e.preventDefault(); - - // Notify the plugin we are about to leave - sendValueToPlugin('propertyInspectorWillDisappear', 'property_inspector'); - - // Don't set a returnValue to the event, otherwise Chromium with throw an error. -}); - -function initPropertyInspector() { - // Place to add functions -} - - -function addDynamicStyles(clrs) { - const node = document.getElementById('#sdpi-dynamic-styles') || document.createElement('style'); - if (!clrs.mouseDownColor) clrs.mouseDownColor = fadeColor(clrs.highlightColor, -100); - const clr = clrs.highlightColor.slice(0, 7); - const clr1 = fadeColor(clr, 100); - const clr2 = fadeColor(clr, 60); - const metersActiveColor = fadeColor(clr, -60); - - node.setAttribute('id', 'sdpi-dynamic-styles'); - node.innerHTML = ` - - input[type="radio"]:checked + label span, - input[type="checkbox"]:checked + label span { - background-color: ${clrs.highlightColor}; - } - - input[type="radio"]:active:checked + label span, - input[type="radio"]:active + label span, - input[type="checkbox"]:active:checked + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.mouseDownColor}; - } - - input[type="radio"]:active + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.buttonPressedBorderColor}; - } - - td.selected, - td.selected:hover, - li.selected:hover, - li.selected { - color: white; - background-color: ${clrs.highlightColor}; - } - - .sdpi-file-label > label:active, - .sdpi-file-label.file:active, - label.sdpi-file-label:active, - label.sdpi-file-info:active, - input[type="file"]::-webkit-file-upload-button:active, - button:active { - background-color: ${clrs.buttonPressedBackgroundColor}; - color: ${clrs.buttonPressedTextColor}; - border-color: ${clrs.buttonPressedBorderColor}; - } - - ::-webkit-progress-value, - meter::-webkit-meter-optimum-value { - background: linear-gradient(${clr2}, ${clr1} 20%, ${clr} 45%, ${clr} 55%, ${clr2}) - } - - ::-webkit-progress-value:active, - meter::-webkit-meter-optimum-value:active { - background: linear-gradient(${clr}, ${clr2} 20%, ${metersActiveColor} 45%, ${metersActiveColor} 55%, ${clr}) - } - `; - document.body.appendChild(node); -}; - -/** UTILITIES */ - -/* - Quick utility to lighten or darken a color (doesn't take color-drifting, etc. into account) - Usage: - fadeColor('#061261', 100); // will lighten the color - fadeColor('#200867'), -100); // will darken the color -*/ -function fadeColor(col, amt) { - const min = Math.min, max = Math.max; - const num = parseInt(col.replace(/#/g, ''), 16); - const r = min(255, max((num >> 16) + amt, 0)); - const g = min(255, max((num & 0x0000FF) + amt, 0)); - const b = min(255, max(((num >> 8) & 0x00FF) + amt, 0)); - return '#' + (g | (b << 8) | (r << 16)).toString(16).padStart(6, 0); -} diff --git a/VoiceMeeter/PropertyInspector/Modify.js b/VoiceMeeter/PropertyInspector/Modify.js deleted file mode 100644 index 9df8367..0000000 --- a/VoiceMeeter/PropertyInspector/Modify.js +++ /dev/null @@ -1,214 +0,0 @@ -var websocket = null, - uuid = null, - actionInfo = {}, - inInfo = {}, - runningApps = [], - isQT = navigator.appVersion.includes('QtWebEngine'); - -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { - uuid = inUUID; - actionInfo = JSON.parse(inActionInfo); // cache the info - inInfo = JSON.parse(inInfo); - websocket = new WebSocket('ws://localhost:' + inPort); - - addDynamicStyles(inInfo.colors); - - websocket.onopen = function () { - var json = { - event: inRegisterEvent, - uuid: inUUID - }; - - websocket.send(JSON.stringify(json)); - - // Notify the plugin that we are connected - sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); - }; - - websocket.onmessage = function (evt) { - // Received message from Stream Deck - var jsonObj = JSON.parse(evt.data); - - if (jsonObj.event === 'sendToPropertyInspector') { - var payload = jsonObj.payload; - - var paramType = document.getElementById('paramType'); - paramType.value = payload['paramType']; - - var strip = document.getElementById('strip'); - strip.value = payload['strip']; - - var stripNum = document.getElementById('stripNum'); - stripNum.value = payload['stripNum']; - - var setValue = document.getElementById('setValue'); - setValue.value = payload['setValue']; - - var longPressValue = document.getElementById('longPressValue'); - longPressValue.value = payload['longPressValue']; - - var titleType = document.getElementById('titleType'); - titleType.value = payload['titleType']; - - - } - }; -} - -function updateSettings() { - var paramType = document.getElementById('paramType'); - var strip = document.getElementById('strip'); - var stripNum = document.getElementById('stripNum'); - var setValue = document.getElementById('setValue'); - var longPressValue = document.getElementById('longPressValue'); - var titleType = document.getElementById('titleType'); - - - var payload = {}; - - payload.property_inspector = 'updateSettings'; - payload.paramType = paramType.value; - payload.strip = strip.value; - payload.stripNum = stripNum.value; - payload.setValue = setValue.value; - payload.longPressValue = longPressValue.value; - payload.titleType = titleType.value; - - sendPayloadToPlugin(payload); -} - -function sendPayloadToPlugin(payload) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': payload - }; - websocket.send(JSON.stringify(json)); - } -} - -// our method to pass values to the plugin -function sendValueToPlugin(value, param) { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', - 'context': uuid, - 'payload': { - [param]: value - } - }; - websocket.send(JSON.stringify(json)); - } -} - -function openWebsite() { - if (websocket && (websocket.readyState === 1)) { - const json = { - 'event': 'openUrl', - 'payload': { - 'url': 'https://BarRaider.github.io' - } - }; - websocket.send(JSON.stringify(json)); - } -} - -if (!isQT) { - document.addEventListener('DOMContentLoaded', function () { - initPropertyInspector(); - }); -} - -window.addEventListener('beforeunload', function (e) { - e.preventDefault(); - - // Notify the plugin we are about to leave - sendValueToPlugin('propertyInspectorWillDisappear', 'property_inspector'); - - // Don't set a returnValue to the event, otherwise Chromium with throw an error. -}); - -function initPropertyInspector() { - // Place to add functions -} - - -function addDynamicStyles(clrs) { - const node = document.getElementById('#sdpi-dynamic-styles') || document.createElement('style'); - if (!clrs.mouseDownColor) clrs.mouseDownColor = fadeColor(clrs.highlightColor, -100); - const clr = clrs.highlightColor.slice(0, 7); - const clr1 = fadeColor(clr, 100); - const clr2 = fadeColor(clr, 60); - const metersActiveColor = fadeColor(clr, -60); - - node.setAttribute('id', 'sdpi-dynamic-styles'); - node.innerHTML = ` - - input[type="radio"]:checked + label span, - input[type="checkbox"]:checked + label span { - background-color: ${clrs.highlightColor}; - } - - input[type="radio"]:active:checked + label span, - input[type="radio"]:active + label span, - input[type="checkbox"]:active:checked + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.mouseDownColor}; - } - - input[type="radio"]:active + label span, - input[type="checkbox"]:active + label span { - background-color: ${clrs.buttonPressedBorderColor}; - } - - td.selected, - td.selected:hover, - li.selected:hover, - li.selected { - color: white; - background-color: ${clrs.highlightColor}; - } - - .sdpi-file-label > label:active, - .sdpi-file-label.file:active, - label.sdpi-file-label:active, - label.sdpi-file-info:active, - input[type="file"]::-webkit-file-upload-button:active, - button:active { - background-color: ${clrs.buttonPressedBackgroundColor}; - color: ${clrs.buttonPressedTextColor}; - border-color: ${clrs.buttonPressedBorderColor}; - } - - ::-webkit-progress-value, - meter::-webkit-meter-optimum-value { - background: linear-gradient(${clr2}, ${clr1} 20%, ${clr} 45%, ${clr} 55%, ${clr2}) - } - - ::-webkit-progress-value:active, - meter::-webkit-meter-optimum-value:active { - background: linear-gradient(${clr}, ${clr2} 20%, ${metersActiveColor} 45%, ${metersActiveColor} 55%, ${clr}) - } - `; - document.body.appendChild(node); -}; - -/** UTILITIES */ - -/* - Quick utility to lighten or darken a color (doesn't take color-drifting, etc. into account) - Usage: - fadeColor('#061261', 100); // will lighten the color - fadeColor('#200867'), -100); // will darken the color -*/ -function fadeColor(col, amt) { - const min = Math.min, max = Math.max; - const num = parseInt(col.replace(/#/g, ''), 16); - const r = min(255, max((num >> 16) + amt, 0)); - const g = min(255, max((num & 0x0000FF) + amt, 0)); - const b = min(255, max(((num >> 8) & 0x00FF) + amt, 0)); - return '#' + (g | (b << 8) | (r << 16)).toString(16).padStart(6, 0); -} diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/Advanced.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/Advanced.html index fa3daa2..1ffd88b 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/Advanced.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/Advanced.html @@ -5,7 +5,7 @@ - BarRaider's VM Modify Setting + BarRaider's VoiceMeeter Plugin @@ -16,29 +16,34 @@ Example: "Strip[0].mono=1;Bus[2].Gain=-20;"

- For more information or feedback/suggestions contact me at https://BarRaider.github.io + For feedback/suggestions contact me at https://BarRaider.github.io +

Key Press
- +
Long-Press
- +
Title
-
+
+
Title Prefix
+ +
Title Value
- +
- + diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html new file mode 100644 index 0000000..8a227eb --- /dev/null +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedPTT.html @@ -0,0 +1,48 @@ + + + + + + + + BarRaider's VoiceMeeter Plugin + + + +
+
+ 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.github.io +

+
+
+
Key Press
+ +
+
+
Key Release
+ +
+
+
Title
+ +
+
+
Title Prefix
+ +
+
+
Title Value
+ +
+
+ + + diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedToggle.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedToggle.html index f8a46d2..3522b6f 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedToggle.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/AdvancedToggle.html @@ -5,7 +5,7 @@ - BarRaider's VM Modify Setting + BarRaider's VoiceMeeter Plugin @@ -16,50 +16,54 @@ Example: "Strip[0].mono=1;Bus[2].Gain=-20;"

- For more information or feedback/suggestions contact me at https://BarRaider.github.io + For feedback/suggestions contact me at https://BarRaider.github.io

Mode1 Key Press
- +
Mode1 Check
- +
Mode1 image1
- - - + + +
Mode2 Key Press
- +
Mode2 image
- - - + + +
Title
-
+
+
Title Prefix
+ +
Title Value
- +
- + diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/Microphone.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/Microphone.html index 11d3cb0..178c3d6 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/Microphone.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/Microphone.html @@ -5,17 +5,17 @@ - BarRaider's VM Mute/Unmute + BarRaider's VoiceMeeter Plugin
- For feedback/suggestions contact me at https://BarRaider.github.io + For feedback/suggestions contact me at https://BarRaider.github.io
Mic Type
- @@ -23,14 +23,14 @@
Strip/Bus
-
Strip/Bus Num
- @@ -43,7 +43,7 @@
Icon
- @@ -53,25 +53,25 @@
Value
- +
User defined image1
- - - + + +
User defined image2
- - - + + +
- + diff --git a/VoiceMeeter/PropertyInspector/VoiceMeeter/Modify.html b/VoiceMeeter/PropertyInspector/VoiceMeeter/Modify.html index 3cc3167..dd63122 100644 --- a/VoiceMeeter/PropertyInspector/VoiceMeeter/Modify.html +++ b/VoiceMeeter/PropertyInspector/VoiceMeeter/Modify.html @@ -5,17 +5,17 @@ - BarRaider's VM Modify Setting + BarRaider's VoiceMeeter Plugin
- For feedback/suggestions contact me at https://BarRaider.github.io + For feedback/suggestions contact me at https://BarRaider.github.io
Param
- @@ -36,14 +36,14 @@
Strip/Bus
-
Strip/Bus Num
- @@ -56,20 +56,24 @@
Value
- +
Long-Press Value
- +
Title
-
+
+
Title Prefix
+ +
- + diff --git a/VoiceMeeter/PropertyInspector/sdpi.css b/VoiceMeeter/PropertyInspector/sdpi.css index 48a476f..b9f6a90 100644 --- a/VoiceMeeter/PropertyInspector/sdpi.css +++ b/VoiceMeeter/PropertyInspector/sdpi.css @@ -1,3 +1,9 @@ +.linkspan { + cursor: pointer; + color: #7397d2; + text-decoration: underline; +} + html { --sdpi-bgcolor: #2D2D2D; --sdpi-background: #3D3D3D; diff --git a/VoiceMeeter/PropertyInspector/Advanced.js b/VoiceMeeter/PropertyInspector/sdtools.common.js similarity index 58% rename from VoiceMeeter/PropertyInspector/Advanced.js rename to VoiceMeeter/PropertyInspector/sdtools.common.js index 78f4377..f88f265 100644 --- a/VoiceMeeter/PropertyInspector/Advanced.js +++ b/VoiceMeeter/PropertyInspector/sdtools.common.js @@ -1,75 +1,115 @@ -var websocket = null, +// sdtools.common.js v1.0 +var websocket = null, uuid = null, + registerEventName = null, actionInfo = {}, inInfo = {}, runningApps = [], isQT = navigator.appVersion.includes('QtWebEngine'); -function connectSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { +function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { uuid = inUUID; + registerEventName = inRegisterEvent; + console.log(inUUID, inActionInfo); actionInfo = JSON.parse(inActionInfo); // cache the info inInfo = JSON.parse(inInfo); websocket = new WebSocket('ws://localhost:' + inPort); addDynamicStyles(inInfo.colors); - websocket.onopen = function () { - var json = { - event: inRegisterEvent, - uuid: inUUID - }; - - websocket.send(JSON.stringify(json)); + websocket.onopen = websocketOnOpen; + websocket.onmessage = websocketOnMessage; + loadConfiguration(actionInfo.payload.settings); +} - // Notify the plugin that we are connected - sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); +function websocketOnOpen() { + var json = { + event: registerEventName, + uuid: uuid }; + websocket.send(JSON.stringify(json)); - websocket.onmessage = function (evt) { - // Received message from Stream Deck - var jsonObj = JSON.parse(evt.data); - - if (jsonObj.event === 'sendToPropertyInspector') { - var payload = jsonObj.payload; - - var setValue = document.getElementById('setValue'); - setValue.value = payload['setValue']; + // Notify the plugin that we are connected + sendValueToPlugin('propertyInspectorConnected', 'property_inspector'); +} - var longPressValue = document.getElementById('longPressValue'); - longPressValue.value = payload['longPressValue']; +function websocketOnMessage(evt) { + // Received message from Stream Deck + var jsonObj = JSON.parse(evt.data); - var titleType = document.getElementById('titleType'); - titleType.value = payload['titleType']; + if (jsonObj.event === 'sendToPropertyInspector') { + var payload = jsonObj.payload; + loadConfiguration(payload); + } + else if (jsonObj.event === 'didReceiveSettings') { + var payload = jsonObj.payload; + loadConfiguration(payload.settings); + } + else { + console.log("Unhandled websocketOnMessage: " + jsonObj.event); + } +} - var titleParam = document.getElementById('titleParam'); - titleParam.value = payload['titleParam']; +function loadConfiguration(payload) { + console.log('loadConfiguration'); + console.log(payload); + for (var key in payload) { + try { + var elem = document.getElementById(key); + if (elem.classList.contains("sdCheckbox")) { // Checkbox + elem.checked = payload[key]; + } + else if (elem.classList.contains("sdFile")) { // File + var elemFile = document.getElementById(elem.id + "Filename"); + elemFile.innerText = payload[key]; + if (!elemFile.innerText) { + elemFile.innerText = "No file..."; + } + } + else { // Normal value + elem.value = payload[key]; + } + console.log("Load: " + key + "=" + payload[key]); } - }; + catch (err) { + console.log("loadConfiguration failed for key: " + key + " - " + err); + } + } } -function updateSettings() { - var setValue = document.getElementById('setValue'); - var longPressValue = document.getElementById('longPressValue'); - var titleType = document.getElementById('titleType'); - var titleParam = document.getElementById('titleParam'); - - +function setSettings() { var payload = {}; + var elements = document.getElementsByClassName("sdProperty"); - payload.property_inspector = 'updateSettings'; - payload.setValue = setValue.value; - payload.longPressValue = longPressValue.value; - payload.titleType = titleType.value; - payload.titleParam = titleParam.value; - - sendPayloadToPlugin(payload); + Array.prototype.forEach.call(elements, function (elem) { + var key = elem.id; + if (elem.classList.contains("sdCheckbox")) { // Checkbox + payload[key] = elem.checked; + } + else if (elem.classList.contains("sdFile")) { // File + var elemFile = document.getElementById(elem.id + "Filename"); + payload[key] = elem.value; + if (!elem.value) { + // Fetch innerText if file is empty (happens when we lose and regain focus to this key) + payload[key] = elemFile.innerText; + } + else { + // Set value on initial file selection + elemFile.innerText = elem.value; + } + } + else { // Normal value + payload[key] = elem.value; + } + console.log("Save: " + key + "<=" + payload[key]); + }); + setSettingsToPlugin(payload); } -function sendPayloadToPlugin(payload) { +function setSettingsToPlugin(payload) { if (websocket && (websocket.readyState === 1)) { const json = { - 'action': actionInfo['action'], - 'event': 'sendToPlugin', + 'event': 'setSettings', 'context': uuid, 'payload': payload }; diff --git a/VoiceMeeter/VMAdvancedPTT.cs b/VoiceMeeter/VMAdvancedPTT.cs new file mode 100644 index 0000000..b765c0c --- /dev/null +++ b/VoiceMeeter/VMAdvancedPTT.cs @@ -0,0 +1,121 @@ +using BarRaider.SdTools; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VoiceMeeter +{ + [PluginActionId("com.barraider.vmadvancedptt")] + public class VMAdvancedPTT : PluginBase + { + private class PluginSettings + { + public static PluginSettings CreateDefaultSettings() + { + PluginSettings instance = new PluginSettings(); + instance.KeyPressValue = String.Empty; + instance.KeyReleaseValue = String.Empty; + instance.TitleType = TitleTypeEnum.VMLive; + instance.TitleParam = String.Empty; + instance.TitlePrefix = String.Empty; + + return instance; + } + + [JsonProperty(PropertyName = "keyPressValue")] + public string KeyPressValue { get; set; } + + [JsonProperty(PropertyName = "keyReleaseValue")] + public string KeyReleaseValue { get; set; } + + [JsonProperty(PropertyName = "titleType")] + public TitleTypeEnum TitleType { get; set; } + + [JsonProperty(PropertyName = "titleParam")] + public string TitleParam { get; set; } + + [JsonProperty(PropertyName = "titlePrefix")] + public string TitlePrefix { get; set; } + } + + #region Private members + + private PluginSettings settings; + + #endregion + + #region Public Methods + + public VMAdvancedPTT(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(); + } + } + + public async override void KeyPressed(KeyPayload payload) + { + if (!VMManager.Instance.IsConnected) + { + await Connection.ShowAlert(); + return; + } + + if (!String.IsNullOrEmpty(settings.KeyPressValue)) + { + VMManager.Instance.SetParameters(settings.KeyPressValue); + } + } + + public override void KeyReleased(KeyPayload payload) + { + if (!String.IsNullOrEmpty(settings.KeyReleaseValue)) + { + VMManager.Instance.SetParameters(settings.KeyReleaseValue); + } + } + + public async override void OnTick() + { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetImageAsync(Properties.Plugin.Default.VMNotRunning); + return; + } + + if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) + { + string prefix = String.Empty; + if (!String.IsNullOrEmpty(settings.TitlePrefix)) + { + prefix = settings.TitlePrefix.Replace(@"\n", "\n"); + } + + await Connection.SetTitleAsync($"{prefix}{VMManager.Instance.GetParam(settings.TitleParam)}"); + } + } + + public override void ReceivedSettings(ReceivedSettingsPayload payload) + { + // New in StreamDeck-Tools v2.0: + Tools.AutoPopulateSettings(settings, payload.Settings); + Logger.Instance.LogMessage(TracingLevel.INFO, $"Settings loaded: {payload.Settings}"); + } + + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + + public override void Dispose() { } + + #endregion + } +} diff --git a/VoiceMeeter/VMAdvanced.cs b/VoiceMeeter/VMAdvancedPress.cs similarity index 63% rename from VoiceMeeter/VMAdvanced.cs rename to VoiceMeeter/VMAdvancedPress.cs index ed31993..f6a6746 100644 --- a/VoiceMeeter/VMAdvanced.cs +++ b/VoiceMeeter/VMAdvancedPress.cs @@ -9,14 +9,9 @@ namespace VoiceMeeter { - class VMAdvanced : PluginBase + [PluginActionId("com.barraider.vmadvanced")] + class VMAdvancedPress : PluginBase { - private enum TitleTypeEnum - { - VMLive = 0, - None = 1 - } - private class PluginSettings { public static PluginSettings CreateDefaultSettings() @@ -26,6 +21,7 @@ public static PluginSettings CreateDefaultSettings() instance.LongPressValue = String.Empty; instance.TitleType = TitleTypeEnum.VMLive; instance.TitleParam = String.Empty; + instance.TitlePrefix = String.Empty; return instance; } @@ -41,6 +37,9 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "titleParam")] public string TitleParam { get; set; } + + [JsonProperty(PropertyName = "titlePrefix")] + public string TitlePrefix{ get; set; } } #region Private members @@ -55,15 +54,16 @@ public static PluginSettings CreateDefaultSettings() #region Public Methods - public VMAdvanced(SDConnection connection, JObject settings) : base(connection, settings) + public VMAdvancedPress(SDConnection connection, InitialPayload payload) : base(connection, payload) { - if (settings == null || settings.Count == 0) + if (payload.Settings == null || payload.Settings.Count == 0) { this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); } else { - this.settings = settings.ToObject(); + this.settings = payload.Settings.ToObject(); } } @@ -77,9 +77,16 @@ public void LongKeyPressed() #endregion - #region IPluginable + #region PluginBase + + public override void ReceivedSettings(ReceivedSettingsPayload payload) + { + // New in StreamDeck-Tools v2.0: + Tools.AutoPopulateSettings(settings, payload.Settings); + Logger.Instance.LogMessage(TracingLevel.INFO, $"Settings loaded: {payload.Settings}"); + } - public async override void KeyPressed() + public async override void KeyPressed(KeyPayload payload) { // Used for long press keyPressed = true; @@ -97,13 +104,19 @@ public async override void KeyPressed() } } - public override void KeyReleased() + public override void KeyReleased(KeyPayload payload) { keyPressed = false; } public async override void OnTick() { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetImageAsync(Properties.Plugin.Default.VMNotRunning); + return; + } + // Stream Deck calls this function every second, // so this is the best place to determine if we need to call the long keypress if (!String.IsNullOrEmpty(settings.LongPressValue) && keyPressed && (DateTime.Now - keyPressStart).TotalSeconds > LONG_KEYPRESS_LENGTH) @@ -113,39 +126,20 @@ public async override void OnTick() if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) { - await Connection.SetTitleAsync(VMManager.Instance.GetParam(settings.TitleParam)); - } - } - - public override void Dispose() - { - } - - public async override void UpdateSettings(JObject payload) - { - if (payload["property_inspector"] != null) - { - switch (payload["property_inspector"].ToString().ToLower()) + string prefix = String.Empty; + if (!String.IsNullOrEmpty(settings.TitlePrefix)) { - case "propertyinspectorconnected": - await Connection.SendToPropertyInspectorAsync(JObject.FromObject(settings)); - break; - - case "propertyinspectorwilldisappear": - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - - case "updatesettings": - settings.SetValue = (string)payload["setValue"]; - settings.LongPressValue = (string)payload["longPressValue"]; - settings.TitleType = (TitleTypeEnum)Enum.Parse(typeof(TitleTypeEnum), (string)payload["titleType"]); - settings.TitleParam = (string)payload["titleParam"]; - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; + prefix = settings.TitlePrefix.Replace(@"\n", "\n"); } + + await Connection.SetTitleAsync($"{prefix}{VMManager.Instance.GetParam(settings.TitleParam)}"); } } + public override void Dispose() { } + + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + #endregion } } diff --git a/VoiceMeeter/VMAdvancedToggle.cs b/VoiceMeeter/VMAdvancedToggle.cs index 9d1352c..7798f35 100644 --- a/VoiceMeeter/VMAdvancedToggle.cs +++ b/VoiceMeeter/VMAdvancedToggle.cs @@ -9,14 +9,9 @@ namespace VoiceMeeter { + [PluginActionId("com.barraider.vmadvancedtoggle")] class VMAdvancedToggle : PluginBase { - private enum TitleTypeEnum - { - VMLive = 0, - None = 1 - } - private class PluginSettings { public static PluginSettings CreateDefaultSettings() @@ -29,6 +24,7 @@ public static PluginSettings CreateDefaultSettings() instance.TitleParam = String.Empty; instance.UserImage1 = String.Empty; instance.UserImage2 = String.Empty; + instance.TitlePrefix = String.Empty; return instance; } @@ -42,9 +38,11 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "mode2Value")] public string Mode2Value { get; set; } + [FilenameProperty] [JsonProperty(PropertyName = "userImage1")] public string UserImage1 { get; set; } + [FilenameProperty] [JsonProperty(PropertyName = "userImage2")] public string UserImage2 { get; set; } @@ -53,6 +51,9 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "titleParam")] public string TitleParam { get; set; } + + [JsonProperty(PropertyName = "titlePrefix")] + public string TitlePrefix { get; set; } } #region Private members @@ -63,15 +64,16 @@ public static PluginSettings CreateDefaultSettings() #region Public Methods - public VMAdvancedToggle(SDConnection connection, JObject settings) : base(connection, settings) + public VMAdvancedToggle(SDConnection connection, InitialPayload payload) : base(connection, payload) { - if (settings == null || settings.Count == 0) + if (payload.Settings == null || payload.Settings.Count == 0) { this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); } else { - this.settings = settings.ToObject(); + this.settings = payload.Settings.ToObject(); } } @@ -79,7 +81,7 @@ public VMAdvancedToggle(SDConnection connection, JObject settings) : base(connec #region IPluginable - public async override void KeyPressed() + public async override void KeyPressed(KeyPayload payload) { if (!VMManager.Instance.IsConnected) { @@ -99,10 +101,16 @@ public async override void KeyPressed() } } - public override void KeyReleased() {} + public override void KeyReleased(KeyPayload payload) { } public async override void OnTick() { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetImageAsync(Properties.Plugin.Default.VMNotRunning); + return; + } + // Set the image if (!String.IsNullOrEmpty(settings.UserImage1) && IsMode1()) { @@ -116,42 +124,30 @@ public async override void OnTick() // Set the title if (settings.TitleType == TitleTypeEnum.VMLive && !String.IsNullOrEmpty(settings.TitleParam)) { - await Connection.SetTitleAsync(VMManager.Instance.GetParam(settings.TitleParam)); + string prefix = String.Empty; + if (!String.IsNullOrEmpty(settings.TitlePrefix)) + { + prefix = settings.TitlePrefix.Replace(@"\n", "\n"); + } + + await Connection.SetTitleAsync($"{prefix}{VMManager.Instance.GetParam(settings.TitleParam)}"); } } - public override void Dispose() + public override void Dispose() { } + + public async override void ReceivedSettings(ReceivedSettingsPayload payload) { - } + // New in StreamDeck-Tools v2.0: + Tools.AutoPopulateSettings(settings, payload.Settings); + Logger.Instance.LogMessage(TracingLevel.INFO, $"Settings loaded: {payload.Settings}"); - public async override void UpdateSettings(JObject payload) - { - if (payload["property_inspector"] != null) - { - switch (payload["property_inspector"].ToString().ToLower()) - { - case "propertyinspectorconnected": - await Connection.SendToPropertyInspectorAsync(JObject.FromObject(settings)); - break; - - case "propertyinspectorwilldisappear": - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - - case "updatesettings": - settings.Mode1Value = (string)payload["mode1Value"]; - settings.Mode1Param = (string)payload["mode1Param"]; - settings.Mode2Value = (string)payload["mode2Value"]; - settings.TitleType = (TitleTypeEnum)Enum.Parse(typeof(TitleTypeEnum), (string)payload["titleType"]); - settings.TitleParam = (string)payload["titleParam"]; - settings.UserImage1 = Uri.UnescapeDataString(((string)payload["userImage1"]).Replace("C:\\fakepath\\", "")); - settings.UserImage2 = Uri.UnescapeDataString(((string)payload["userImage2"]).Replace("C:\\fakepath\\", "")); - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - } - } + // Used to return the correct filename back to the Property Inspector + await Connection.SetSettingsAsync(JObject.FromObject(settings)); } + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + #endregion #region Private Methods @@ -165,7 +161,6 @@ private bool IsMode1() return false; } - #endregion } } diff --git a/VoiceMeeter/VMManager.cs b/VoiceMeeter/VMManager.cs index e19a2b1..6d9bc6b 100644 --- a/VoiceMeeter/VMManager.cs +++ b/VoiceMeeter/VMManager.cs @@ -8,6 +8,12 @@ namespace VoiceMeeter { + internal enum TitleTypeEnum + { + VMLive = 0, + None = 1 + } + public sealed class VMManager { #region Private members @@ -67,7 +73,7 @@ public bool GetParamBool(string paramName) public string GetParam(string paramName) { - return client.GetParam(paramName).ToString(); + return client.GetParam(paramName).ToString("0.##"); } public void SetParam(string paramName, float value) diff --git a/VoiceMeeter/VMMicrophone.cs b/VoiceMeeter/VMMicrophone.cs index 4ad8cba..158bad3 100644 --- a/VoiceMeeter/VMMicrophone.cs +++ b/VoiceMeeter/VMMicrophone.cs @@ -5,6 +5,7 @@ namespace VoiceMeeter { + [PluginActionId("com.barraider.vmmicrophone")] class VMMicrophone : PluginBase { private enum MicTypeEnum @@ -54,9 +55,11 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "imageType")] public ImageTypeEnum ImageType { get; set; } + [FilenameProperty] [JsonProperty(PropertyName = "userImage1")] public string UserImage1 { get; set; } + [FilenameProperty] [JsonProperty(PropertyName = "userImage2")] public string UserImage2 { get; set; } } @@ -69,15 +72,16 @@ public static PluginSettings CreateDefaultSettings() #region Public Methods - public VMMicrophone(SDConnection connection, JObject settings) : base(connection, settings) + public VMMicrophone(SDConnection connection, InitialPayload payload) : base(connection, payload) { - if (settings == null || settings.Count == 0) + if (payload.Settings == null || payload.Settings.Count == 0) { this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); } else { - this.settings = settings.ToObject(); + this.settings = payload.Settings.ToObject(); } } @@ -85,7 +89,7 @@ public VMMicrophone(SDConnection connection, JObject settings) : base(connection #region IPluginable - public async override void KeyPressed() + public async override void KeyPressed(KeyPayload payload) { if (!VMManager.Instance.IsConnected) { @@ -96,7 +100,15 @@ public async override void KeyPressed() switch (settings.MicType) { case MicTypeEnum.SingleMode: - VMManager.Instance.SetParam(BuildDeviceName(), Convert.ToInt16(settings.SingleValue)); + int value; + if (Int32.TryParse(settings.SingleValue, out value)) + { + VMManager.Instance.SetParam(BuildDeviceName(), value); + } + else + { + await Connection.ShowAlert(); + } break; case MicTypeEnum.Toggle: bool isMuted = VMManager.Instance.GetParamBool(BuildDeviceName()); @@ -108,7 +120,7 @@ public async override void KeyPressed() } } - public override void KeyReleased() + public override void KeyReleased(KeyPayload payload) { if (settings.MicType == MicTypeEnum.PTT) { @@ -118,41 +130,29 @@ public override void KeyReleased() public async override void OnTick() { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetImageAsync(Properties.Plugin.Default.VMNotRunning); + return; + } + await Connection.SetImageAsync(GetBase64ImageStatus()); } - public override void Dispose() - { - } + public override void Dispose() { } - public async override void UpdateSettings(JObject payload) + public async override void ReceivedSettings(ReceivedSettingsPayload payload) { - if (payload["property_inspector"] != null) - { - switch (payload["property_inspector"].ToString().ToLower()) - { - case "propertyinspectorconnected": - await Connection.SendToPropertyInspectorAsync(JObject.FromObject(settings)); - break; - - case "propertyinspectorwilldisappear": - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - - case "updatesettings": - settings.MicType = (MicTypeEnum)Enum.Parse(typeof(MicTypeEnum), (string)payload["micType"]); - settings.Strip = (string)payload["strip"]; - settings.StripNum = (int)payload["stripNum"]; - settings.SingleValue = (string)payload["singleValue"]; - settings.ImageType = (ImageTypeEnum)Enum.Parse(typeof(ImageTypeEnum), (string)payload["imageType"]); - settings.UserImage1 = Uri.UnescapeDataString(((string)payload["userImage1"]).Replace("C:\\fakepath\\", "")); - settings.UserImage2 = Uri.UnescapeDataString(((string)payload["userImage2"]).Replace("C:\\fakepath\\", "")); - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - } - } + // New in StreamDeck-Tools v2.0: + Tools.AutoPopulateSettings(settings, payload.Settings); + Logger.Instance.LogMessage(TracingLevel.INFO, $"Settings loaded: {payload.Settings}"); + + // Used to return the correct filename back to the Property Inspector + await Connection.SetSettingsAsync(JObject.FromObject(settings)); } + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + #endregion #region Private Methods @@ -201,7 +201,6 @@ private string BuildDeviceName() { return $"{settings.Strip}[{settings.StripNum}].Mute"; } - #endregion } } diff --git a/VoiceMeeter/VMModify.cs b/VoiceMeeter/VMModify.cs index e866a87..e890e1b 100644 --- a/VoiceMeeter/VMModify.cs +++ b/VoiceMeeter/VMModify.cs @@ -9,6 +9,7 @@ namespace VoiceMeeter { + [PluginActionId("com.barraider.vmmodify")] class VMModify : PluginBase { private enum ParamTypeEnum @@ -31,12 +32,6 @@ private enum ParamTypeEnum audibility = 15 } - private enum TitleTypeEnum - { - VMLive = 0, - None = 1 - } - private class PluginSettings { public static PluginSettings CreateDefaultSettings() @@ -48,6 +43,7 @@ public static PluginSettings CreateDefaultSettings() instance.SetValue = String.Empty; instance.LongPressValue = String.Empty; instance.TitleType = TitleTypeEnum.VMLive; + instance.TitlePrefix = String.Empty; return instance; } @@ -69,6 +65,9 @@ public static PluginSettings CreateDefaultSettings() [JsonProperty(PropertyName = "titleType")] public TitleTypeEnum TitleType { get; set; } + + [JsonProperty(PropertyName = "titlePrefix")] + public string TitlePrefix { get; set; } } #region Private members @@ -83,15 +82,16 @@ public static PluginSettings CreateDefaultSettings() #region Public Methods - public VMModify(SDConnection connection, JObject settings) : base(connection, settings) + public VMModify(SDConnection connection, InitialPayload payload) : base(connection, payload) { - if (settings == null || settings.Count == 0) + if (payload.Settings == null || payload.Settings.Count == 0) { this.settings = PluginSettings.CreateDefaultSettings(); + Connection.SetSettingsAsync(JObject.FromObject(settings)); } else { - this.settings = settings.ToObject(); + this.settings = payload.Settings.ToObject(); } } @@ -108,7 +108,7 @@ public void LongKeyPressed() #region PluginBase - public async override void KeyPressed() + public async override void KeyPressed(KeyPayload payload) { if (!VMManager.Instance.IsConnected) { @@ -127,13 +127,19 @@ public async override void KeyPressed() } } - public override void KeyReleased() + public override void KeyReleased(KeyPayload payload) { keyPressed = false; } public async override void OnTick() { + if (!VMManager.Instance.IsConnected) + { + await Connection.SetImageAsync(Properties.Plugin.Default.VMNotRunning); + return; + } + // Stream Deck calls this function every second, // so this is the best place to determine if we need to call the long keypress if (!String.IsNullOrEmpty(settings.LongPressValue) && keyPressed && (DateTime.Now - keyPressStart).TotalSeconds > LONG_KEYPRESS_LENGTH) @@ -143,41 +149,27 @@ public async override void OnTick() if (settings.TitleType == TitleTypeEnum.VMLive) { - await Connection.SetTitleAsync(VMManager.Instance.GetParam(BuildDeviceName())); + string prefix = String.Empty; + if (!String.IsNullOrEmpty(settings.TitlePrefix)) + { + prefix = settings.TitlePrefix.Replace(@"\n", "\n"); + } + + await Connection.SetTitleAsync($"{prefix}{VMManager.Instance.GetParam(BuildDeviceName())}"); } } - public override void Dispose() - { - } + public override void Dispose() { } - public async override void UpdateSettings(JObject payload) + public override void ReceivedSettings(ReceivedSettingsPayload payload) { - if (payload["property_inspector"] != null) - { - switch (payload["property_inspector"].ToString().ToLower()) - { - case "propertyinspectorconnected": - await Connection.SendToPropertyInspectorAsync(JObject.FromObject(settings)); - break; - - case "propertyinspectorwilldisappear": - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - - case "updatesettings": - settings.ParamType = (ParamTypeEnum)Enum.Parse(typeof(ParamTypeEnum), (string)payload["paramType"]); - settings.Strip = (string)payload["strip"]; - settings.StripNum = (int)payload["stripNum"]; - settings.SetValue = (string)payload["setValue"]; - settings.LongPressValue = (string)payload["longPressValue"]; - settings.TitleType = (TitleTypeEnum)Enum.Parse(typeof(TitleTypeEnum), (string)payload["titleType"]); - await Connection.SetSettingsAsync(JObject.FromObject(settings)); - break; - } - } + // New in StreamDeck-Tools v2.0: + Tools.AutoPopulateSettings(settings, payload.Settings); + Logger.Instance.LogMessage(TracingLevel.INFO, $"Settings loaded: {payload.Settings}"); } + public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { } + #endregion #region Private Methods diff --git a/VoiceMeeter/VoiceMeeter.csproj b/VoiceMeeter/VoiceMeeter.csproj index 3d64a42..35058c3 100644 --- a/VoiceMeeter/VoiceMeeter.csproj +++ b/VoiceMeeter/VoiceMeeter.csproj @@ -7,7 +7,7 @@ {0D702B80-1750-4153-9DCC-1594D2CD46CC} Exe VoiceMeeter - com.barraider.voicemeeter.sdPlugin + com.barraider.voicemeeter v4.7.2 512 true @@ -39,15 +39,26 @@ ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - - ..\packages\streamdeck-client-csharp.0.1.2-preview\lib\netstandard2.0\streamdeck-client-csharp.dll + + ..\..\..\DotNet\StreamDeck\packages\NLog.4.5.11\lib\net45\NLog.dll + + + ..\..\..\DotNet\StreamDeck\packages\streamdeck-client-csharp.4.1.0\lib\netstandard2.0\streamdeck-client-csharp.dll + + + ..\..\..\DotNet\StreamDeck\packages\StreamDeck-Tools.2.0.0\lib\net472\StreamDeckTools.dll + - - ..\packages\System.Drawing.Common.4.6.0-preview.18571.3\lib\net461\System.Drawing.Common.dll + + ..\..\..\DotNet\StreamDeck\packages\System.Drawing.Common.4.5.1\lib\net461\System.Drawing.Common.dll + + + + @@ -66,7 +77,8 @@ - + + @@ -86,12 +98,6 @@ Plugin1.Designer.cs - - - {dabbd97d-6687-4cbd-a19e-ac9ffa3cef03} - barraider-sdtools - - PreserveNewest @@ -120,13 +126,10 @@ PreserveNewest - + PreserveNewest - - PreserveNewest - - + PreserveNewest @@ -138,9 +141,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/VoiceMeeter/manifest.json b/VoiceMeeter/manifest.json index f9a27c9..ac71494 100644 --- a/VoiceMeeter/manifest.json +++ b/VoiceMeeter/manifest.json @@ -7,7 +7,7 @@ { "Image": "Images/vm", "TitleAlignment": "bottom", - "FontSize": "16" + "FontSize": "11" } ], "SupportedInMultiActions": true, @@ -22,7 +22,7 @@ { "Image": "Images/vmTop", "TitleAlignment": "bottom", - "FontSize": "16" + "FontSize": "11" } ], "SupportedInMultiActions": true, @@ -37,11 +37,11 @@ { "Image": "Images/vmTop", "TitleAlignment": "bottom", - "FontSize": "16" + "FontSize": "11" } ], "SupportedInMultiActions": true, - "Tooltip": "VoiceMeeter Advanced Click/Long-Click - Directly access any setting through text. Supports both Click and Long Click (allows you to change between two preset list of settings).", + "Tooltip": "VoiceMeeter 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/Advanced.html" }, @@ -52,13 +52,28 @@ { "Image": "Images/vmTop", "TitleAlignment": "bottom", - "FontSize": "16" + "FontSize": "11" } ], "SupportedInMultiActions": true, "Tooltip": "VoiceMeeter 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" + }, + { + "Icon": "Images/icon", + "Name": "VoiceMeeter Advanced PTT", + "States": [ + { + "Image": "Images/vmTop", + "TitleAlignment": "bottom", + "FontSize": "11" + } + ], + "SupportedInMultiActions": true, + "Tooltip": "VoiceMeeter Advanced PTT - Enable settings until you release the button", + "UUID": "com.barraider.vmadvancedptt", + "PropertyInspectorPath": "PropertyInspector/VoiceMeeter/AdvancedPTT.html" } ], "Author": "BarRaider", @@ -67,7 +82,7 @@ "Icon": "Images/pluginIcon", "URL": "https://barraider.github.io/", "Version": "1.2", - "CodePath": "com.barraider.voicemeeter.sdPlugin.exe", + "CodePath": "com.barraider.voicemeeter.exe", "Category": "BarRaider", "CategoryIcon": "Images/categoryIcon", "OS": [ @@ -75,5 +90,9 @@ "Platform": "windows", "MinimumVersion": "10" } - ] + ], + "SDKVersion": 2, + "Software": { + "MinimumVersion": "4.1" + } } diff --git a/VoiceMeeter/packages.config b/VoiceMeeter/packages.config index 75278cf..c9fb599 100644 --- a/VoiceMeeter/packages.config +++ b/VoiceMeeter/packages.config @@ -3,6 +3,8 @@ - - + + + + \ No newline at end of file