[Solved] Help, code for play/stop specific note of a instrument, is po

Interested in this also… don’t you need a device plugged in to generate the midi/osc note?

Interested in this also… don’t you need a device plugged in to generate the midi/osc note?

Hmm… No. The user must enable the OSC server in the Renoise preferences. When that is done, it’s quite simple sending notes to the server from a script.

I don’t know if my way is the pretties, but I use “OscClient.lua” which provides a very simple syntax. One simple line for initializing a “connection” object (client), and then one simple line for sending a note value. It’s documented quite clearly in the file mentioned so it should be difficult to go wrong with it.

The “tricky” part when implementing it is that you have to keep a table of active notes, because they have to be note-offed with a separate transmission, of course depending on for how long you want to hold your note(s). So you might need to use some timers and tailor-made scheme for triggering the following note-off… However, I strongly suspect there might even be a better class available, providing a “duration” parameter when triggering notes. I haven’t looked that deep into what’s available.

(Danoise?)

Hmm so in theory you could build a sequencer using just the scripting side of things and not rely on writing notes to the pattern editor?

Will have a look at the code you mentioned, I had a few tool ideas that never went anywhere because I didn’t think you could do this

Hmm so in theory you could build a sequencer using just the scripting side of things and not rely on writing notes to the pattern editor?

Will have a look at the code you mentioned, I had a few tool ideas that never went anywhere because I didn’t think you could do this

I suggest checking the accuracy of the renoise.app() timer. I can’t find the forum thread now, but IIRC taktik stated somewhere that the timer only roughly guarantees an accuracy of XYZ milliseconds. That’s probably why it’s best to rely on the native playback. Perhaps it could at least be “good enough” for previewing a sequence, though…

EDIT, PS: Except for what’s mentioned above, I have noticed that there might be some small additional irregular timing factor with OSC, on a millisecond(s) level. Possibly due to network latency, even when it’s local? It was noticable in a function that 1) turns off edit mode, 2) sends OSC, 3) turns on edit mode. (notes could get recorded). So, a little bit of timer management might also be needed if you want to prevent recording by this scheme.

Hmm so in theory you could build a sequencer using just the scripting side of things and not rely on writing notes to the pattern editor?

Will have a look at the code you mentioned, I had a few tool ideas that never went anywhere because I didn’t think you could do this

In theory, yes. It would be wonky like hell, due to the reasons mentioned by joule.

But definitely good enough for something like a preview… something like a chord navigator hint

I don’t know if my way is the pretties, but I use “OscClient.lua” which provides a very simple syntax. One simple line for initializing a “connection” object (client), and then one simple line for sending a note value. It’s documented quite clearly in the file mentioned so it should be difficult to go wrong with it.

The “tricky” part when implementing it is that you have to keep a table of active notes, because they have to be note-offed with a separate transmission, of course depending on for how long you want to hold your note(s). So you might need to use some timers and tailor-made scheme for triggering the following note-off… However, I strongly suspect there might even be a better class available, providing a “duration” parameter when triggering notes. I haven’t looked that deep into what’s available.

(Danoise?)

That’s right, today I would use the xOscClient(part of xLib).

Also, to keep track of active voices you could use thexVoiceManagerclass

Here is a simple player class that should hopefully work in most cases.

I’ve included all that it needed (including the duplex oscclient) but nothing more. If you make a tool out of it, it will play a Cmaj7 chord for 3 seconds, extremely pleasing to your ears.

In Rauls’ case this might not be needed, since I’m guessing he’ll just start and stop on button press / button release. Then it would be sufficient to use the OscClient class only.

Click to view contents
-- Simple OSC voice player supporting duration
-- client: OscClient object
-- voices: table (!) of voices, one voice being { instrument_index, track_index, note_value, velocity }
-- duration: play duration in milliseconds
-- no_autoplay: if false, voices will play automatically when object is created
class 'OscPlayer'
function OscPlayer:__init(client, voices, duration, no_autoplay)
  self.client = client
  self.voices = voices
  self.duration = duration
  self._playing_voices = { }
  self._release_func = function() self:stop() end
  -- don't autoplay if true
  if not no_autoplay then
    self:play()
  end
  
end
-- bloating the syntax for dubious legibility
function OscPlayer:get_parameters(voice_index)
  local params = self._playing_voices[voice_index]
  return params[1], params[2], params[3], params[4]
end
function OscPlayer:play()
  -- cut notes if already playing
  if (#self._playing_voices > 0) then self:stop() end
  
  -- play and memorize voices
  for k = 1, #self.voices do
    local voice = self.voices[k]
    self._playing_voices[k] = table.rcopy(self.voices[k])
    self.client:trigger_instrument(true, self:get_parameters(k))
  end
  -- add "release" timer
  renoise.tool():add_timer(self._release_func, self.duration)
  
end
function OscPlayer:stop()
  -- remove the "release" timer
  if renoise.tool():has_timer(self._release_func) then
    renoise.tool():remove_timer(self._release_func)
  end
  -- send note-offs
  for k = 1, #self._playing_voices do
    local voice = self._playing_voices[k]
    self.client:trigger_instrument(false, self:get_parameters(k))
  end
  self._playing_voices = { }
end

--[[============================================================================
-- Duplex.OscClient
============================================================================]]--
--[[--
OscClient is a simple OSC client that connect to the built-in OSC server in Renoise, producing realtime messages that trigger notes or send MIDI messages
--]]
--==============================================================================
class 'OscClient' 
--------------------------------------------------------------------------------
--- Initialize the OscClient class
-- @param osc_host (string) the host-address name (can be an IP address)
-- @param osc_port (int) the host port
function OscClient:__init(osc_host,osc_port, protocol)
  -- the socket connection, nil if not established
  self._connection = nil
  local client, socket_error = renoise.Socket.create_client(osc_host, osc_port, protocol)
  if (socket_error) then 
    renoise.app():show_warning("Warning: Chord Tracker failed to start the internal OSC client")
    self._connection = nil
  else
    self._connection = client
  end
end

--------------------------------------------------------------------------------
--- Trigger instrument-note
-- @param note_on (bool), true when note-on and false when note-off
-- @param instr (int), the Renoise instrument index 
-- @param track (int) the Renoise track index
-- @param note (int), the desired pitch, 0-120
-- @param velocity (int), the desired velocity, 0-127
function OscClient:trigger_instrument(note_on,instr,track,note,velocity)
-- TRACE("OscClient:trigger_instrument()",note_on,instr,track,note,velocity)
  
  if not self._connection then
    return false
  end

  local osc_vars = { }
  osc_vars[1] = {tag = "i",value = instr}
  osc_vars[2] = {tag = "i",value = track}
  osc_vars[3] = {tag = "i",value = note}
  local header = nil
  if (note_on) then
    header = "/renoise/trigger/note_on"
    osc_vars[4] = {tag = "i",value = velocity}
  else
    header = "/renoise/trigger/note_off"
  end
  self._connection:send(renoise.Osc.Message(header,osc_vars))
  return true
end
--------------------------------------------------------------------------------
--- END OF CLASSES. TESTING CODE BELOW
local my_osc_client = OscClient("127.0.0.1", 8000, 2)
local voices = {
  { 1, 1, 60, 100 },
  { 1, 1, 64, 100 },
  { 1, 1, 67, 100 },
  { 1, 1, 71, 100 }
}
local my_player = OscPlayer(my_osc_client, voices, 3000)
my_player:stop()
my_player:play() -- a bit of stress testing
my_player:stop()
my_player:play()

Ok Joule,I am using the upper code provided.For now, I can shoot the note from the button. And stop.

The steps I’ve taken:

I used the whole code, canceling the following lines:

...
...

--- END OF CLASSES. TESTING CODE BELOW

local my_osc_client = OscClient("127.0.0.1", 8000, 2)

local voices = {
{ 1, 1, 60, 100 },
{ 1, 1, 64, 100 },
{ 1, 1, 67, 100 },
{ 1, 1, 71, 100 }
}

local my_player = OscPlayer(my_osc_client, voices, 3000)

my_player:stop()
my_player:play() -- a bit of stress testing
my_player:stop()
my_player:play()

Then I add the functions in the button:

--The button

-------------------------------(1)
presd()
  OscPlayer(my_osc_client, { { 1, 1, 60, 50 }, }, 3000) --<<----- 3000??? (delete duration)
end

-------------------------------(2)
reled()
  stop()
end

-------------------------------(the button)
WMP_KEY_00 = vb:button {
  id = 'WMP_KEY_00',
  width = 14,
  height = 50,
  pressed = function() presd() end, --(1)
  notifier = function() reled() end, --(2)  
--released = function() end, --released is similar to notifier
}

Now I have to look if I can eliminate time.And fix the code to use the selected instrument at any time.I have to find a way to not repeat the same code for the 120 buttons.

@Danoise. If something should be added in the API are the functions that will allow all this, without having to activate the OSC Server.Maybe it’s not available, it’s because it’s hard to implement.But apparently, I am not the only interested. Many things can be done with this.

To all, thank you very much!

If I have more doubts about this I will ask here.This is the first time I use the OSC Server.

Attached a code for a timer, in case it serves anything.:

function amic_CH04_4A() --This loop will stop itself after 1.5 seconds. Use a clock
  local starttime = os.clock()
  print("I'm about to spam you horribly for 1.5 seconds")
  while os.clock() - starttime < 1.5 do
  print("SPAM!")
  end
  print("All done :P")
end

One last question:

Is it possible to remove the automatic color selection or marked?For example, pressing a button lights up a color related to the skin of Renoise.Is it possible to replace it?

@Danoise. If something should be added in the API are the functions that will allow all this, without having to activate the OSC Server.Maybe it’s not available, it’s because it’s hard to implement.

It’s rarely the case that something is too hard to implement. More critical is,does it make sense to do so?

In this case, I think it would be nice. Perhaps not for the obvious reason (I’ve worked a lot with the current implementation and think it’s great - you can route messages to specific instrument, tracks, etc.) but rather, for these two reasons:

  1. We don’t actually have a way in the API to check if the OSC server is enabled, and at which port it is listening. So right now, as a tool author, you basically need to display a one-time message to the user that (s)he needs to enable the OSC server in Renoise.

And obviously, being able to control the OSC server via scripting would then be a first step… no need to display this message. But even this would have to be done carefully, because the OSC server allows you to send messages to containing lua code, which Renoise will then evaluate. I think Renoise will sandbox the code, protecting you against running certain methods (so you can’t wipe someones harddisk through the network!!), but it’s still something to be aware of. So I would basically still want to display a warning (“you’re going to enable the OSC server, are you sure you want to continue?”)

  1. The note that you create is a “note proper”. As a result, it might end up getting recorded somewhere in the project. This is not necessarily what you want. Workaround? Temporarily disable edit-mode if it was enabled, and re-enable after note has been triggered (ugly, but that should work).

@Raul,

Are you just gonna play and stop on pressed/release? In that case you don’t need the intermediate player I did. That was just for duration support.

Otherwise you can simply do something like:

local audition_client = OscClient("127.0.0.1", 8000, 2)

function create_button(instrument, note_value, velocity)

    local button = vb:button {
        text = "my_button",
        pressed = audition_client:trigger_instrument(
            true, instrument, note_value, velocity),
        released = audition_client:trigger_instrument(
            false, instrument, note_value, velocity)
    }
    
    return button
end

function create_row()

    local instrument = 1
    local velocity = 100

    local row = vb:row { }
    
    for note_value = 1, 10 do
        row:add_child(
            create_button(instrument, note_value, velocity)
        )
    end

    -- do what you want with the row here
    
end

I made the example a bit extensive since your approach seemed a little bit strange.

It’s rarely the case that something is too hard to implement. More critical is,does it make sense to do so?

In this case, I think it would be nice. Perhaps not for the obvious reason (I’ve worked a lot with the current implementation and think it’s great - you can route messages to specific instrument, tracks, etc.) but rather, for these two reasons:

  1. We don’t actually have a way in the API to check if the OSC server is enabled, and at which port it is listening. So right now, as a tool author, you basically need to display a one-time message to the user that (s)he needs to enable the OSC server in Renoise.

And obviously, being able to control the OSC server via scripting would then be a first step… no need to display this message. But even this would have to be done carefully, because the OSC server allows you to send messages to containing lua code, which Renoise will then evaluate. I think Renoise will sandbox the code, protecting you against running certain methods (so you can’t wipe someones harddisk through the network!!), but it’s still something to be aware of. So I would basically still want to display a warning (“you’re going to enable the OSC server, are you sure you want to continue?”)

  1. The note that you create is a “note proper”. As a result, it might end up getting recorded somewhere in the project. This is not necessarily what you want. Workaround? Temporarily disable edit-mode if it was enabled, and re-enable after note has been triggered (ugly, but that should work).

1)Maybe a checkbox (activate/deactivate) would suffice → Active OSC Server [v], with tooltip: “warning, bla bla bla”.What would be the function to activate?

  1. Wooow this has been a disappointment.!!! :blush:This is creating me problems. I need the edit-mode activated, to write the notes in the pattern editor while the note is being played.

At the point where I am, everything works:

  • Timer deleted. OK!
  • Note associated with each button (each key of virtual piano), with play/stop, OK!
  • Track selection, OK! (in pattern editor)
  • Instrument selection, OK! (in instruments box)
  • Volume control with a valuebox OK! (in tool)
  • edit-mode activated writte the notes in pattern editor with keys of virtual piano OK!
  • edit-mode activated with OSC activate, writte notes also, NOT OK! ← problem!!!

I just need to avoid your point 2 (to prevent notes being written to the pattern editor when playing the notes):

7144 osc-write-notes.gif

This should not happen(should not write any notes, should only sound the associated sample of the selected instrument).In the gif image capture, my functions about write notes in the pattern editor are disabled.It is caused by OSC related functions.

Is there any way to avoid this?

@Raul,

Are you just gonna play and stop on pressed/release? In that case you don’t need the intermediate player I did. That was just for duration support.

Otherwise you can simply do something like:

local audition_client = OscClient("127.0.0.1", 8000, 2)

function create_button(instrument, note_value, velocity)

local button = vb:button {
text = "my_button",
pressed = audition_client:trigger_instrument(
true, instrument, note_value, velocity),
released = audition_client:trigger_instrument(
false, instrument, note_value, velocity)
}
    
return button
end

function create_row()

local instrument = 1
local velocity = 100

local row = vb:row { }
    
for note_value = 1, 10 do
row:add_child(
create_button(instrument, note_value, velocity)
)
end

-- do what you want with the row here
    
end

I made the example a bit extensive since your approach seemed a little bit strange.

Thanks joule!I have it organized differently, maybe I will change it. I will try to experiment with this code.The “track selection” is missing.It should also be.

As mentioned earlier in the thread, you have to prevent the recording the notes by using some workaround. It can be done with a one-shot timer, delaying edit_mode=on for maybe 40ms or so. (one-shot = timer self destructs after one run)

EDIT: Yeah I forgot the track parameter in the osc triggering. For you to correct :wink:

As mentioned earlier in the thread, you have to prevent the recording the notes by using some workaround. It can be done with a one-shot timer, delaying edit_mode=on for maybe 40ms or so. (one-shot = timer self destructs after one run)

I do not know how to add it. I am attaching the code I currently use (the note duration timer is off, overridden lines):

--------------------------------------------------------------------------------
-- OSC SERVER
--------------------------------------------------------------------------------
class 'OscPlayer'

function OscPlayer:__init( client, voices, duration, no_autoplay )
  self.client = client
  self.voices = voices
  --self.duration = duration

  self._playing_voices = { }
  self._release_func = function() self:stop() end
  --don't autoplay if true
  if not no_autoplay then
    self:play()
  end  
end

-- bloating the syntax for dubious legibility
function OscPlayer:get_parameters( voice_index )
  local params = self._playing_voices[voice_index]
  return params[1], params[2], params[3], params[4]
end

function OscPlayer:play()
  --cut notes if already playing 
  if ( #self._playing_voices > 0 ) then self:stop() end
  --play and memorize voices
  for k = 1, #self.voices do
    local voice = self.voices[k]
    self._playing_voices[k] = table.rcopy( self.voices[k] )
    self.client:trigger_instrument( true, self:get_parameters( k ) )
  end
  --add "release" timer
  --renoise.tool():add_timer( self._release_func, self.duration )
end

function OscPlayer:stop()
  --remove the "release" timer
  --if renoise.tool():has_timer( self._release_func ) then
  --renoise.tool():remove_timer( self._release_func )
  --end
  --send note-offs
  for k = 1, #self._playing_voices do
    local voice = self._playing_voices[k]
    self.client:trigger_instrument( false, self:get_parameters( k ) )
  end
  self._playing_voices = { }
end

class 'OscClient' --Initialize the OscClient class
  -- osc_host (string) the host-address name (can be an IP address)
  -- osc_port (int) the host port

function OscClient:__init( osc_host, osc_port, protocol )
  -- the socket connection, nil if not established
  self._connection = nil
  local client, socket_error = renoise.Socket.create_client( osc_host, osc_port, protocol )
  if ( socket_error ) then 
    renoise.app():show_warning( "Warning: Chord Tracker failed to start the internal OSC client" )
    self._connection = nil
  else
    self._connection = client
  end
end

--------------------------------------------------------------------------------
-- Trigger instrument-note
--- note_on (bool), true when note-on and false when note-off
--- instr (int), the Renoise instrument index 1-254
--- track (int), the Renoise track index 
--- note (int), the desired pitch, 0-120
--- velocity (int), the desired velocity, 0-127
function OscClient:trigger_instrument( note_on, instr, track, note, velocity )
  if not self._connection then
    return false
  end
  local osc_vars = { }
    osc_vars[1] = { tag = "i", value = instr } --wmp_instr()
    osc_vars[2] = { tag = "i", value = track }
    osc_vars[3] = { tag = "i", value = note }
  local header = nil
  if ( note_on ) then
    header = "/renoise/trigger/note_on"
    osc_vars[4] = { tag = "i", value = velocity }
  else
    header = "/renoise/trigger/note_off"
  end
  self._connection:send( renoise.Osc.Message( header,osc_vars ) )
  return true
end
local my_osc_client = OscClient( "127.0.0.1", 8000, 2 )

--my_player = OscPlayer( my_osc_client, { { 1, 1, 60, 50 } }, 10000 )
--my_player:stop()

function wmp_instr( song, instr )
  song = renoise.song()
  instr = song.selected_instrument_index
  return instr
end

function wmp_track( song, track )
  song = renoise.song()
  track = song.selected_track_index
  return track
end

--function wmp_note() end

function wmp_vel( vel )
  vel = vb.views['WMP_CTRL_VEL'].value
  return vel
end

--------------------------------------------------------------------------------
-- A BUTTON: (C-0)
--------------------------------------------------------------------------------

function wmp_fn_key_00_pre()
  OscPlayer( my_osc_client, { { wmp_instr(), wmp_track(), 0, wmp_vel() } } ) --0 is the note
end
function wmp_fn_key_00_rel()
  OscPlayer( my_osc_client, { { wmp_instr(), wmp_track(), 0, wmp_vel() } } ):stop()
end
function wmp_fn_key_00( song, spi, sti, sli, snci ) --To write parameters in the pattern editor
  song = renoise.song()
  spi, sti, sli, snci = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index
  if ( song.transport.edit_mode == true and song.selected_note_column ) then --condition for edit_mode == true
    song.patterns[spi].tracks[sti].lines[sli].note_columns[snci].note_value = 0 --note
    ins_inst()
    ins_vol()
    ins_pan()
    ins_dly()
    step_length()    
  end
end

--the vb:button (C-0 note)

WMP_KEY_00 = vb:button {
  id = 'WMP_KEY_00',
  tooltip = '',
  width = 14,
  height = 50,
  color = { 0xFF,0xFF,0xFF },
  pressed = function() wmp_fn_key_00_pre() end,
  --notifier = function() wmp_fn_key_00() end, --<----Disabled to test the OSC server in isolation
  released = function() wmp_fn_key_00_rel() end,
  bitmap = 'icons/c-0.png',
}

How do I add the “one_short timer” as you mention?

Hmm… I can’t verify my code now, but the principle looks pretty much like this:

local stored_edit_mode = renoise.song().transport.edit_mode

-- we only need the workaround if recording mode was on. let's not clutter memory/cpu otherwise ;)
if stored_edit_mode then
 renoise.song().transport.edit_mode = false
end
 
-- put playing trigger here!

-- reset to edit_mode = true after some delay
if stored_edit_mode then
 local timer_func
 timer_func = function()
   renoise.song().transport.edit_mode = true
   renoise.tool():remove_timer(timer_func) -- works?
  end

 renoise.tool():add_timer(timer_func, 40)
end

Not sure if it would fire an error if you press the key repeatedly very fast, but I don’t think so. Try it and we’ll work it out.

PS. Do you really need duration support? If not, ditch the OscPlayer class and use OscClient:trigger_instrument directly.

Where do I put the code exactly?

I’ve pasted it inside the function "function OscPlayer:__init( client, voices, duration, no_autoplay )"But the effect is strange. The red frame of the edit_mode flashes, and if very fast pulse ends off.

in,renoise.tool():add_timer(timer_func, 40)I’ve tested it with 10, 20, 40, 60, 80, and I keep typing values.

However, it does not return any errors, even shredding the mouse.

It should flash, very briefly, since it’s temporarily switching off edit_mode to prevent recording any notes. If edit_mode was on before, it should be on after.

The reason why you’re getting a strange behavior is likely due to the dubious structure of your code, specifically the way you handle the player object (rather creating two separate objects for pressed/released, which is not optimal).

Create one player object (with no_autoplay = true) in the scope above the button creation, then call my_player:play() and my_player.stop() from the pressed/released notifiers.

I think (!) it is enough if the workaround goes inside the OscPlayer:play() function. IIRC “orphan” note-offs won’t get recorded anyway.

I true this:

function OscPlayer:__init( client, voices, duration, no_autoplay )
  self.client = client
  self.voices = voices
  --self.duration = duration

  self._playing_voices = { }
  self._release_func = function() self:stop() end
  --don't autoplay if true
  if not no_autoplay then
    self:play()
  end  

----delay edit_mode 40ms 
  local stored_edit_mode = renoise.song().transport.edit_mode
  -- we only need the workaround if recording mode was on. let's not clutter memory/cpu otherwise ;)
  if stored_edit_mode then
    renoise.song().transport.edit_mode = false
end
  -- put playing trigger here!
  -- reset to edit_mode = true after some delay
  if stored_edit_mode then
    local timer_func
    timer_func = function()
            renoise.song().transport.edit_mode = true
            renoise.tool():remove_timer(timer_func) -- works?
        end
    renoise.tool():add_timer(timer_func, 40)
  end
end

Keep writing one note (or two notes, depending the timer), when press the mouse.

What exactly happens (in slow process):

  1. Pressing click: write note A in note column 01
  2. Releasing click: write note B (the same note A) in note column 02

That is, as the gif shows.

This seems to work perfectly fine here…

local button = vb:button {
      text = tostring(note_value),
      pressed = function()
          local stored_edit_mode = renoise.song().transport.edit_mode
          if stored_edit_mode then
              renoise.song().transport.edit_mode = false
          end
      
          osc_client:trigger_instrument(true, instrument, track, note_value, velocity)
  
          if stored_edit_mode then
            local timer_func
            timer_func = function()
                renoise.song().transport.edit_mode = true
                renoise.tool():remove_timer(timer_func)
              end
            renoise.tool():add_timer(timer_func, 40)
          end      
        end,
      released = function()
          osc_client:trigger_instrument(false, instrument, track, note_value, velocity)
        end,
    }

A practical example is attached.

7145 joule.no0b.testpad.xrnx

This seems to work perfectly fine here…

local button = vb:button {
text = tostring(note_value),
pressed = function()
local stored_edit_mode = renoise.song().transport.edit_mode
if stored_edit_mode then
renoise.song().transport.edit_mode = false
end
      
osc_client:trigger_instrument(true, instrument, track, note_value, velocity)
  
if stored_edit_mode then
local timer_func
timer_func = function()
renoise.song().transport.edit_mode = true
renoise.tool():remove_timer(timer_func)
end
renoise.tool():add_timer(timer_func, 40)
end      
end,
released = function()
osc_client:trigger_instrument(false, instrument, track, note_value, velocity)
end,
}

A practical example is attached.

attachicon.gifjoule.no0b.testpad.xrnx

Is there a problem with setting the value “15” or “10” or “5” instead of 40?Why 40?With value <16 edit_modenot blinking, but I do not know if it depends on the hardware.

The “left click repeat rate” in preferences/Keys/Mouse Wheel has something to do?

I’ll try to build a new piano based on your code, even if it means starting over. I want the tool to work as fine as possible. I believe that with all this I can move forward.I also have another option in mind, edit the notes without using edit_mode.In this case, osc server would be canceled, and the notes would not sound if active edit_mode. As I have my code, I have a separate configuration for each button.I think I understand all your code, and I can use it to compress it to the maximum (within my possibilities).

Thank you very much for all this!I think it’s the best solution for this case.

…withmy_player:play() and my_player.stop(),I had an error in the terminal, by using them separately in “pressed” and “released” of the button.I was doing something wrong.

I will continue to build, and when I have something fine I will comment here, maybe this weekend.

Yeah, I could use 10ms to avoid any noticable flickering, but that’s on an I7 processor. To be safe on slower computers, I would guess that 40ms would be OK. I’d suggest making a stress test in 1 core mode while using a couple of heavy VST/VSTi:s.