I’m working on a rackspace for the B3-V from Arturia. I’ve got a bunch of it, there there is one control/widget that I’d like to try and get just a bit better. It’s the “Chorus Type” selector, which selects between 3 different vibrato or chorus settings - a total of 6 different options.
In the plugin, it’s a single “continuous” knob that just has 6 positions, or detents. It looks like this:

I’ve got it mapped to one of my continuous encoders on my MIDI controller, and it works - each “section” matches an option - so 0-.16 is V1, .167-.33 is C1, .34-.5 is V2, etc.
But the knob widget is linear - it goes through every value, and it displays every value. The indicator moves smoothly, instead of showing “stuck” on each of the positions. I already used the “step” option to create steps in the value - so it is technically sending a single value to the plugin. I’m just wondering if there is an option to control the “steps” of the widget.
I also can’t keep turning it around. At some point I get to “max” - which is C3 - and I can’t keep turning clockwise to get back around to V1. I have to turn the other way.
Now - let’s be honest - this is a REALLY minor thing - It won’t really make much difference, other than to satisfy my OCD.
, but I thought I would put it out there to see if anyone had some ideas. I’m happy to write some scripts if needed - just not sure exactly what the script should do.
Thanks for any tips. I tried searching and reading, so hopefully I didn’t miss something.
1 Like
You could try setting the value of the knob after it receives a value to its closes rane.
Below is a script to get you started (it’s not perfect though). This code is for a normal knob, I don’t have an encoder myself to try.
var
Knob1 : Widget
NR_OF_POSITIONS: integer = 6
On WidgetValueChanged(newValue : double) from Knob1
newValue = IntToFloat(Floor(newValue * (NR_OF_POSITIONS - 1))) / (NR_OF_POSITIONS - 1)
SetWidgetValue(Knob1, newValue)
End
Below is a second variant that prevents bouncing the knob between two positions:
var
Knob1 : Widget
NR_OF_POSITIONS: integer = 6
var NoResponseGap : integer = 50 // % to prevent knob from bouncing between two positions
var LastKnobValue : double = 1.0
var LastKnobValueSet : boolean = False
function SetKnobValue(positionValue : double)
var newValue : double = IntToFloat(Floor(positionValue)) / (NR_OF_POSITIONS - 1)
SetWidgetValue(Knob1, newValue)
LastKnobValue = newValue
end
On WidgetValueChanged(newValue : double) from Knob1
var positionValue : double = newValue * (NR_OF_POSITIONS - 1)
var modValue : integer = Floor(positionValue * 1000 + 0.5) % 1000
if modValue >= (500 - NoResponseGap * 10 / 2) and modValue <= (600 + NoResponseGap * 10 / 2) then
if LastKnobValueSet then
SetWidgetValue(Knob1, LastKnobValue)
else
LastKnobValueSet = true
SetKnobValue(positionValue)
end
else
SetKnobValue(positionValue)
end
End
1 Like
@Michelkeijzers - Thanks for both of those. Sorry I was so slow. Your scripts made me think about some of the challenges with what I was trying to do.
In the end, I wrote a different script (I’ll add it below) for one particular reason - I wanted a continuous knob - I wanted to be able to turn the “continuous encoder” on my MIDI controller, and have it keep turning the widget/knob. Basically it “wraps around” to the beginning instead of getting to the end, then having to be turned “backwards”.
To make this work, I leveraged the “relative control mode” on my MIDI controller - so it just sends either 63 or 65 depending on what direction it is turning.
Here’s the script. It is doing what I want. It turns and “latches” to each position as it goes around and I can keep turning clockwise or counter-clockwise to get to whatever I want. I suspect I might be able to do this with a scriptlet, which might also make it a bit more flexible, but for now … 
var
VibCh_Knob : Widget
MainKeys : MidiInBlock
// This is the number of positions on the rotary switch
NUM_POSITIONS: integer = 6
STEP_SIZE : double = 1.0 / NUM_POSITIONS
// This will track which of the positions we are currently in
CurrentKnobPosition : integer = 0
// BUFFERZONE helps make sure any changes are intentional and prevent the knob from turning
// too quickly. Once we have X number of CC changes, then we will change the widget
BUFFERZONE : integer = 15
bufferzone_counter : integer = 0
// This determines the knob position based on a double
// And helps handle changing rackspaces or moving the widget from the screen
Function GetKnobPositionFromDouble(position : double) returns integer
result = Round(position / STEP_SIZE)
End
// Gets the new knob position, bounded by NUM_POSITIONS, and wrapping around for a continuous knob
Function GetNewKnobPosition(increment : integer) returns integer
var newPos : integer = CurrentKnobPosition + increment
// Now, let's wrap the knob around
if newPos < 0 then
newPos = NUM_POSITIONS - 1
end
result = newPos % NUM_POSITIONS
//Print("GetNewKnobPos => increment: " + increment + " curr: " + CurrentKnobPosition + " newPos: " + newPos + " result: " + result)
End
// Sets the Knob Widget to one of the positions
Function SetKnobWidget(newPosition : integer)
// Set our current knob position and then the widget
// This cheats a little to even out the knob steps by setting in the middle
var middle : double = newPosition * STEP_SIZE + STEP_SIZE / 2
CurrentKnobPosition = newPosition
SetWidgetValue(VibCh_Knob, middle)
End
// Determines what the relative change is. Returns +1 or -1 regardless "speed" from relative input
Function GetRelativeChange(value: integer) returns integer
var newVal : integer = value - 64
if newVal < 0 then
result = -1
else
result = 1
end
End
Function SetInitialPosition()
var c : double = GetWidgetValue(VibCh_Knob)
CurrentKnobPosition = GetKnobPositionFromDouble(GetWidgetValue(VibCh_Knob))
SetKnobWidget(CurrentKnobPosition)
End
On Activate
SetInitialPosition()
End
On Variation(oldVariation: integer, newVariation: integer)
SetInitialPosition()
End
// Captures the MIDI change so that we can handle the relative thing and the "stepping"
// Make sure MIDI channel is set correctly
On ControlChangeEvent(c : ControlChangeMessage) Matching 74 from MainKeys
var change : integer = GetRelativeChange(GetCCValue(c))
var newKnobPos : integer
// This determines if we have turned enough to leave the "buffer zone"
if Abs(bufferzone_counter) <= BUFFERZONE then
bufferzone_counter = bufferzone_counter + change
else
// reset the buffer zone
bufferzone_counter = 0
// Get and set the knob position
newKnobPos = GetNewKnobPosition(change)
SetKnobWidget(newKnobPos)
end
End
On WidgetValueChanged(newValue : double) from VibCh_Knob
var newPosition : integer = GetKnobPositionFromDouble(newValue)
Print("Widgetchanged")
End
If someone wants to use this script, you will need to make sure you give a GPScript name to the MIDI IN block that has your encoder/knob and put that at the top (mine is “MainKeys”). Also, in the ControlChangeEvent, you will need to set the right CC value.
Stuff to improve later:
- Determine if I can simplify or make this easier to use by using a scriptlet
- Handle the manual changing of the on-screen widget better. Right now, you can move it on-screen, and it will work, but it won’t “snap” to the labels/positions until you change variation or rackspace.
1 Like
Thanks for your reply … I’m sure it will help others looking for a similar solution or as base for their own script.