Synchronize a slider with values of the pattern editor...

I am trying to build a minislider that is synchronized with a specific parameter of the pattern editor, but with the ability to set the slider to 0 "with double click, and it returns a specific value, such as 255. Anyone who has tried it You will know what it is about.

A slider or mini-slip does not have a restart property, which obeys the double click of the mouse. With the double click of the mouse it always returns 0.This is the main problem.

A concrete case:

For example, I use a line timer, to be able to read the volume value of the note, within a specific note column. The valid range is from 0 to 128 for note_value, 255 for vacuum.Also, use the notifier of the slider to modify the note_value in the pattern editor. That is to say:

  1. line timer forpattern editor to update the slider.
  2. notifier for slider to update the pattern editor.

…is bidireccional.

But, interesting to move the value a unitto release the 0 value of the slider, so that it can take the volume_value = 255. A scheme:

slider (value): 0, 1, 2,3,4,5,6…128

pattern editor (volume_value) 255, 0,1,2,3,4,5…127

With the double click of the slider, it would take 255 in pattern editor.

with the 1 value of the slider, it would take 0 in pattern editor.

Can you think of some way to synchronize it (pattern editor (volume_value) with slider (value)) and not to return any errors?It must comply: with the double click, the slider is set to 0, making the volume_value worth 255.

You will need to do some value filtering.

For my Note Properties tool I have used functions like this:
The function takes a renoise note_column object and returns the legitimate volume value for a rotary (could be minislider)

local cur_note = renoise.song().patterns[x].tracks[y].lines[z]:note_column(index)

You can modify to suit how you want 255 to behave too. Also you will need to adjust the final return value by +1, if 255 will be 0 and other values are offset, as you explained above.

--------------------------------------------------
--Converts pattern volume data to GUI rotary range
--------------------------------------------------
local function volume_to_rotary(cur_note)
  --return early if on fx track
  if cur_note == nil then
    return
  end
  --bounds checking as other non volume commands can be in the column
  if cur_note.volume_value > 255 then
    return 127 --maximum (volume default)
  end
  --range of rotary is 127 but renoise returns 255 when volume
  --column is empty so we need to change it back to 127 to fit the rotary range
  if cur_note.volume_value == 255 then
    return 127 -------Raul return 0 HERE-----
  else
    return cur_note.volume_value --------Raul ADD +1 HERE----------------
  end
end

You do similar for the opposite direction (minislider to pattern editor), adjusting the if statements and return values accordingly.

Also you need to remember to set a global flag in the timer when it is running:

--declare a global flag
timer_running = false
 
function timer_function()
  --set the global flag to true
  timer_running = true

  ---your code to update minislider here
    
  
   --set the global flag to false as timer is finished
  timer_running = false
 
 end

So you can stop the timer from triggering your minislider notifier when you update it e.g. vb.views[“minislider 1”].value = x

At the beginning of your minislider notifier you put:

if timer_running == true then
  return
end

This stops the minislider from writing back to the pattern in an unwanted internal feedback between the timer and notifier functions.

Thanks Ledger!

I had a similar approach, but I did not get it to work properly. I returned errors. Then I deduced that it was necessary to add math.floor in the value of the mini-slip to always return whole numbers.

Also, the mess I was having is that I’m working with an updateable pattern buffer when necessary, and all the data I get out of there.One question I have is, is it better to use a table and avoid mathematical operations (basically add or subtract). I do not know if it is faster to consult a table of 129 values (whole numbers) than to add an addition or subtraction in an equality, thinking that it is inside a timer and that it affects many lines, at least 64, just like me I have it configured at most.

In the end, I chose to use a table like this:

PRE_VOL_PAN_VAL = {
  255,0,1,2,3,4,5,6,7,8,9,10,
  11,12,13,14,15,16,17,18,19,20,
  21,22,23,24,25,26,27,28,29,30,
  31,32,33,34,35,36,37,38,39,40,
  41,42,43,44,45,46,47,48,49,50,
  51,52,53,54,55,56,57,58,59,60,
  61,62,63,64,65,66,67,68,69,70,
  71,72,73,74,75,76,77,78,79,80,
  81,82,83,84,85,86,87,88,89,90,
  91,92,93,94,95,96,97,98,99,100,
  101,102,103,104,105,106,107,108,109,110,
  111,112,113,114,115,116,117,118,119,120,
  121,122,123,124,125,126,127
}

The timer executes this:

PRE_BYPASS_VOL = false

   ...
      ...
        if ( vws.PRE_VOL.visible ) then
          PRE_BYPASS_VOL = true
          local slot_vol = vws[("PRE_SLOT_VOL_%s"):format( lns )]
          local vfd_vol_val = vws[("PRE_VFD_VAL_VOL_%s"):format( lns )]
          local vol_val = PRE_BUFFER_PTT_TRK[lns]["note_columns"][snci]["volume_value"] --line:note_column(snci).volume_value
          if ( vol_val > 127 ) then
            slot_vol.value = 1
            vfd_vol_val.value = 1
          else
            slot_vol.value = vol_val +2
            vfd_vol_val.value = vol_val +2
          end
          PRE_BYPASS_VOL = false
        end

I use a "bypass"to avoid running the minislider’s notifier from the timer that reads the pattern (my pattern buffer).

And the minislider:

class "Pre_Show_PR_Vol"
  function Pre_Show_PR_Vol:__init( lns )
    M_ROW_VOL = vb:row { id = "M_ROW_VOL_"..lns, spacing = -3,
      vb:space { height = 15 },
      vb:row { spacing = -4,
        id = ("PRE_SLD_VAL_VOL_%s"):format( lns ),
        visible = false,
        vb:row { spacing = -3,
          vb:bitmap {
            active = false,
            height = 15,
            mode = "body_color",
            bitmap = "ico/v_ico.png"
          },
          vb:minislider {
            id = ("PRE_SLOT_VOL_%s"):format( lns ),
            height = 15,
            width = 79,
            min = 1,
            max = 129,
            value = 1,
            notifier = function( value ) pre_sld_val_vol( math.floor(value), lns ) end
          }
        },
        vb:valuefield {
          id = ("PRE_VFD_VAL_VOL_%s"):format( lns ),
          height = 15,
          width = 21,
          align = "center",
          min = 1,
          max = 129,
          value = 1,
          tostring = function( value ) if ( value ~= 1 ) then return ("%.2X"):format( tostring( PRE_VOL_PAN_VAL[value] ) ) else return "--" end end,
          tonumber = function( value ) return tonumber( value +2, 16 ) end,
          notifier = function( value ) if ( vws[("PRE_SLOT_VOL_%s"):format( lns )].value ~= value ) then vws[("PRE_SLOT_VOL_%s"):format( lns )].value = value end end
        }
      }
    }
    self.cnt = M_ROW_VOL
  end

Although I use a table, it is also necessary to adjust the values by +2. I have tried it and the performance is optimal and the bypass works perfectly.I was afraid that this issue would worsen the timer’s performance.This table would also be worth panning. For delay it is not necessary.Another problem that I have found is that, if the track uses concrete effects in the effects columns linked with the effect chains, the value 0 of the volume_value is returned as a 4-digit number. Then the minislider will not return 0, because it depends on a condition <= 127.As long as the user does not use effects that affect the volume, the sliders will update correctly.I really do not know how to solve that, and I do not want to overload the functions either.

I understand that access to a type table of 129 values: PRE_VOL_PAN_VAL[2] it’s instantaneous.It is not like this? I mean, if the table has 10,000 values, will it take the same time to access the second position?

If you want to find out what is most efficient you can run a test with operating system clock, to see the time elapsed to run your code. (It is run many multiple times in a loop to get to the scale of seconds)

os.clock()

In the following code Just change the s = s + i for the code that you want to test. Then run the code that you are testing it against in the same loop afterwards.

Note: It may be worth reducing the loop limit from 200000000 down to 1000000 or so, to stop renoise freezing up for too long if code tested is more complex.

local start_time = os.clock() --get current time
local s = 0 --counter

--loop
for i=1,200000000 do 
  s = s + i 
end

--print current time minus original time start_time
print(string.format("elapsed time: %.2f\n", os.clock() - start_time))
--printed result depends on individual system performance
>>elapsed time: 1.82

Then the minislider will not return 0, because it depends on a condition <= 127.As long as the user does not use effects that affect the volume, the sliders will update correctly.I really do not know how to solve that, and I do not want to overload the functions either.

Can you not just filter these results and set to a default 255 or 0?

I’ll have to isolate each case outside the timer to use os.clock () and see which case is slowest.

Can you not just filter these results and set to a default 255 or 0?

Here I had an absurd confusion.It turns out that I confused the character of 0 0 with O 0 (letter) in the volume column. Ox serves to fade out:http://tutorials.renoise.com/wiki/Effect_Commands#Volume_Column

If there is any effect in the volume column, I assign it “255” in the slider, and the value appears as “–”. I think it’s better that the function ignores all these cases.

I put this issue as resolved. Thanks for all the help!

Just out of interest I ran the following test with your table

PRE_VOL_PAN_VAL = {
      255,0,1,2,3,4,5,6,7,8,9,10,
      11,12,13,14,15,16,17,18,19,20,
      21,22,23,24,25,26,27,28,29,30,
      31,32,33,34,35,36,37,38,39,40,
      41,42,43,44,45,46,47,48,49,50,
      51,52,53,54,55,56,57,58,59,60,
      61,62,63,64,65,66,67,68,69,70,
      71,72,73,74,75,76,77,78,79,80,
      81,82,83,84,85,86,87,88,89,90,
      91,92,93,94,95,96,97,98,99,100,
      101,102,103,104,105,106,107,108,109,110,
      111,112,113,114,115,116,117,118,119,120,
      121,122,123,124,125,126,127
    }

local x = os.clock()
local s = 0

for i=1,20000000 do
  s = s + i
  --s = PRE_VOL_PAN_VAL[7] --(commenting out switched for each example)
end

print(string.format("elapsed time: %.2f\n", os.clock() - x))

For the addition s = s + i

elapsed time: 0.18

for the table lookup I got

elapsed time: 0.51

So at least in this simple case, an addition is more than twice as fast. Gets more complex of course if you add a bunch of conditional if statements to get to the addition first.

Just a quick thought aswell to save yourself a bit of typing you could create your table with a loop:

--this loop
-------------------------

PRE_VOL_PAN_VAL = {255} --insert first value manually

for i = 0,127 do
  table.insert(PRE_VOL_PAN_VAL , i) --table.insert adds the value i at last/next position in table
end 

 
--creates the same table as this
--------------------------------
PRE_VOL_PAN_VAL = {
      255,0,1,2,3,4,5,6,7,8,9,10,
      11,12,13,14,15,16,17,18,19,20,
      21,22,23,24,25,26,27,28,29,30,
      31,32,33,34,35,36,37,38,39,40,
      41,42,43,44,45,46,47,48,49,50,
      51,52,53,54,55,56,57,58,59,60,
      61,62,63,64,65,66,67,68,69,70,
      71,72,73,74,75,76,77,78,79,80,
      81,82,83,84,85,86,87,88,89,90,
      91,92,93,94,95,96,97,98,99,100,
      101,102,103,104,105,106,107,108,109,110,
      111,112,113,114,115,116,117,118,119,120,
      121,122,123,124,125,126,127
    }

https://www.lua.org/pil/19.2.html

Ledger, yes,very good advice. I’m always trying to optimize the code to make it work faster, and also compact it.

Thanks! :slight_smile:

Why not something like this?

function getPreVolPanVal(i)
  return (i-2)%256
end

The Win!

Why not something like this?

function getPreVolPanVal(i)
return (i-2)%256
end

Why not something like this?

function getPreVolPanVal(i)
return (i-2)%256
end

With this are already 2! Very good trick!

I hope the API improves and all these details are added. Despite these tricks, I still miss the reset value with double click and a slider designed for panorama (starting in the center), for minislider and slider.

Apart from that, you can control the middle and right mouse buttons as well, and combine the mouse buttons with the keyboard buttons. All these details, which are important, would make a difference to create interesting tools.

Agree, those would be nice additions!