Suppose I have a knob sending PCs. If I move the knob from 20 to 50, my goal is to send out PC 50. I do not want to send PCs 21-49 in between (all those extra PCs make a bad reaction downstream).
Is this possible to accomplish in a scriptlet?
Has anybody written such, or else at least sees clearly what the algorithm for it would be?
Aside: If this can’t be done in a scriptlet, would it be possible/reasonable for the GP developers to add an on/off property to knob widgets that would make them behave this way?
The standard way to do this is to use a timer and wait a certain amount if time before deciding there’s no more data and then sending out the last seen value.
And so you could absolutely do this with a script or scriptlet.
When you do, it works fine.
For instance, if you have ranges of 1-21, 50-75, and 100-127 where you want to ignore the numbers not included in those ranges.
Seems logical, but Is this something that needs to be done outside the realm of callbacks?
From within a callback like this:
On ParameterValueChanged matching ProgramChangeNumber
End
How I can know there are no more future values (“asks”) coming in for this particular knob twist.
I’m not having any problem ignoring many asks until some time has elapsed since the last one - but my problems seem to be 1) not knowing the first ask is the first (so I can in general ignore it) 2) not knowing that last ask is the last (so I can for sure send it and reset the situation)
Yes, you can use the TriggerOneShotRamp and the StopOneShotRamp system functions
By the way - the title of this topic is very misleading. In the MIDI world, thinning does not mean just sending the last value, it just means to not send every single value along the way, so as to REDUCE the amount of data sent out.
Var
PC : Widget
R : Ramp
On WidgetValueChanged(newValue : double) from PC
TriggerOneShotRamp(R, 500, 10)
End
On GeneratorEndCycle(timeX : integer) from R
// Print(timeX)
// Send Out PC message
End
FWIW, I would usually create another song part with the widget and save the value to that song part via “snapshot”. Then I would change song parts to get the change in the widget.
[This may not be helpful for your use case].
I would think you could also create two widgets with two different settings with bypass. Then you could set up a button widget and use a button widget (associated with a controller button) to bypass one instead of the other (radio button?). (I’d have to work out this way of doing it. I only have 3 buttons that send midi, so I use song parts for everything).
I forget - when you a trigger a ramp that is already running, does that reset it automatically or do you have to stop the old one first (I’m not at my computer so can’t look at the functions)
I have arrived at a scriptlet that works well for me.
Here it is:
/*
Scriptlet: PC Knob Final Value 01a (Sends PCs)
Written to pair with a knob widget, an Incr widget, and a Decr widget
This code always sends the final value for the connected knob, but not all the values in-between as the knob is turned.
This can be very helpful if gear downstream doesn't respond all-so-well to a blast of multiple program change messages (PCs).
Note: Results are timer-based. if you twist the connected widget knob slowly, then some intermediate values will go out at
intervals of SnapShot_Time_ms. SnapShot_Time_ms is a Parameter, so you can control it also via widge if you like, or else
just accept the value compiled from the code here.
Use Case #1: Sending PCs to BIAS FX plugin to select guitar patch presets
01a (Progster, 20240916) Initial version
*/
var
ProgramChangeNumber ("Program"): Subrange Parameter 0 .. 127 = 0
Decr : Parameter 0..1 = 0
Incr : Parameter 0..1 = 0
// specify a nominal 500ms ramp - we will wait for it's completion for-to-send the PC
SnapShot_Time_ms : Parameter 0..2000 = 500
Last_PC_Sent : Integer = 0
PC_Ever_Sent : Boolean = False
Waiting_For_Ramp : Boolean = False
MyRamp : Ramp
Ramp_Start_Time: Double
Ramp_End_Time: Double
Ramp_Elapsed_Time : Double
function SendPchange( PC : Int )
if ( PC <> Last_PC_Sent ) then // never send the same PC twice in a row
//Print( "Called SendPchange: " + PC )
SendNow( MakeProgramChangeMessage(PC) ) // PC message on Ch 1
Last_PC_Sent = ProgramChangeNumber
Print( "Last_PC_Sent: " + Last_PC_Sent )
end
end
On GeneratorEndCycle(timeX : integer) from MyRamp
Ramp_End_Time = TimeSinceStartup()
Ramp_Elapsed_Time = Ramp_End_Time - Ramp_Start_Time
//Print( "Ramp Finished: " + TimeSinceStartup() + " Ramp Elapsed time: " + Ramp_Elapsed_Time )
// Send Out PC message
SendPchange( ProgramChangeNumber ) // ProgramChangeNumber is always most current one as provided by the connected knob widget
// reset to "neutral" state
Waiting_For_Ramp = False
End
// ParameterValueChanged callbacks
On ParameterValueChanged matching ProgramChangeNumber
// If in the "neutral" state, start a new Ramp to wait for
if ( !Waiting_For_Ramp ) then
Ramp_Start_Time = TimeSinceStartup()
TriggerOneShotRamp(MyRamp, SnapShot_Time_ms, 10)
//Print( "Ramp Started: " + Ramp_Start_Time + " SnapShot_Time_ms: " + SnapShot_Time_ms )
Waiting_For_Ramp = True
end
End
// Act on change of connected widget used for Increment of preset
On ParameterValueChanged matching Incr
ProgramChangeNumber = Last_PC_Sent
//Print( "ParameterValueChanged matching Incr " + Incr )
if (Incr == 1) and (Last_PC_Sent < 127) then
//Print( " Incr button" )
ProgramChangeNumber = ProgramChangeNumber + 1
SendPchange( ProgramChangeNumber )
Last_PC_Sent = ProgramChangeNumber
end
End
// Act on change of connected widget used for Decrement of preset
On ParameterValueChanged matching Decr
ProgramChangeNumber = Last_PC_Sent
//Print( "ParameterValueChanged matching Decr " + Decr )
if (Decr == 1) and (Last_PC_Sent > 0) then
//Print( " Decr button" )
ProgramChangeNumber = ProgramChangeNumber - 1
SendPchange( ProgramChangeNumber )
Last_PC_Sent = ProgramChangeNumber
end
End
When I first read your question, I thought that it would best be done with a bank/patch number, using 2 rows of 8 buttons (Juno 106 style). Press the first one for the bank, the second one for the patch, calculate the PC, send out the result. Or of course two rows of 10 such that you can have a range 0-99.
Your solution seems te work very well (thanks voor de demo video). Is it easy to turn the knob to the exact number you want, or do you have to slow down towards the target and end up sending 2 or 3 PCs after all?
I find it pretty easy to target and hit a number via knob twist, especially given that the delay interval can be set to individual preference. However, as is clear, this is not a “guaranteed” final-value-only solution. I think such might require psychic abilities on the part of the computer.
Not shown in the video is that the “<” and “>” buttons decrement and increment the value by 1. So, you won’t ever have to get stuck trying and failing to move the knob by a tiny increment to get a -1 or +1 change.
Since widget values save with rackspaces and song parts, once you have found a desired patch for a specific situation it is subsequently “just there”. So the widgets both assist in discovery and then save vetted results of discovery.
In my case, I’ve built and MIDI mapped a bank in BIAS FX where all my chosen clean patches are in a range, all the distorted or high gain patches are in another range, and all the weird FX in yet another range. I do discovery by twisting the knob into the desired range, then use the buttons to move one-by-one for auditions to find my final best choice for a given situation.
By the way, for those who just need a way to type in a specific program change number, there is an easier way. Simply map a label to the PC parameter the MIDI block of interest and unlock the label. You can then just click on it to see the underlying value, click again to get a touchpad through which you can enter a specific MIDI program change number.