Make a Widget not react to user interface

I am currently working on a script for keyswitching orchestral instruments.

The articulations can be switched via the keys from A to B in the lowest end of the keyboard.

I built three led buttons for each instrument, that work like radio-buttons and trigger those keys as well.
So now I can choose my articulations via the buttons and all will be stored in the Rackspace Variation.
Works like a charm.

The manual switching fron the keyboard still works, but those changes are not stored in the variation.
All as intended.

So far so good.

Then I wanted to have three LEDs underneath the buttons, to indicate which articulation is really active at the moment. I want to set their value only via script, when either one of the above buttons is pressed, or the keyswitch is pressed manually on the board.
When the user pushes on one of these LEDs nothing shall happen:

image

Would be nice to have some checkbox in the settings of each widget to make it unresponsive to user interaction.
Or at least there could be more callback functions than just “On WidgetValueChanged”, which is called in three situations:

  • Widget changed via User interface
  • Value changed via Script
  • Rackspace Variation chosen

Then I could handle this myself.

Any ideas?

Thanks in advance,
Markus

You could use a script something like this:

Var
   MIN : MidiInBlock
   nn  : Integer

on NoteEvent(n : NoteMessage) matching [A-1..B-1] from MIN
 nn = GetNoteNumber(n)

 SendNow(MIN,n)
 
 if nn == A-1 then
    //SetLED(A-1) on
    //SetLED(A#1) off
    //SetLED(B-1) off
 elsif nn == A#-1 then
     //SetLED(A-1) off
    //SetLED(A#1) on
    //SetLED(B-1) off
 else
    //SetLED(A-1) off
    //SetLED(A#1) off
    //SetLED(B-1) on 
 end   
 
end

It reacts on incoming Notes and sends them through.
In case of your keys to switch articulations (A-1…A#-1…B-1) it does additional things you define in code.

Thanks, pianopaul! :handshake:

In the meantime I found a tricky solution myself. Here is the code snipped just for the Oboe.
The declarations in the beginning and the callback functions at the end have to be repeated for every instrument. The functions in the middle are fully parameterized.
Works like a charm now! And it doesn’t bother me anymore, that the LED can be pressed manually. It’s just the same as if you press the keyswitch on the keyboard.
:blush:

// DO NOT EDIT THIS SECTION MANUALLY
Var
   OboeMidi : MidiInBlock
   OboeA : Widget
   OboeB : Widget
   OboeAis : Widget
   OboeLedB : Widget
   OboeLedAis : Widget
   OboeLedA : Widget
//$</AutoDeclare>


   OboeRadio : Widget Array
   OboeLed : widget Array
  

initialization
   var i : integer
   OboeRadio = [OboeA,OboeAis,OboeB] 
   OboeLed = [OboeLedA,OboeLedAis,OboeLedB]
end



// **** sends Note to MidiInBlock


Function SendKeySwitch (MidiBlock: MidiInBlock, Note: integer)
  var Message: MidiMessage   
  Message:= MakeNoteMessage(Note, 100)
  Print (Note)
  SendNow(MidiBlock,Message)   // send with low Velocity
  SendNow(MidiBlock,ReinterpretAsNoteOffMessage(Message)) //and Note Off
End


// ****  when RadioButton is pressed ****


Function SetRadio(MidiBlock: MidiInBlock, LedArray: Widget Array, RadioArray: Widget Array, StartKey: integer, Index : integer, Value : Double)
  var i: integer
 
  if Value>0 then  // has the button been activated?
    SetWidgetValue(LedArray[Index], 1) 
    for i = 0; i < Size(RadioArray); i = i + 1 do  // deactivate all others
      if not (i==Index) then
        SetWidgetValue(RadioArray[i], 0) 
        SetWidgetValue(LedArray[i], 0) 
      end
    end
  end
   
End


// ****  when an LED is activated ****


Function SetLed(MidiBlock: MidiInBlock, LedArray: Widget Array, StartKey: integer, Index : integer, Value : Double)
  var i: integer
 
  if Value>0 then  // has the LED been activated?
    SendKeySwitch (MidiBlock, StartKey+Index)    // send KeySwitch Note  
    for i = 0; i < Size(LedArray); i = i + 1 do  // deactivate all others
      if not (i==Index) then
        SetWidgetValue(LedArray[i], 0) 
      end
    end
  end
   
End

// ****  when a KeySwitch has been pressed manually on the keyboard ****

Function ManualKeyswitch(MidiBlock: MidiInBlock, LedArray: Widget Array, StartKey: integer, Note:NoteMessage)
    var Index: integer
    Index:=GetNoteNumber(Note)-StartKey 
    SetWidgetValue(LedArray[Index], 1)     
end





On WidgetValueChanged(val : double) from OboeA
   SetRadio(OboeMidi, OboeLed, OboeRadio, A-1, 0, val)
end

On WidgetValueChanged(val : double) from OboeAis
   SetRadio(OboeMidi, OboeLed, OboeRadio, A-1, 1, val)
end

On WidgetValueChanged(val : double) from OboeB
   SetRadio(OboeMidi, OboeLed, OboeRadio, A-1, 2, val)
end

On WidgetValueChanged(val : double) from OboeLedA
   SetLed(OboeMidi, OboeLed, A-1, 0, val)
end

On WidgetValueChanged(val : double) from OboeLedAis
   SetLed(OboeMidi, OboeLed, A-1, 1, val)
end

On WidgetValueChanged(val : double) from OboeLedB
   SetLed(OboeMidi, OboeLed, A-1, 2, val)
end

On NoteOnEvent(Note: NoteMessage) From OboeMidi
    if Note in [A-1..B-1] then
       ManualKeyswitch(OboeMidi, OboeLed, A-1, Note)
    else
       SendNow(OboeMidi, Note)
    end
end

But it seems that you can switch off the button that is on?

I would like to have a radio group for widgets, a bit like the widget group, but with a radio behavior. And if I remember well, I am not alone to wisch this… Perhaps, we should ask it again to our devs for Xmas?:stuck_out_tongue_winking_eye:

Yes, like those LED widget too, but they are switches, which makes things more complicated. And users can change them, which is difficult to prevent.

I would already be happy with a true On WidgetValueChanged, which would only be called when the widget value really changed. And I proposed to rename the current On WidgetValueChanged in On WidgetValueTouched… I could be also very nice for Xmas.

Especially when programming radio buttons, if you want to be scared, put a Print(“x called with value = “+value) in your callbacks :scream:

By the way this should also work for you:

On NoteOnEvent(Note: NoteMessage) matching [A-1..B-1] From OboeMidi
       ManualKeyswitch(OboeMidi, OboeLed, A-1, Note)
end

If it is your first GP script congrats and welcome to the club :wink:

Yes, but that does indeed make sense. In some situations I would like to have a rackspace variation, that does not change the current articulation at all. When I simply want to add some more instruments while playing.

But in general you are right. Radio buttons usually are not optional.

When the guys at deskew implement this “block widget for user interaction”-checkbox, it would be cool to have it seperated for “turning on” or “turning off”.

That was a good hint.

But I ran into another oddness with “On NoteOnEvent()”. As soon as I handle the NoteOn events, the system also stops the automatic passing on of NoteOff events. So I had to use the general callback “On NoteEvent()” and then pass on everything, that is not a keyswitch.

On NoteEvent(Note: NoteMessage) From OboeMidi
    if Note in [A-1..B-1] and IsNoteOn(Note) then
       ManualKeyswitch(OboeMidi, OboeLed, A-1, Note)
    else
       ParseNote(OboeMidi, Note, 7)
    end
end

And something else was tricky here. I trigger the instruments from Midi-In-Blocks, that channelmap the source channel 1 to different destination channels to trigger different instruments on my Kontakt plug-in. But this mapped channel information is not contained in the NoteMessage that comes with “On NoteEvent()”. Instead, when I passed the message on with SendNow, suddenly all Midi-In-Blocks sent on channel 1. Therefore I put this ParseNote-function in between.

function ParseNote(MidiBlock: MidiInBlock, Note:NoteMessage, Channel: integer)
   var NoteNumber: integer
       Velocity: integer
      
   SendNow(MidiBlock, MakeNoteMessageEx(GetNoteNumber(Note),GetVelocity(Note), Channel))
end

Using the matching syntax should do the job, what you are doing is not necessary. The note events that are not matching are passed.

Yes, that’s how it works. The scripting callbacks act just before the MIDI in blocks.

As @David-san points out, the scripting system is invoked first, the assumption being that if you are writing a script, you get full control over income data.
You can always query the MidiIn block, using GetParameter, to find the channel mappings defined by the plugin so you can match the if you need.

Nice suggestion, thanks :+1:

Can you provide more details please? If you are using the On NoteOn callback (as opposed to On Note) but you do not have an On NoteOff callback defined, then NoteOff messages should continue to go through. If they are not, something is wrong.

then NoteOff messages should continue to go through.

That was what I expected. But they did not. Very likely it was a mistake on my side. But there might as well be some bug in gpscript. Perhaps in combination with the channel mapping. I can do some testing with a more simple script tomorrow.

Thanks for the advice to query the mapping parameter from the Midi-In-Block. That’s quite useful.

Perhaps in combination with the channel mapping.

Reconsidering it now, I bet it happened, when the NoteOn Message was passed on to the wrong channel 1. Then the NoteOff events might have been let through on the right channel, but from there - of course - did not stop the notes on channel 1. So I thought, the NoteOff Messages have not been sent at all.

Very likely it was a mistake on my side.

At least here I was right… :sweat_smile:

1 Like