Octave-transpose with radio buttons


#1

I recently wrote a script (maybe not very elegant, but it works :wink: ) to realize a so called "radio-button"functionality. Some of you may still know those station buttons on old radios, where only one could be pressed at a time. I did that with LED-buttons.
In this example-rackspace i used this script to switch the transpose-setting for incoming notes by whole octaves. So there are seven buttons for a transposition of -3, -2, -1, 0, +1, +2, +3 octaves.
Maybe you have to insert a new MIDI-IN block, which then had to be named as “tp_midi_in” for the script to work properly.

Here is the link to the rackspace (you have to import it to your gig-file!):

EDIT: There was a faulty button-configuration in the rackspace which caused the buttons to flicker around and refuse to react - i uploaded a new one.
Sorry for any inconvenience

And this is the code (which also is embedded in the rackspace)… have fun. :sunglasses:

var 


bt_vs1 : widget
bt_vs2 : widget
bt_vs3 : widget
bt_vs4 : widget
bt_vs5 : widget
bt_vs6 : widget
bt_vs7 : widget

tp_midi_in : MidiInBlock

vs1_active : boolean
vs2_active : boolean
vs3_active : boolean
vs4_active : boolean
vs5_active : boolean
vs6_active : boolean
vs7_active : boolean

tp_amt : integer

Function set_tp (vs1 : double, vs2 : double, vs3 : double, vs4 : double, vs5 : double, vs6 : double, vs7 : double)
    SetWidgetValue(bt_vs1, vs1)
    if vs1 == 0 then
        vs1_active=false
    else
        vs1_active=true
    end

    SetWidgetValue(bt_vs2, vs2)
    if vs2 == 0 then
        vs2_active=false
    else
        vs2_active=true
    end

    SetWidgetValue(bt_vs3, vs3)
    if vs3 == 0 then
        vs3_active=false
    else
        vs3_active=true
    end
    
    SetWidgetValue(bt_vs4, vs4)
    if vs4 == 0 then
        vs4_active=false
    else
        vs4_active=true
    end
    
    SetWidgetValue(bt_vs5, vs5)
    if vs5 == 0 then
        vs5_active=false
    else
        vs5_active=true
    end

    SetWidgetValue(bt_vs6, vs6)
    if vs6 == 0 then
        vs6_active=false
    else
        vs6_active=true
    end

    SetWidgetValue(bt_vs7, vs7)
    if vs7 == 0 then
        vs7_active=false
    else
        vs7_active=true
    end

end

initialization
    vs1_active = false
    vs2_active = false
    vs3_active = false
    vs4_active = false
    vs5_active = false
    vs6_active = false
    vs7_active = false
end


On Activate// Called when rackspace is activated
   // SetWidgetValue (bt_vs4,1) //Button #4 is activated -> Transpose=0
End

//Called when a NoteOn or NoteOff message is received at some MidiIn block
//Note: this will NOT be called for NoteOn/NoteOff messages if you have explicit
//NoteOnEvent/NoteOffEvent respectively callbacks defined

On NoteEvent(m : NoteMessage) from tp_midi_in
    SendNow(tp_midi_in, Transpose(m,tp_amt))
End




// Called when a widget value has changed


On WidgetValueChanged(tp_val : double) from bt_vs1
    if tp_val == 1 And vs1_active == false then
       set_tp (1,0,0,0,0,0,0)
       tp_amt = -36
    end
    
    if tp_val == 0 And vs1_active == true then
        vs1_active = true
        SetWidgetValue(bt_vs1,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs2
    if tp_val == 1 And vs2_active == false then
       set_tp (0,1,0,0,0,0,0)
       tp_amt = -24
    end
    
    if tp_val == 0 And vs2_active == true then
        vs2_active = true
        SetWidgetValue(bt_vs2,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs3
    if tp_val == 1 And vs3_active == false then
       set_tp (0,0,1,0,0,0,0)
       tp_amt = -12
    end
    
    if tp_val == 0 And vs3_active == true then
        vs3_active = true
        SetWidgetValue(bt_vs3,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs4
    if tp_val == 1 And vs4_active == false then
       set_tp (0,0,0,1,0,0,0)
       tp_amt = 0
    end
    
    if tp_val == 0 And vs4_active == true then
        vs4_active = true
        SetWidgetValue(bt_vs4,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs5
    if tp_val == 1 And vs5_active == false then
       set_tp (0,0,0,0,1,0,0)
       tp_amt = 12
    end
    
    if tp_val == 0 And vs5_active == true then
        vs5_active = true
        SetWidgetValue(bt_vs5,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs6
    if tp_val == 1 And vs6_active == false then
       set_tp (0,0,0,0,0,1,0)
       tp_amt = 24
    end
    
    if tp_val == 0 And vs6_active == true then
        vs6_active = true
        SetWidgetValue(bt_vs6,1)
    end
end

On WidgetValueChanged(tp_val : double) from bt_vs7
    if tp_val == 1 And vs7_active == false then
       set_tp (0,0,0,0,0,0,1)
       tp_amt = 36
    end
    
    if tp_val == 0 And vs7_active == true then
        vs7_active = true
        SetWidgetValue(bt_vs7,1)
    end
end

Icon selection for Octave
#2

This is really useful. I’d like to make a few suggestions for improvement, mostly because I think everyone who uses GP Script will benefit

Item 1
It is never necessary to write
if active == true
You can simply write
if active

Item 2
Instead of

if vs1 == 0 then
        vs1_active=false
    else
        vs1_active=true
end

you can just write

vs1_active = vs1 > 0

Item 3

Noticing that there is a lot of duplication in the code, we can create functions to do the common work. We simply need to parameterize the widgets. So here is a complete rewrite of the original using functions. Study this by first looking at the Widget callbacks at the bottom and then follow the function calls.

var 

   // The actual widgets in the rackspace
   bt_vs1 : widget
   bt_vs2 : widget
   bt_vs3 : widget
   bt_vs4 : widget
   bt_vs5 : widget
   bt_vs6 : widget
   bt_vs7 : widget

   // We'll actually keep these in an array and just index them
   // That will allow us to use some functions that will work for all widgets
   radioButtons : Widget Array // Dynamic array
   vs_active : Boolean[7] // Version 2.4 doesn't support dynamic arrays for boolean (will be fixed in next update)

   tp_midi_in : MidiInBlock

   tp_amt : integer

initialization
    var i : integer
    
    tp_amt = 0 // Yeah, it's default but safer to do this
    
    radioButtons = [bt_vs1, bt_vs2, bt_vs3, bt_vs4, bt_vs5, bt_vs6, bt_vs7]
    
    // Not yet implemented - but coming in next update
    //vs_active = [false, false, false, false, false, false, false]
    // So for now just use a FOR loop
    for i = 0; i < 7; i = i + 1 do
        vs_active[i] = false
    end

end

// The array here indicates the value to which each widget should be set
Function UpdateAllWidgets (vs : Integer Array)    
    var i : integer
    
    for i = 0; i < 7; i = i + 1 do
        SetWidgetValue(radioButtons[i], vs[i])
        vs_active[i] = vs[i] > 0
    end    

end


On Activate// Called when rackspace is activated
   // SetWidgetValue (bt_vs4,1) //Button #4 is activated -> Transpose=0
End


On NoteEvent(m : NoteMessage) from tp_midi_in
    SendNow(tp_midi_in, Transpose(m,tp_amt))
End



// Called when any widget value has changed to do the actual work
Function ProcessAndSetTranspose(widgetIndex : integer, widgetValue : Double, transposeAmount : integer)
    var vs : integer array
    vs = [0,0,0,0,0,0,0] 
    
    if widgetValue == 1 and not vs_active[widgetIndex]
       then
          vs[widgetIndex] = 1 // This is the one we need to make active
          UpdateAllWidgets (vs)
          tp_amt = transposeAmount
    end
   
End

On WidgetValueChanged(tp_val : double) from bt_vs1
   ProcessAndSetTranspose(0, tp_val, -36)
end

On WidgetValueChanged(tp_val : double) from bt_vs2
   ProcessAndSetTranspose(1, tp_val, -24)
end

On WidgetValueChanged(tp_val : double) from bt_vs3
   ProcessAndSetTranspose(2, tp_val, -12)
end

On WidgetValueChanged(tp_val : double) from bt_vs4
   ProcessAndSetTranspose(3, tp_val, 0)
end

On WidgetValueChanged(tp_val : double) from bt_vs5
   ProcessAndSetTranspose(4, tp_val, 12)
end

On WidgetValueChanged(tp_val : double) from bt_vs6
   ProcessAndSetTranspose(5, tp_val, 24)
end

On WidgetValueChanged(tp_val : double) from bt_vs7
   ProcessAndSetTranspose(6, tp_val, 36)
end

#3

Thanks for your optimizations!
I have never seen a term like “vs1_active = vs1 > 0” and it seems a little bit cryptic at first glance. But nevertheless it’s also very logical.

Working with arrays is also something i have not done very often before, but it makes everything much more flexible and shorter in code.

But there is one issue i found with your script: It is now possible to switch a button from ON to OFF by pressing it twice. This should not work. The button had to stay ON.

But thanks again for your advice. I really appreciate it.
There is a lot of things i can learn from this. :+1:

EDIT
I changed the IF-statement in the function as follows:

Function ProcessAndSetTranspose(widgetIndex : integer, widgetValue : Double, transposeAmount : integer)
    var vs : integer array
    vs = [0,0,0,0,0,0,0] 
    
    if widgetValue == 1 and not vs_active[widgetIndex] or widgetValue==0 and vs_active[widgetIndex]
       then
          vs[widgetIndex] = 1 // This is the one we need to make active
          UpdateAllWidgets (vs)
          tp_amt = transposeAmount
    end
End

I was curious if it was possible to nest the constraints like this, but it works flawlessly.
Great! :slight_smile:


#4

I’m not sure what you mean by nested constraints.

I would however encourage you to use parentheses in the boolean expression, i.e.

if (widgetValue == 1 and not vs_active[widgetIndex]) or (widgetValue==0 and vs_active[widgetIndex])

While it is the case that or has lower precedence than and so that what you wrote will work, sometimes it’s never totally clear and parentheses make it more obvious.


#5

OK… it seems that “nesting” was the wrong word. I thought of something like “combining”.
I also didn’t know that it can be put in parenthesis, and doing this makes it easier to understand. What can i say? Thanks again. :slight_smile:


#6

Very useful, indeed. Thank you both.