GitHub Copilot helped somewhat
How would you be able reference specific widgets if they didnât have handles?
I donât know what to say â what exactly are you trying to accomplish?
By their IDs? They have internal IDs, right? Right here, in the XML:
<WIDGET type="5" style="0" id="210"
This 210 is all I need.
Sigh⌠I must have some big problem explaining, obviously.
I need an abstraction layer for an external hardware controller so it doesnât have to care how many and which widgets a specific rackspace has.
So imagine the controller having these knobs:
Gain - Master - Presence - Output
Rack 1 has these widget handles:
gain - master - output
Rack 2 has 2 amps with these combinations:
amp1gain - amp1master - amp1 presence - amp1 output
amp2gain - amp2master - amp2output
So, to map those widgets and capture value changes, I need to:
Rack 1:
Declare gain, master, output: Widget
ampcontrols: Widget Array = [gain, master, output]
mapping: Integer Array = [0, 1, 3] //indexes of âmasterâ controls
Rack 2:
amp1gain, amp1master, amp1presence, amp1output: Widget
amp1controls: Widget Array = [amp1gain, amp1master, amp1presence, amp1output]
mapping1: Integer Array = [0, 1, 2, 3]
amp2gain, amp2master, amp2output: Widget
amp1controls: Widget Array = [amp2gain, amp2master, amp2output]
mapping1: Integer Array = [0, 1, 3]
Now, if I want to do callbacks for widget changes, now I need to have code thatâs different for every rack (and 2 versions for Rack 2)
On WidgetValueChange() From gain, master, output
On WidgetValueChange() From amp1gain, amp1master, amp1presence, amp1output
On WidgetValueChange() From amp2gain, amp2master, amp2output
Instead of having one script that I could just include, having something like
On WidgetValueChange() From ampcontrols
For rack 2, depending on context, I could switch between
ampcontrols = amp1controls
and
ampcontrols = amp2controls
But that would be truly rack specific, all the logic would be included from an external script.
Please do not depend on anything inside the XML file - those IDs can change and we reserve the right to change ANYTHING inside the XML file based on our needs.
We have thought about adding explicit handles to all widgets when theyâre created but there hasnât been a particularly strong use case for that at this point.
Sigh back at ya! ![]()
Please understand that while users may only look at a few items, some of us have to at least check every single post and weâre mostly focused on items that impact the larger majority of users.
Thatâs how they started but we can expose stuff differently in the SDK for deeper kinds of control if necessary.
I will have to think about this when I have some time to see if itâs feasible within the current design/implementation - GP Script just wasnât designed for this kind of manipulation and there would be some funky semantic issues - for example, what happens if you change the assignment in the middle of a callback but a widget was changed just before that assignment and a callback already scheduled?
But the original reason for developing the SDK was to support interfacing external hardware (though some users implemented some other cool functionality for it) so you can programmatically listen (or not listen) to widgets and change any time you want. For your need, you could just use indirect callbacks (or in modern C++ just assign lambda functions) to configure what should happen.
Apart from what Iâm trying to do here, thereâs at least one extension, the radio buttons one, which relies on naming widgets a certain way. Thatâs an awkward way to do it really, users can accidentally rename something. For me the main problem is reusability of it all. Suppose I build an extension that does my global linking, and then if someone else will use it he or she will have to name things, add a ton of declarations everywhere etc. Iâd guess most users donât even want to mess with it all.
Of course, thatâs understood.
That would be great.
Which is exactly what Iâm trying to achieve here.
You most certainly understand, but let me reiterate just in case - the issue of course isnât assigning ampcontrols the value of amp1controls, but being able to separate the event processing logic from specific widget set/names in a particular rack.
Not sure what lambda functions are, but will take a look, thanks for the direction!
GP Script was designed for users who are musicians, not serious programmers and the event system was designed to allow people to express simple behaviors such as,
âWhen I turn widget X, I want event E to occurâ
If youâre trying to interface hardware with significant functionality, then GP Script is simply the wrong tool and you should be using the SDK to develop a C or C++ extension.
In that environment the event processing is separate.
You listen to your widgets by calling the function
bool GP_ListenForWidget(LibraryHandle h, const char* widgetHandle, bool listen);
for each widget handle and then responding to the callback
void OnWidgetValueChanged(const char* widgetHandle, double newValue)
where the actual widget that triggered the call is included by name.
By defining a map of widget names to lambda functions, you can (a) invoke arbitrary functions for each widget and (b) change the function for any widget on the flyâŚe.g.
std::map<std::string, std::function<void(double newValue)>> myWidgetCallbacks;
then do such things as
myWidgetCallbacks["abc"] = [](double newValue)
{
// Execute this anonymous function when widget "abc" changes
}
or, in your case, where you need to be able to change the functionality on the fly,
myWidgetCallbacks["abc"] = foo;
where foo is a function that takes a newValue as a parameter and then later change to
myWidgetCallbacks["abc"] = bar;
where bar is a different function to be called when widget âabcâ changes.
I regret Iâm not in a position to teach C++ programming but what youâre trying to do is pretty simple with the SDK
Iâm certainly not a programmer, neither a serious nor even an unserious one ![]()
I resorted to coding several times in my life, but that was out of desperation, when some functionality was missing in things I used daily, and I couldnât find a solution. Some 20 years ago I had to do something in C++, and at the time promised it not to touch it ever again ![]()
Now with GP I have a similar situation, since Iâm moving a lot and even changing countries, portability requirements demand using software instead of my rack with Axe-FX and other toys waiting in storage until they can be shipped to a more permanent destination maybe in a couple of years. Then Iâll forget this nightmare of using plugins for love playing, but probably will still use GP for backing tracks and automation instead of a DAW.
Anyway, the reason why I gave this unsolicited life story was to say that I do understand how software may not have tweaking as a priority. If only it did what I needed without any scripting Iâd have been a very happy camper, I donât enjoy spending time on this at all. ![]()
Also, I pretty much solved MY problem, so probably will spend another weekend on finalizing this for using in my actual gig files with the actual controller and call it a day.
That I was thinking though was how can I share this usefully with other people who arenât programmers. And for that a capability to use variables for sources of callbacks is really key, thereâs too much tweaking required otherwise, and people need to know what theyâre doing.
Or maybe indeed turning this into an extension would be another way. But the overhead of diving into this whole void main header file cmake nonsense to write a tiny library is a bit discouraging, Iâm not sure my love of mankind extends that far. ![]()
Anyhow, thanks for your explanation, Iâll definitely take a look, maybe with tools like Copilot or Cursor itâs easier these days.
I downloaded your gig file on the other thread. I have an X-Touch Mini, so attempted to replicate your setup.
It was definitely a large challenge, particularly because bi-directional syncing with the X-Touch Mini was a key influence on what path you could take with your solution. Not only did you have the 4 main modes/banks, but I saw you also had sub-modes/banks for the FX, where you could control either the overdrive, chorus or reverb.
Congratulations on what you achieved with the scripting - I can see a comprehensive use of so many functions and callbacks!
The only advice I had that came to mind, was there may be a path for you to use a single Gig Script to handle all of the heavy lifting and communication with the X-Touch Mini, to the extent where you could do away with the Local and Global Rackspace scripts, and instead just use OSC enabled widgets and the Gig Script.
- The Gig Script can intercept incoming Midi messages and also âinjectâ any new/altered midi messages into the input stream so that GP sees the messages as all having come from your controller.
- You could track what mode/sub-mode youâre in, and track the received messages from the X-Touch while you were in a particular mode.
- You could artificially generate new unique CC messages depending on what mode/sub-mode youâre in, so these can be mapped directly to the widgets in the local panel. For example, the X-Touch sends CC 1 for knob 1, but because the Gig Script knows youâre in EQ mode, it will convert this to CC 10 and forward that onto GP to control your first EQ band slider.
- The OSC enabled widgets mean that the Gig Script OSC callbacks can capture any/all widget changes and store those. This will be how you can keep the X-Touch Mini in sync as you change modes or rackspaces, as you can send back the required CC/value to the X-Touch.
In saying all this, I have no idea whether there will be a roadblock with this approach. But if it did work, you might be able to keep everything in the single Gig Script.
Thank you so much for taking the time to check it out and provide feedback, thatâs truly appreciated! ![]()
Yes, XTM is a pain indeed. Iâm working on a full version now, with 16 knobs turns, 16 knob presses, and the buttons selecting modes and effects, so I wrote some functions to do the syncing, in 2 âLayersâ. The buttons light up when an effect is selected/turned on, go off if thereâs no such effect in the active rack, blink when an effect is available but is off. I also have something similar for knob presses.
It kinda works, but there are a couple problems. First, I canât get the hardware to refresh reliably on song/song part/variation changes, sometimes it reflects the new widget states, sometimes it doesnât, sometimes it does for some and doesnât for others. If I force refresh, I get those darn feedback loops sometimes (but not always - sigh, I so wish GP had âproperâ global parameters).
Yeah, Iâve been thinking in that direction as well. But there are challenges with this approach as well. I think I even started this way, but found that itâs quite tricky to debug (not that rackspace scripts are too easy
). The problem is that relying on the fact that something is named a certain way somewhere is a huge can of worms (thatâs why I was asking here whether itâs possible to manipulate widgets/get widget related events without using handles). Itâs also difficult/not always possible to have a universal naming scheme across all racks - i.e. some guitar plugins like those from NDSP have multiple âampsâ, and therefore 2 to 4 âgainâ controls.
So right now the problem is repeating the script for multiple racks and configuring it each time. On the flipside, itâs all done in one script - I see the declarations and can copy/paste stuff. Moving all logic to one script would make configuration easier, but would require that Iâm super careful with naming widgets properly.
Iâll keep thinking about it, and in either case thanks a lot for the suggestion!
Oh, and the second problem is that XTM doesnât send anything when switching âLayersâ, and doesnât ârememberâ anything sent to a layer thatâs currently inactive.
Yes that is a pain. Maybe you could donate a ânormalââ button to be the layer change, as you can then send a PC message from the Gig Script to change the layer and then sync up the knobs/buttons.
Alternatively, if you have your modes/banks working well, maybe you could just forego the X-Touchâs layers altogether: one less headache to deal with.
The bottom row of buttons could be set up as your bank radio buttons, giving you 8 banks. So you still have the top row of 8 buttons as well as the buttons when clicking the knobs.
Having a fixed set of banks might help with muscle memory as well, so youâre not reliant on watching the GP screen.
I have a strip of black tape along the top of my X-Touch Mini and used silver marker to list the amp control letters e.g. G, B, M, T, Pr, Lv
Aaah, thatâs a promising direction, thanks a lot for this!
Having unused Layer buttons will annoy me
, and it would be kind of awkward if I accidentally switch those layers, but I guess thatâs tolerable. Or maybe I can use the controller in MC mode where the layer buttons just donât workâŚ
So in the end, after trying multiple approaches, I ended up with intercepting button presses after switching layers in a gig script, which switches internal banks and notifies all other scripts. One extra button press to confirm and update LEDs. Not perfect, but this is what I liked best after all.
And I pretty much completed the whole thing, with full LED sync for X-Touch Mini, even using different LED modes there, so things now blink, knob LEDs switch to different modes indicating various states and availability of widgets for various parameters, so I now can adjust everything without looking at the screen, finally!!!
Hereâs the updated gig file with everything
Also made an overlay template
And the whole thing grew to some 1.5K lines of code, because so unwieldy that I had to get a repo on Github to track everything, like a real grown-up developer almost! ![]()