Script tool to map midi to groove value(s)

Hi,

Before I start trying to script a tool, is it possible to use lua for mapping a midi cc to the groove settings?

Is it even possible to map midi to sliders in tools? I tried to midi map the slider in the tool below.

https://www.renoise.com/tools/groove-control

But when I open any tool, and hit midi map, the sliders on my tools are not available for mapping.

I hope someone can help me out.

1 Like

I have yet to add the global groove control in my MUC tool, therefore this matter interests me.

That said, you can add the midi mapping to any control object with midi_mapping (a string). Here’s an example for a button:

  vb:button{
    id="MUC_BT_EXTRA_DEV_REM",
    active=false,
    height=MUC.HEIGHT_2,
    width=MUC.HEIGHT_2,
    bitmap="ico/minus_ico.png",
    pressed=function() muc_bt_extra_dev_rem_repeat() end,   
    released=function() muc_bt_extra_dev_rem_repeat(true) end,
    midi_mapping=("Tools:%s:Button:Bottom (-) [Trigger]"):format(muc_abbreviated_title),
  },

Read the 164 to 224 lines of Renoise.ScriptingTool.API.lua for more information to “midi_mappings”…

Here’s an example for the same midi link as the previous button to appear in Renoise’s MIDI MAP window (see name):

  renoise.tool():add_midi_mapping{
    name=("Tools:%s:Button:Bottom (-) [Trigger]"):format(muc_abbreviated_title),
    invoke=function(message)
      if message:is_trigger() then
        if (MUC.ON_OFF) then
          vws.MUC_BT_EXTRA_DEV_REM.color=MUC_CLR.MARKER
          return muc_bt_extra_dev_rem_repeat(false), muc_view_panel_area(vws.MUC_PP_LIST_1.value)
        end
      else
        vws.MUC_BT_EXTRA_DEV_REM.color=MUC_CLR.DEFAULT
        return muc_bt_extra_dev_rem_repeat(true)
      end
    end
  }

“midi_mapping” and “name” must be the same string.

1 Like

Thanks Raul for taking the time pointing me in the right direction. I appreciate it a lot man!

   self.__vb:row {
      margin = 4,
      self.__vb:slider {
        value = renoise.song().transport.groove_amounts[1],
        midi_mapping = ("Tools:GrooveControl:Slider"),
        notifier = function(value)
           --round the value
           value = round(value)
           --fill table with the same vales fo all 4 sliders
           local groove_table = {value,value,value,value}
           --update the renoise groove sliders
           renoise.song().transport.groove_amounts = groove_table 
           --update the text
           self.__vb.views["text"].text = aligned_precentage(value)
           --update hex text
           self.__vb.views["hex_text"].text = " 16th Note Delay: "..convert_pc_to_sixteenth_delay()
           --update Linn text
           self.__vb.views["Linn_text"].text = " Drum Machine: "..convert_renoise_pc_to_Linn_pc()
           --focus the pattern editor
           focus_pattern_ed() 
         end
        },

I’ve added the midi_mapping part, I can now select the slider when I hit midi map in Renoise, but after that I can’t change the value. So the dial seems to be mapped, but it’s not communicating.

I probably also need to add it to the Mapping window with your second code, but I’m not sure where to put it.

renoise.tool():add_midi_mapping{
    name=("Tools:GrooveControl:Slider"),
    invoke=function(message)
    end
}

Yes, at the end of your main.lua file would be a good place. Remember that you must define the function for invoke…

Thanks and Yeah I was affraid that the function needed to be defined, because I got no errors, but nothing happened when I turned the dial iether.

I have a endless rotary dial which I want to connect to the tool’s slider, I don’t know exactly how this function should look like then. Well, maybe tomorrow :slight_smile:

I thought that the function only needed to contain code for the moving of the slider, just the gui. But now I see that this is not the case, or partly the case.

What function should I write? My guess is that I don’t need to setup the midi communication or do i ?

I have written this directly here. I haven’t tested it but it should work. There is no single way to do this.
In short, you have to relate your object (slider) to the function that you invoke from add_midi_mapping.
As for the notifier function of your slider, you must define the function according to what you want to do with those values which returns the slider.

-- at the beginning of your main.lua
local vb=renoise.ViewBuilder()
local vws=vb.views
local rnt=renoise.tool()

--inside the code that defines your GUI
vb:slider{
  id="MY_SLIDER_01",
  height=19,
  width=199,
  min=0,
  max=100,
  value=100,
  notifier=function(value)  --[[ bla  bla bla ]]  end,
  midi_mapping="Tools:MY_TOOL:Slider 01",
  tooltip="bla bla bla"
}
--at the end of your main.lua
rnt:add_midi_mapping{
  name="Tools:MY_TOOL:Slider 01",
  invoke=function(message)
    local val_min=vws.MY_SLIDER_01.min
    local val_max=vws.MY_SLIDER_01.max
    local val_med=val_max-val_min
    if message:is_abs_value() then
      local val=val_min+message.int_value*val_med/127
      if (val<val_min) then
        vws.MY_SLIDER_01.value=val_min
      elseif (val>val_max) then
        vws.MY_SLIDER_01.value=val_max
      else
        vws.MY_SLIDER_01.value=val
      end
    elseif message:is_rel_value() then
      local increment=1
      if (message.int_value<=-1) then
        if (val_val-increment<val_min) then
          vws.MY_SLIDER_01.value=val_min
        elseif (val_val-increment<=val_max) then
          vws.MY_SLIDER_01.value=val_val-increment
        end
      elseif (message.int_value>=1) then
        if (val_val+increment>val_max) then
          vws.MY_SLIDER_01.value=val_max
        else
          vws.MY_SLIDER_01.value=val_val+increment
        end
      end
    end
  end
}

Do not use a “class” if not necessary (self.__ bla bla bla ). For example, a class is suitable for defining a specific object (a slider, a button…) that you can repeat countless times inside your GUI.

It looks like you are copying code from other programmers. I suggest you don’t do that. You must understand what each line of code means, even each word, every each sign. A good tip is to write the code yourself, and use trial and error. Copying code can be useful if you know exactly what you are buying.

I recognize that the API documentation can be overwhelming and confusing in many areas. You also have to “understand” it from a programmer’s point of view. You will learn by writing yourself.

Thanks for motivating me to start a new script myself. Yes I copied all of this, but I don’t need all the things that are in the script right now. I always hope that there is a shortcut, because I’d rather be making music :slight_smile: But tonight I’ll start with my own script and see how far I get.

Thanks again for your patience!

1 Like

:+1:

I’ve managed to get the midi mapping working, by starting a new script by myself. I figured I didn’t need the gui at all, so now a midi mapping for groove gets added everytime I start renoise and thats it :slight_smile: Great.

Now I have one rotary dial, which is a special one: When I turn it left it outputs 126, 127 and so and when I turn it right it outputs 1, 2 ,3. I think you can call it a nudge dial.

This is exactly the rotary I want to use for the groove setting, so now I’m trying to make a scripts that adds 0.1 when the incoming midi value is 0 and substracts 0.1 when the incoming midi message is 127.

Now my question: How can I get the current groove amount? I’ve read about the observable method, but that method is only triggered when the groove changes.

I would like a method to get the value of the current groove setting in my variable, so I can substract or add 0.1 from that amount. That would be great. Is that possible?

1 Like

Sorry for bothering, found it transport.groove_amounts[1]

1 Like

hi spdk
midi controls for groove settings has been in Paketti for a while.

--Groove Settings, control Grooves with a slider
renoise.tool():add_midi_mapping{name = "Paketti:Groove Settings Groove #1 x[Knob]",
  invoke=function(midi_message)
  local ga=renoise.song().transport.groove_amounts
    if not renoise.song().transport.groove_enabled then renoise.song().transport.groove_enabled=true end
    renoise.app().window.active_lower_frame=1
    renoise.song().transport.groove_amounts = {midi_message.int_value/127, ga[2], ga[3], ga[4]}
    end}

renoise.tool():add_midi_mapping{name = "Paketti:Groove Settings Groove #2 x[Knob]",
  invoke=function(midi_message)
  local ga=renoise.song().transport.groove_amounts
    if not renoise.song().transport.groove_enabled then renoise.song().transport.groove_enabled=true end    
    renoise.app().window.active_lower_frame=1
    renoise.song().transport.groove_amounts = {ga[1], midi_message.int_value/127, ga[3], ga[4]}
    end}

renoise.tool():add_midi_mapping{name = "Paketti:Groove Settings Groove #3 x[Knob]",
  invoke=function(midi_message)
  local ga=renoise.song().transport.groove_amounts
    if not renoise.song().transport.groove_enabled then renoise.song().transport.groove_enabled=true end
    renoise.app().window.active_lower_frame=1
    renoise.song().transport.groove_amounts = {ga[1], ga[2], midi_message.int_value/127, ga[4]}
    end}

renoise.tool():add_midi_mapping{name = "Paketti:Groove Settings Groove #4 x[Knob]",
  invoke=function(midi_message)
  local ga=renoise.song().transport.groove_amounts
    if not renoise.song().transport.groove_enabled then renoise.song().transport.groove_enabled=true end
    renoise.app().window.active_lower_frame=1
    renoise.song().transport.groove_amounts = {ga[1], ga[2], ga[3], midi_message.int_value/127}
    end}

it’s really quite neat to use 4 sliders to control these things.