[Solved] Help: button with MIDI routing running twice same function

I come from here (a Button with MIDI Routing):

https://forum.renoise.com/t/solved-help-code-for-midi-routing-of-a-single-button-of-a-tool/46967

vb:button {
          color = { 0x66,0x00,0x00 },
          text = 'CLM',
          id = '33',
          tooltip = '33: Clean all Parameter Columns all Tracks inside selected Group.\n[MOUSE CLICK]',
          midi_mapping = 'Tools:GT16-Colors:Clean All values in Track [Trigger]', <---------LOOK HERE
          height = 24,
          width = 40,
          notifier = function()tool_01_33() end
        },
 
 
--------------------------------------------------------------------------------
-- MIDI/KEY MAPPING
--------------------------------------------------------------------------------
 
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors:Clean All values in Track [Trigger]', <---------LOOK HERE
  invoke = function()tool_01_33() end
}

My problem is the following:

When you press and drop the MIDI keyboard button, the function is executed twice:

  1. Once the button is pressed
  2. Again when drop the button

Is there a way to execute the function once, just by pressing, ignoring the drop?

What I think happens is that the button gives two MIDI orders, one to press and another to drop.I only want to run the function once, when I press the button.

What code is needed?I have 45 buttons with this condition…

I want to perform functions like “collapse/expand selected track”, undo, redo… which obviously should not be repeated.

Can you help me?

Thanks!

I’ve been browsing the Duplex tool. In “UIButton.lua” archive, seems to differentiate different behaviors for the button:

Button, PushButton, ToggleButton… I copy part of the code…

--------------------------------------------------------------------------------

--- Add event listeners to the button
-- DEVICE_EVENT.BUTTON_PRESSED
-- DEVICE_EVENT.BUTTON_RELEASED
-- DEVICE_EVENT.VALUE_CHANGED
-- DEVICE_EVENT.BUTTON_HELD
-- @see Duplex.UIComponent.add_listeners

function UIButton:add_listeners()
  TRACE("UIButton:add_listeners()")

  self:remove_listeners()

  if self.on_press then
    self.app.display.device.message_stream:add_listener(
      self, DEVICE_EVENT.BUTTON_PRESSED,
      function(msg) return self:do_press(msg) end )
  end

  if self.on_release then
    self.app.display.device.message_stream:add_listener(
      self, DEVICE_EVENT.BUTTON_RELEASED,
      function(msg) return self:do_release(msg) end )
  end

  if self.on_hold then
    self.app.display.device.message_stream:add_listener(
      self,DEVICE_EVENT.BUTTON_HELD,
      function(msg) self:do_hold(msg) end )
  end

  if self.on_change then
    self.app.display.device.message_stream:add_listener(
      self,DEVICE_EVENT.VALUE_CHANGED,
      function(msg) return self:do_change(msg) end )  
  end

end

--------------------------------------------------------------------------------

--- Remove previously attached event listeners
-- @see Duplex.UIComponent

function UIButton:remove_listeners()
  TRACE("UIButton:remove_listeners()")

  self.app.display.device.message_stream:remove_listener(
    self,DEVICE_EVENT.BUTTON_PRESSED)

  self.app.display.device.message_stream:remove_listener(
    self,DEVICE_EVENT.BUTTON_RELEASED)

  self.app.display.device.message_stream:remove_listener(
    self,DEVICE_EVENT.VALUE_CHANGED)

  self.app.display.device.message_stream:remove_listener(
    self,DEVICE_EVENT.BUTTON_HELD)

end

“Pressed”, “released”… I do not know if it has direct relation, but it is clear that I lack some code to rectify the behavior of the button using MIDI.This is new for me.

Please, help me!I’m stuck right now with my code.

Raul I’m really really no expert on this but isn’t there a midi message coming in with your defined function?

For example:

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors:Clean All values in Track [Trigger]', <---------LOOK HERE

  invoke = function(midimessage)
    if midimessage:is_trigger() then
      print("Triggered!!") --This might happen only once?
    end
  end
}

Raul I’m really really no expert on this but isn’t there a midi message coming in with your defined function?

For example:

renoise.tool():add_midi_mapping {
name = 'Tools:GT16-Colors:Clean All values in Track [Trigger]', <---------LOOK HERE

invoke = function(midimessage)
if midimessage:is_trigger() then
print("Triggered!!") --This might happen only once?
end
end
}

I think that’s the way. I will try to implement.I already saw the condition… if X then Y bla bla bla

But being new, it is necessary to include the exact code for it to work.

When operating on a button, the rest of the buttons are repeated with other different functions.

This must be nonsense, but there is not much information regarding MIDI code…

Thank you! if I find the solution, I will post it here.

SOLVED:

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:4A XXX [Trigger]',
  --invoke = function()tool_02_CH01_4A() end
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_4A()
    end
  end
}

Is to remove:

invoke = function()tool_02_CH01_4A() end

and add:

invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_4A()
    end
  end

I had problems with the parentheses… Thank you very much for the help 4Tey!

Example (work):

--------------------------------------------------------------------------------
-- MIDI/KEY MAPPING
--------------------------------------------------------------------------------
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:1A XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_1A()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:2A XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_2A()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:3A XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_3A()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:4A XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_4A()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:5B XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_5B()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_6B()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:7B XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_7B()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:8B XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_8B()
    end
  end
}
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:9M XXX [Trigger]',
  invoke = function(message)
    if (message:is_trigger()) then
      tool_02_CH01_9M()
    end
  end
}

There is not much information regarding MIDI code…

I saw you posted some code from Duplex. But built-in mappings work differently - documentation is here:
https://github.com/renoise/xrnx/blob/master/GlobalMidiActions.lua

I saw you posted some code from Duplex. But built-in mappings work differently - documentation is here:
https://github.com/renoise/xrnx/blob/master/GlobalMidiActions.lua

Thanks Danoise!

I will keep this file always present.

I’m going to play several things MIDI code in these days for my tool (GT16-Colors), so it’s very possible that I ask a lot of related things and I’ll ask for help, insurance!

I need to practice the code. To get started:

if (message:is_trigger()) then

What would the condition be like when you release the button?

if (message:is_release()) then --???

Sometimes I invoke logic, but it does not always work! :slight_smile:

What would the condition be like when you release the button?

if (message:is_release()) then --???

I suppose you may be able to use the message:is_switch() function Raul?

Something like:

invoke = function(midimessage)
  if midimessage:is_switch() then
    if midimessage.boolean_value then
      print("On!!") --This executed when switch is pressed
    else
      print("Off!!") --This executed when switch is released
    end
  end  
end

I suppose you may be able to use the message:is_switch() function Raul?

Something like:

invoke = function(midimessage)
if midimessage:is_switch() then
if midimessage.boolean_value then
print("On!!") --This executed when switch is pressed
else
print("Off!!") --This executed when switch is released
end
end  
end

I suppose you also have to make sure that this is set when mapping:

attachicon.gifgate.png

OK, Thanks 4Tey!

To press button (Work!):

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if message.boolean_value then
      tool_02_CH01_6B()
    end
  end
}

To release button (Work!):

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if message.boolean_value then
    else
      tool_02_CH01_6B()
    end
  end
}

To press & release button (Work! two functions):

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if message.boolean_value then
      tool_02_CH01_6B()
    else
      tool_02_CH01_7B()
    end
  end
}

To hold the button pressed (I lack!):

Another question! Now I only need to repeat the same function several times by holding down the button.Is there any way to do this? Per example, the functioncould be “jump the next pattern”:

  1. Pressing once will only execute the function once (the first case).
  2. Holding down the button , execute repeatedly the same function.It stops when the button is released.This is possible?Any speed regulators, too?This would be wonderful!

Any ideas?

Yes, I suppose it is possible Raul to simulate a ‘keyboard repeat’ system as you know when the key (button) is pressed and released. I’d be thinking to use a timer callback and flag mechanism maybe?

N.B. That message:is_switch() if is important Raul as you can see from the docs that danoise linked (message.boolean_value tends to be only valid when is_switch() returns true):

renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if message:is_switch() then -- this line is important :)
      if message.boolean_value then
        tool_02_CH01_6B()
      else
        tool_02_CH01_7B()
      end
    end
  end
}
From docs:
31 -- valid [true OR false] when :is_switch() returns true, else nil
32 boolean_value

Yes, I suppose it is possible Raul to simulate a ‘keyboard repeat’ system as you know when the key (button) is pressed and released. I’d be thinking to use a timer callback and flag mechanism maybe?

N.B. That message:is_switch() if is important Raul as you can see from the docs that danoise linked (message.boolean_value tends to be only valid when is_switch() returns true):

renoise.tool():add_midi_mapping {
name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
invoke = function(message)
if message:is_switch() then -- this line is important :)
if message.boolean_value then
tool_02_CH01_6B()
else
tool_02_CH01_7B()
end
end
end
}
From docs:
31 -- valid [true OR false] when :is_switch() returns true, else nil
32 boolean_value

Yes, yesterday i tried (trial and error) all the functions without the codition:

if message:is_switch() then

And works perfectly! However, if necessary, I will include it.

On the other hand, what I intend is something like this:

renoise.tool():add_midi_mapping {.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors AMIC:Channel 01:6B XXX [Trigger]',
  invoke = function(message)
    if midimessage:is_switch() then
      if "you hold down the button ... Repeat every half second this function" then
        tool_02_CH01_6B()
      end
    end
  end
}

Be able to assign the function repeat timeor something similar. Have control in the code (this already sounds more complicated).

On the other hand, I would also like to control the following:

  1. Illuminate or highlight the associated button inside the tool, pressing from the MIDI keyboard, just like the mouse pointer.This is a visual location aid.Illumination shuts off when you release the button…

All these things are directly related.Any ideas?

Thanks!

I’m probably not the right person to ask Raul about the in’s and out’s of Renoise lua. I tend to just hack my way through things. But because I’m such a nice fellow (irrespective of what danoise thinks :wink: ) I’ve hacked together a silly ‘tool’ that tries to demo what you are kinda looking for that you can study (but don’t take too literally, it isn’t exactly a work of programming art) and maybe get some ideas from :slight_smile:

I’m probably not the right person to ask Raul about the in’s and out’s of Renoise lua. I tend to just hack my way through things. But because I’m such a nice fellow (irrespective of what danoise thinks :wink: ) I’ve hacked together a silly ‘tool’ that tries to demo what you are kinda looking for that you can study (but don’t take too literally, it isn’t exactly a work of programming art) and maybe get some ideas from :slight_smile:

Oohh, I like this.I’ll try to study, see if I can apply it.This is part of VoiceRunner?

Thanks!

OK, the subject of highlighting the color is solved…I use class ‘??’ to organize my code.I have been able to add the correct illumination of the button when I press and release.

renoise.tool():add_midi_mapping {
  name = MR.CH01_6B,
  invoke = function(message)
    if message:is_switch() then
      if message.boolean_value then
        amic_CH01_6B() --function to pressing. How to repeat???????
        vb.views.CH01_6B.color = CR.ROW01_F --OK! (pressing)
      else
        vb.views.CH01_6B.color = CR.ROW01_Y --OK! (release)
      end
    end
  end
}
  • amic_CH01_6B() is the function
  • CH01_6B is the “id” of button
  • CR.ROW01_F is the focused (highlighted) color of button {0x8F,0x3C,0xB9}
  • CR.ROW01_Y is the normal color of button {0x5F,0x0C,0x89}

All work fine!

However,I do not understand the code regarding time. I’ve tried to implement it and it does not work:

What is this?

local buttonheldtimer = 250 --???
local midinxtbuttondown = false
local midiprvbuttondown = false

function timercallback()
  if (not dialog or not dialog.visible) then
      if (renoise.tool():has_timer(timercallback)) then
        renoise.tool():remove_timer(timercallback)
      end
    return
  end

  if midinxtbuttondown == true then
    vb.views.plugintext.text = getnextplug()
  end

  if midiprvbuttondown == true then
    vb.views.plugintext.text = getprevplug()
  end
end

????...

  if not (renoise.tool():has_timer(timercallback)) then
    renoise.tool():add_timer(timercallback,buttonheldtimer)
  end

  if (renoise.tool():has_timer(timercallback)) then
    renoise.tool():remove_timer(timercallback)
  end

Does this code force the button to hold down XXX millisecondseven if the user releases? If so, this is something other than my request.It’s okay to show off the snack notices ^_^.It is also interesting.

But I want is to repeat a function for a while. for example, 2 seconds (the 2 seconds with the button pressed)…

Or put another way: for 2 seconds (2000 millisecons) of pressed time, repeat the X function 20 times, 1 time every 100 milliseconds.This makes sense?There must be some form of repetition…

It's okay to show off

I don’t have the talent to show off. Is this showing off? -> https://forum.renoise.com/t/new-tool-3-1-gt16-colors-v1-2a1-updated-12-june-2017/46473

Is Bach showing off here with the BWV 542? -> https://youtu.be/8y8mlNOftv0?t=332

Does this code force the button to hold down XXX milliseconds even if the user releases? If so, this is something other than my request.

Well if it is something other than what you requested I can’t do anything about that Raul. When I tried learning a little bit of programming (and it is only a little bit) it was with a Commodore 64 and a very old tatty book that was written for the Commodore PET. You couldn’t just log on to the internet and start requesting/asking/shouting for people to drop everything and give pristine example code that is tailored made. Now listen to the Bach.

PS. If you’re a perfectionist and want good highlighting of buttons, you can use RGB->HSL->RGB conversion to modify the color. L changes the lightness (which is not the same as increasing R, G and B by the same amount). cLib has some helper functions for this in cColor.lua (iirc).

It's okay to show off

I don’t have the talent to show off. Is this showing off? → https://forum.renoise.com/t/new-tool-3-1-gt16-colors-v1-2a1-updated-12-june-2017/46473

Is Bach showing off here with the BWV 542? → https://youtu.be/8y8mlNOftv0?t=332

Does this code force the button to hold down XXX milliseconds even if the user releases? If so, this is something other than my request.

Well if it is something other than what you requested I can’t do anything about that Raul. When I tried learning a little bit of programming (and it is only a little bit) it was with a Commodore 64 and a very old tatty book that was written for the Commodore PET. You couldn’t just log on to the internet and start requesting/asking/shouting for people to drop everything and give pristine example code that is tailored made. Now listen to the Bach.

^_^I laughed watching the video.

By the way, GT16-Colors is the tool with which I am working.All this code is for him.I do not know if I mentioned it before.

PS. If you’re a perfectionist and want good highlighting of buttons, you can use RGB->HSL->RGB conversion to modify the color. L changes the lightness (which is not the same as increasing R, G and B by the same amount). cLib has some helper functions for this in cColor.lua (iirc).

I will see all this in depth. To begin with, it seems a little complicated.

By the way, maybe some function type loop with “repeat” and “break” serve for what I seek.This is more convoluted…

I want to do something like: Repeat this function several times while the button is still pressed.

Thanks!!!

I’m just a little curious Raul. Did you actually run my silly ‘SelectPlugin’ example and midi map errr midi keyboard notes to the Next Plugin/Prev Plugin buttons (with the gate (switch on and off) option)? (BTW it assumes you already have a list of plugins installed.) If it does work, can you hold the midi keyboard note down that you have mapped and it will cycle through the plugins every quarter of a second?

I’m just a little curious Raul. Did you actually run my silly ‘SelectPlugin’ example and midi map errr midi keyboard notes to the Next Plugin/Prev Plugin buttons (with the gate (switch on and off) option)? (BTW it assumes you already have a list of plugins installed.) If it does work, can you hold the midi keyboard note down that you have mapped and it will cycle through the plugins every quarter of a second?

Yes, I tried it, but it did not work. An error appears in the terminal. Maybe related to a plugin.I’ve tried it with several installed plugins…

The subject of highlighting the color is solved (I have yet to look at the information provided by Joule, that seems better).The theme of time and repeat a function is more complicated.I have it parked.