[ANSWERED] "Keep only one button on" latching
-
Try this: javascript-stream-deck-button-toggle-2024-02-09-3.2.6.zip
It's a JavaScript User Actor that:
- Lets you define the number of pages and also the number of buttons per page
- Lets you specify a specific button on a specific page you'd like to activate
- Has a trigger input that will turn on the specified button on the specified page and turn off all other buttons
- Outputs a normal JSON array, as well as one that follows the formatting you used in your example where the double quotes around the xlb#'s are escaped but the quotes around the xlp#'s are not.
-
@woland. Thanks so much. I was in a session all day today but will dig into this over the weekend. I feel like this is the last bit of difficult logic for my build...the rest of it is straight toggles and dials and faders. I appreciate you wading on this.
Best,
- J
-
@woland, oh. cachedResultData. This is the key! I did not previously understand how to use global variables in JavaScript. It's the one ring to rule them all :-)
-
@woland. Your javascript for the toggles was inspiring and lead me to an complete shift in how I am handling midi. The idea of generating the JSON values for the toggle based on the button pressed and the use of a global variable to hold the current state of the JSON were revelations to me.
Previously, I had created a receiver user actor that I used for each page which would take midi values from a defined range of controller#s and then output values for each button. These were then combined into a JSON for the page. I itterated this over the 9 pages (2-10) and then combined this into the larger JSON that you have seen.
Here's some grabs of that build. It's huge.
Looking at how you did your Javascript I broke it down into chunks: the calculation of page#/button# based on my midi layout, calculate which midi values were for the only-one-on toggles (designated buttons) and which were for non designated buttons. I created a script (with the help of CHATGPT) that would output the the correct page/button and a booelan each for designed and non designated button presses for midi values that are in range for a given track. I filter the boolean for the designated buttons for 0 so that the script will not trigger when the button is released.
From there I used your only-one toggle generation for the designated buttons and am updating the JSON with any non-designed button values - I have some value cycles on some buttons to choose audio track, bank/page group etc. The result is this tiny thing that builds and updates my entire JSON for the Streamdeck XL.
While I am a bit sad to throw away all that other work, I learned a lot and am ultimately very happy to find this sleak solution. It will make me think much differently about how to solve some of problems moving forward. I am also slowing learning some more advanced Javascript from you and from Chat GPT :-) I am an oldschool Basic/Pascal/C guy from my earlier years. I know how to break a problem down, but I am unfamilar with many Javascript operations, the way it works with classes, and the fineries of its syntax....but i'm learning.
A quick question for you. It looks like the global variable in the javascript actor is cleared when changing scenes. This causes an error for me when I reenter the scene. Is there a way in javascript to store a global variable that is persistent, or would I use a global variable actor and trigger this on scene enter to restore the current state of the JSON for the JSON Generating Javascript?Thank-you again for all of your help.
-
I got it working using the persitent state of the Javascript output. I added an enter scene trigger that sends the output to a new input called stored JSON, then I trigger an initialize on a small delay, which builds the JSON from the last JSON state. A bit more complex, but not overly so.
streamdeck-xl-video-player-track-buttons-json.izz
-
@jtsteph said:
I got it working using the persitent state of the Javascript output. I added an enter scene trigger that sends the output to a new input called stored JSON, then I trigger an initialize on a small delay, which builds the JSON from the last JSON state. A bit more complex, but not overly so.
You could also keep all your control actors in a background Scene that stays active so that you never leave the Scene that needs the info. You can send data to and receive data from the background Scene using either Broadcaster actors (only sends data when a value is pushed into it) and Listener actors (new data is not spit out unless data is pushed into the Broadcaster while the Listener is active in a Scene) or Set/Get Global Values actors (Set Global Values sends all its values to all relevant Get Global Values actors whenever any of its values has something pushed in and also upon entering the Scene and Get pushes all of its values out whenever it receives any new data from Set Global Values and upon entering a Scene).
This file isn't entirely about background Scenes, but explains how to always keep your "Control" Scene active: 2018-global-mapping-template.zip
- Go to the Scene Named "CONTROLS" and look inside the Macro named "Broadcast CONTROLS Scene # Macro" near the top center
- Go to the Scene named "SCENE Template" and look inside the User Actor named "Activate CONTROLS Scene"
-
@woland said:
Set/Get Global Values actors (Set Global Values sends all its values to all relevant Get Global Values actors whenever any of its values has something pushed in and also upon entering the Scene
I think this will be more reliable. The "persistent" state of the outputs is not always consistent unless snapshotting. I will try working with global values or broadcaster/listeners. Thanks, @Woland .
-
@jtsteph said:
The "persistent" state
This made me realize that there's also the Data Array actor. You could write your information to a text file whenever you need to update it and then read the most recent information from that text file in any Scene.
-
@woland Very cool. I think this might be a great way to "save" the current state of a patch. When I build the the show, it would be really great if I could recall things using a simple switcher rather than creating a whole bunch of snapshots and scenes that I will inevitably get lost in.
I just put together a cool little recall actor that uses a pulse generator and some javascript to output a stream of midi to the controller to update their button states from the JSON. it seems to be working well and replaces a truly massive patch.
My previous recall patch. I put an entire page of 32 buttons in one user actor and duplicated this 9 times:
The new recall patch.
It might not be needed, but I thought that sending the midi information back to the controller as a stream would be more reliable than just sending it at the speed of Javascript. I thought to use a pulse generator to step through the javascript one JSON segment at a time. When the executeRecall is triggered, it turns the Processing_flag on which starts the Pulse Generator. Each pulse moves through a segment of the JSON and sends the stored values to the midi controller based on the track, page and button number. When it's finished it turns the processing flag back to 0.
I started working with the pulse generator at 60hz and worked up to 480hz. It's all seemingly still sending reliably at 480hz so I stopped there. The whole thing is working smoothly and so much lighter than what I was doing before.
I am kind of crying about the time spent on the other patch, but perhaps it took me the time it took in order to sort out the procedures and functionality that I needed which really emerged as I worked.With all of my midi stuff running I have .6% load now! Even when I initiate a recall, it hardly bumps the load at all.
-
For future reference. When using the "Keep only one button on" javascript strategy by @woland, you can accompany this with a streamdeck midi plugin script on each of your toggle buttons. https://trevligaspel.se/stream...
It uses a global variable to store the ID of the currently active button's hardware ID (@e_id) to effect an only-one-on toggle DISPLAY for an entire profile. It sends 127 when pressed, and displays "---ON--". If you press another button, the "---ON---" display turns off on the previous button and turns on on the current button. It will also accept 0 or 127 via midi on the specified channel and controller. It works over all 10 pages as long as the midi controller numbers are unique for each button.
The trevligaspel midi plugin for streamdeck is quite remarkable. You can script translations, cycles, toggles, all kind of things. The developer is really very pleasant and helpful. I'll to a post a report about it in another thread later...
For now, this is the streamdeck companion to the only-one-on toggle javascript. I'll paste the script for a single button and also a commented version. I have added a thumbnail display for the button. I use environmental variables in windows to point to my thumbnails directory.
Script for a single button:
[ (config) {TriggerOnUnchangedVariables:No} ]
[(init){@l_state:#IF(@g_activeID=@e_id, 1, 0)#}{image:%thumbs%\vidTN\T01_P02_B01_C01.jpg}]
[ (press) {@g_activeID:#@e_id#}{cc:1,3,127}]
[ (press) {@g_activeID:" "}{cc:1,3,0}]
[(@g_activeID:*){@l_state:#IF(@g_activeID=@e_id, 1, 0)#}]
[(cc:1,3,0) {@l_state:0}{@g_activeID:" "}]
[(cc:1,3,127){@l_state:1}{@g_activeID:#@e_id#}]
[(@l_state:0){text:#none#} {nextpress:1}]
[(@l_state:1){text:--ON--} {nextpress:2}]Commented script.
//Trigger script on variable change
[ (config) {TriggerOnUnchangedVariables:No} ]//Initialize - assign Local State to the result of (if the global active ID = the @e_id (hardware id for the button) then 1 else 0. Load thumbnail to button image.
[(init){@l_state:#IF(@g_activeID=@e_id, 1, 0)#}{image:%thumbs%\vidTN\T01_P02_B01_C03.jpg}]//Press 1. make global id = the e_id. send cc 1,5,127
[ (press) {@g_activeID:#@e_id#}{cc:1,5,127}]//Press 2. make global id- " ". Send cc: 1,5,0
[ (press) {@g_activeID:" "}{cc:1,5,0}]//assign the global ID to the result of: If global id =e_id then 1 else 0
[(@g_activeID:*){@l_state:#IF(@g_activeID=@e_id, 1, 0)#}]//on midi recieve 0, assign local state (@l_state) to 0. assign global active ID to " "
[(cc:1,5,0) {@l_state:0}{@g_activeID:" "}]//on midi recieve 0, assign l_state to 0. assign global ID to " "
[(cc:1,5,127){@l_state:1}{@g_activeID:#@e_id#}]//Local state action for Local state 0. No text. Next button press is press index 1 (ON)
[(@l_state:0){text:#none#} {nextpress:1}]//Local state action for Local state 1. Text "--ON--", next press is press index 2 (OFF)
[(@l_state:1){text:--ON--} {nextpress:2}]The great thing is that you can do a page worth of scripts in a sheet, editor, chatgpt...whatever and then format them into an xml and use a pulldown menu to assign them making quick and accurate work of building your streamdeck interface. It's efficient.
-
@jtsteph said:
I also looked at @dusx's multi toggler which uses javascript, but it does not seem like a fit for this problem.
Just to provide a link to my Toggle User Actor, since it may be helpful to others that come across this thread: TROIKATRONIX : ISADORA - DX – JS – Multi Toggle