[Solved] Help: check notes with follow the player position in Patt. Ed

As I said, just process the range of lines that has passed since the last processing. This should eliminate any missed lines.

So, the timer should basically check how many lines have passed since the last time it checked how many lines had passed. Then loop thru that range of lines. Often it will be just 1 or 0 lines, but sometimes it might be 2 lines or more (that’s where your skip is occurring now).

Good advice there joule.

Coding for realtime is mostly about figuring out what you don’t need to do, or if it can be done later.

In this case, you don’t need the script to be called a 1000x per second, because that would definitely be harder for the CPU than calling it just 10 times. And also, if your script can_deal_ with being called fewer times it means it’s amore robust implementation - you could miss a few calls for whatever reason, and it would still produce the expected result.

I also get the feeling that it usually fails more when Renoise are reading the data at the end or beginning of each pattern.Is it possible that when you change patterns, Renoise has to execute more processes and not give you time to react?

Yes, coding for realtime also means identifying the worst cases. And pattern boundaries could be one such thing. But they are far from the worst performance hog - that would be e.g. instantiating plugins and heavy I/O operations (saving a song).

I’ve not yet found a way to let lua keep spinning smoothly along while creating an instance of e.g. Bazille. Works fine once it’s created, but to begin with it’s practically freezing everything for a moment.

But here’s the thing: if we are just talking visual feedback I wouldn’t really call it a problem. I’d rather have that audio is smooth at all times :walkman:

For now, what I intend to build is that the timer only makes one pass per line.If the pattern has 64 lines, it only makes 64 passes, one per line…I think I’m getting close.According to my previous code, the clock made too many passes per line.

Something like that, new line, just one new pass.At least that’s the idea. I guess this will avoid reading errors.

If the pattern has 64 lines, it only makes 64 passes, one per line…

But you can’t really control that. You can’t say, “my idle notifier is going to fire on every line”.

And you seem to appreciate solid solutions, so I would go with joules approach. Something like this:

  • To begin with, add an idle notifier and in there, check the current playback position (line)

  • Still in the idle notifier, compare a previously stored position/line with the current one. If changed, do something (here you are already avoiding processing the same line multiple times).

  • And the “do something” method should then get both the old and the new position as arguments. That way, you can display all notes that were encountered during those lines.

But you can’t really control that. You can’t say, “my idle notifier is going to fire on every line”.

I already noticed :slight_smile:

  • To begin with, add an idle notifier and in there, check the current playback position (line)

  • Still in the idle notifier, compare a previously stored position/line with the current one. If changed, do something (here you are already avoiding processing the same line multiple times).

I’m trying to build just that.Tonight I’ll look to check the code, see if I can build it.I have to do tests with print to print only when a new line is read.By doing that I think I’m done (register the line position and then compare it, if different, then execute a function).

Thank you, I will try to follow this approach!

I still do not move here!

I have tried to try other codes to register the “playback position” which is my reference at all times (renoise.song().transport.playback_pos.line).

What is my problem? If I use a app_idle_observable (or add_timer), if the BMP and LPB are very fast or slow, print the position of line skipping values or repeat values. Example:

function tnc_check_playback_line( song, stpl )
  song = renoise.song()
  stpl = song.transport.playback_pos.line
  print ( " ________ : ", stpl )
end

function line_timer()
  if vb.views['TNC_CB'].value == true then -- a checkbox to active
    --if not renoise.tool().app_idle_observable:has_notifier(tnc_check_playback_line) then
    -- renoise.tool().app_idle_observable:add_notifier(tnc_check_playback_line)
    if not renoise.tool():has_timer(tnc_check_playback_line) then
      renoise.tool():add_timer( tnc_check_playback_line, 5 )
    end
  end
end

Then, the “tnc_check_playback_line” function is inside a loop of repetition desynchronized (a machine gun).In case of repeating values because add_timer work in 5 or 10 or 20ms and BPM and LPB are reasonably low, how do I return a single value to launch a specific function?

I do not know if this approach would be correct, but I can not build a code that works by following your instructions.I’ve tried a thousand things inside the function that works with add_timer, but I can not get it to work properly. I’ve also tried to stop with remove_timer, and reactivate, but nothing.

Can you provide a code that prints in the terminal the line number each time the playback position is changed?

A minimum reference speed could be: BPM = 400 and LPB = 8 (I do not think I use a higher speed ever, but of course, the ideal is a tool that works well at any speed.)

It seems obvious that what I want to look for is something like this: "_renoise.song().transport.playback_pos.line _observable _ " , but this does not exist in the API.I’ve been searching the forums and I have found that already tried to build several tools but always with problems, and with the request to add the observable for transport.playback_pos.line.If it implies a great impact to the CPU depending on the functions to be executed, add a warning for the programmer to know.From what I see, make visual changes using the viewbuilder does not imply a great impact of the CPU.

Not having theobservable fortransport.playback_pos.line involves using add_timer with imprecision and for me, a very big mess to build such a basic tool, like changing the color of a button when the playback position changes.

Could you help me with a custom code that works?I do not know what to try.

A related topichttps://forum.renoise.com/t/executing-a-script-function-with-timing/34936

Can you provide a code that prints in the terminal the line number each time the playback position is changed?

No, not possible. Seems you’ve gone back to the old idea that you can somehow obtain each and every change in position. But you can’t.

All you need is the current position and the position it was a fraction of a second ago. Then, act on that information - it really is sufficient.

No, not possible. Seems you’ve gone back to the old idea that you can somehow obtain each and every change in position. But you can’t.

All you need is the current position and the position it was a fraction of a second ago. Then, act on that information - it really is sufficient.

Ok, and what would the code look like?I’m stuck here…

With this function, I get the same as before:

local line = 0
function cpl()
  if not ( renoise.song().transport.playback_pos.line == line) then
    line = renoise.song().transport.playback_pos.line
    print("line:"..line)
  end
end

renoise.tool().app_idle_observable:add_notifier(function() cpl() end )

All the functions I have tried end up doing the same thing…

… and the position it was a fraction of a second ago…

How would that be?

Ok, and what would the code look like?I’m stuck here…

Check 4Teys example?

Just add a local variable which stores the position. And compare against that each time the timer callback is invoked

Edit: nevermind - you have figured it out with the code above.

Raul, you are aware that there is a renoise.song().transport.edit_pos.line as well? For example:

Raul, you are aware that there is a renoise.song().transport.edit_pos.line as well? For example:

compare renoise.song().transport.playback_pos.line with renoise.song().transport.edit_pos.line ???

Edit: In this case, if following the player is not activated, in theory the final function would not run. It is not like this?

compare renoise.song().transport.playback_pos.line with renoise.song().transport.edit_pos.line ???

Errr, no, now I didn’t say anything like that Raul. What I’m just pointing out is that possibly in your case reading the edit_pos.line of the transport is going to be more useful? Remember in Renoise you can decouple the playback and the edit position.

Errr, no, now I didn’t say anything like that Raul. What I’m just pointing out is that possibly in your case reading the edit_pos.line of the transport is going to be more useful? Remember in Renoise you can decouple the playback and the edit position.

Ok, for the case is the same, edit_pos.line and playback_pos.line return the same value if following player is activated.If you print the values using vb.views.textid.text, the same value of text is written several times, although it is visually the same value in the tool.

If I’m not mistaken, your tool print this:

Playback Line. 01 - Edit Line : 01 …

Playback Line. 01 - Edit Line : 01 …

Playback Line. 01 - Edit Line : 01 …

Playback Line. 01 - Edit Line : 01 …

Playback Line. 01 - Edit Line : 01 …

Playback Line. 01 - Edit Line : 01 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 03 - Edit Line : 03 …

Playback Line. 03 - Edit Line : 03 …

Playback Line. 03 - Edit Line : 03 …

Playback Line. 03 - Edit Line : 03 …

Playback Line. 03 - Edit Line : 03 …

Playback Line. 03 - Edit Line : 03 …

… depending on the speed in BPB and LPB

When I would have to do this:

Playback Line. 01 - Edit Line : 01 …

Playback Line. 02 - Edit Line : 02 …

Playback Line. 03 - Edit Line : 03 …

… without depending onthe speed in BPB and LPB (apparently something impossible)

The issue is that I do not know how to translate this, to handle a button and its properties, color, name, to be invoked only once per line.

As I understand from Danoise’s comments…

I have the current position, I can choose between edit_pos.line or playback_pos.line.And I have to compare this variable with a fixed value, which I do not know how to fix.

I tried to do comparison tests with “if x == y then” inside the function with timer activated.But since the function does not stop repeating, the “fixed value” also changes.Do I have to set a value within a function that works with a timer?I do not understand how I should pose what Danoise comments…

Edit: In this case, if following the player is not activated, in theory the final function would not run. It is not like this?

I’m not fully sure what you mean by ‘following the player’. But what I can say is that so long as that dialog (in my example) is on the screen (and you haven’t switched to another application, because that would probably also stop the timer), that timer function is called around every 20ms, irrespective of the transport started/stopped state. All this is Raul is you have a timer function ‘in the background’ sampling the pattern every 20ms. If your timer function overruns 20ms it will probably begin to stutter and skip etc… (believe me, not good). If you set your timer to say 5ms (to sample the pattern faster) make sure that the code in the timer function executes in less than 5ms of CPU time. You can’t just keep adding code to that timer function, at some point it could/will overrun. Now you say: ‘Well I’ll just set the timer to 40ms to give it more time’. Yup, you could do this, but now you are sampling the pattern at a lower frequency, so now you run the risk of missing notes at higher LPB/BPM. See part of the game here? Becomes more and more inaccurate.

The issue is that I do not know how to translate this, to handle a button and its properties, color, name, to be invoked only once per line.

So now we’ve moved on from sampling the pattern to the question: How do I work out if the line has changed? Well if you look at this section of code:

if (CurrentLine == rs.transport.edit_pos.line) then
    return
  else
    CurrentLine = rs.transport.edit_pos.line
  end

Is there not a return statement in there? If the CurrentLine is equal to the Renoise transport edit position then leave the timer function? Otherwise set the CurrentLine to the transport edit position. In other words anything that comes after that If/else statement, is only executed on a change of line.

if (CurrentLine == rs.transport.edit_pos.line) then
    return <<--------------------------------------- RETURN!!!
  else
    CurrentLine = rs.transport.edit_pos.line
    -- here launch the desired function
  end

Ok, this matter is solved already!!! :o :o :o

Thank you very much for the help, Danoise and 4Tey!!!

However, it does not solve a very strange reading problem. I explain…

You imagine a complex piano melody written using the 12 note columns, occupying several patterns.When the tool is working, it illuminates the buttons correctly (each button represents a note column).An image of support:

[sharedmedia=core:attachments:7374]

But a concrete failure always occurs:

When Renoise read the first line of each pattern (line 001 of each note column), if the line is empty, it sometimes "read the same note (change the characteristics of the button) as the first line of the previous pattern. It happens almost 90% of the time.It is a subtle error very annoying. Always the same. If I move the note of the previous pattern on line number 002, the error no longer occurs.

A ) Example of error :wacko::-------------------------------------------------------------------------

---------------- Pattern 01, note column 03

C-0 line 001

D-2 line 002

---------------- Pattern 02, note column 03

nocing line 001 <------- ERROR, here read a C-0 (As the same note from the previous pattern (same line 001)

… line 002

B ) Example not error-------------------------------------------------------------------

---------------- Pattern 01, note column 03

nocing line 001

D-2 line 002

---------------- Pattern 02, note column 03

nocing line 001 <------- NOT ERROR, because in the previous patter in line 001 is empty )

… line 002

This is the function that executes once each time a line starts:

function change_buttons( song, spi, sti, stpl )
  song = renoise.song()
  spi, sti, stpl = song.selected_pattern_index, song.selected_track_index, song.transport.playback_pos.line

  if song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER then

      -- note column 1
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string ~= '---' then
        vb.views["TNC_NC_01"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string
        vb.views["TNC_NC_01"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string == 'OFF' then
        vb.views["TNC_NC_01"].color = { 0x40,0x00,0x00 }
      end
      -- note column 2
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 2 ).note_string ~= '---' then
        vb.views["TNC_NC_02"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 2 ).note_string
        vb.views["TNC_NC_02"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 2 ).note_string == 'OFF' then
        vb.views["TNC_NC_02"].color = { 0x40,0x00,0x00 }
      end
      -- note column 3
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 3 ).note_string ~= '---' then
        vb.views["TNC_NC_03"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 3 ).note_string
        vb.views["TNC_NC_03"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 3 ).note_string == 'OFF' then
        vb.views["TNC_NC_03"].color = { 0x40,0x00,0x00 }
      end
      -- note column 4
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 4 ).note_string ~= '---' then
        vb.views["TNC_NC_04"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 4 ).note_string
        vb.views["TNC_NC_04"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 4 ).note_string == 'OFF' then
        vb.views["TNC_NC_04"].color = { 0x40,0x00,0x00 }
      end
      -- note column 5
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 5 ).note_string ~= '---' then
        vb.views["TNC_NC_05"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 5 ).note_string
        vb.views["TNC_NC_05"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 5 ).note_string == 'OFF' then
        vb.views["TNC_NC_05"].color = { 0x40,0x00,0x00 }
      end
      -- note column 6
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 6 ).note_string ~= '---' then
        vb.views["TNC_NC_06"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 6 ).note_string
        vb.views["TNC_NC_06"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 6 ).note_string == 'OFF' then
        vb.views["TNC_NC_06"].color = { 0x40,0x00,0x00 }
      end
      -- note column 7
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 7 ).note_string ~= '---' then
        vb.views["TNC_NC_07"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 7 ).note_string
        vb.views["TNC_NC_07"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 7 ).note_string == 'OFF' then
        vb.views["TNC_NC_07"].color = { 0x40,0x00,0x00 }
      end
      -- note column 8
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 8 ).note_string ~= '---' then
        vb.views["TNC_NC_08"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 8 ).note_string
        vb.views["TNC_NC_08"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 8 ).note_string == 'OFF' then
        vb.views["TNC_NC_08"].color = { 0x40,0x00,0x00 }
      end
      -- note column 9
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 9 ).note_string ~= '---' then
        vb.views["TNC_NC_09"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 9 ).note_string
        vb.views["TNC_NC_09"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 9 ).note_string == 'OFF' then
        vb.views["TNC_NC_09"].color = { 0x40,0x00,0x00 }
      end
      -- note column 10
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 10 ).note_string ~= '---' then
        vb.views["TNC_NC_10"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 10 ).note_string
        vb.views["TNC_NC_10"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 10 ).note_string == 'OFF' then
        vb.views["TNC_NC_10"].color = { 0x40,0x00,0x00 }
      end
      -- note column 11
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 11 ).note_string ~= '---' then
        vb.views["TNC_NC_11"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 11 ).note_string
        vb.views["TNC_NC_11"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 11 ).note_string == 'OFF' then
        vb.views["TNC_NC_11"].color = { 0x40,0x00,0x00 }
      end
      -- note column 12
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 12 ).note_string ~= '---' then
        vb.views["TNC_NC_12"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 12 ).note_string
        vb.views["TNC_NC_12"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
      end
      if song:pattern( spi ):track( sti ):line( stpl ):note_column( 12 ).note_string == 'OFF' then
        vb.views["TNC_NC_12"].color = { 0x40,0x00,0x00 }
      end
      --- ---
      vb.views['TN_NC_NM_01'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string )
      vb.views['TN_NC_NM_02'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 2 ).note_string )
      vb.views['TN_NC_NM_03'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 3 ).note_string )
      vb.views['TN_NC_NM_04'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 4 ).note_string )
      vb.views['TN_NC_NM_05'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 5 ).note_string )
      vb.views['TN_NC_NM_06'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 6 ).note_string )
      vb.views['TN_NC_NM_07'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 7 ).note_string )
      vb.views['TN_NC_NM_08'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 8 ).note_string )
      vb.views['TN_NC_NM_09'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 9 ).note_string )
      vb.views['TN_NC_NM_10'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 10 ).note_string )
      vb.views['TN_NC_NM_11'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 11 ).note_string )
      vb.views['TN_NC_NM_12'].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( 12 ).note_string )
      --- --- 
  end 
end

It can probably be reduced.But, what can this error be?All notes already written on line 001 are well read, except the strange case above, which seems to read a note that is not written in the pattern editor…Really strange!!!

I’ll look to create a separate tool for them to you see…

Ok, here is the tool “finished”. Is named “Track Notes Checker”. Please test the tool for search errors! :

  1. The tool TNC “Track Notes Checker” :7378 ulneiz.TNC_v1.0b1.xrnx

Look at all the time that happens whenever you read the first line of each pattern (line 00, first line). Something does not work right here.Need to fix this little bug.Any idea of the cause of this error?

I have tried the tool even at a speed of: BPM = 400 aprox. and LPM = 8.That’s more than twice the speed I usually use.I would like to leave this perfect tool, within its limitations by the timer.

Why do you read a note on line 00 when it is not written?It is strange, because the notes that are already written on line 00 are read correctly.

To check, I think all notes have OFF at the end in the song for test…

The operation is very basic: if exist note or note off, change name and color for each button, (12 buttons, one for each note column).If you write complex melodies using the 12 note columns, following the order of one octave, it is very simple to visualize.Can be very fun!!! :slight_smile:

7380 ulneiz.TNC.png

Please prove it and help to perfect it!!!

Thank you very much for the help and the patience!!!

Note: The read is made to be continuous between patterns, let the song be a piece. You need to activate the follow player…

Look at all the time that happens whenever you read the first line of each pattern (line 00, first line). Something does not work right here.Need to fix this little bug.Any idea of the cause of this error?

I’ve had a quick look Raul. I’ll try and explain what I think the problem is here. When Renoise is playing and it changes pattern in the sequence, when you read the note data (sampled from your timer function) of the first line from the new pattern say using:

song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string

the note data you get back is probably incorrect. So let’s assume that there is a ‘setup’ time when Renoise switches patterns when playing, the timer catches it (sometimes) when it is in this ‘setup’ time and…you get incorrect note data back when you read the first line. I would say it is nothing to do with the logic in your code Raul.

Offhand (because Renoise has put a spanner in the works) the thing to try would be to say buffer up at least the next line (or a few lines ahead) in the pattern/sequence on each timer call, and read from that. Doable, but more tricky/messy to code though. Also remember that you don’t want overruns, so whatever buffering/caching system you come up with (including button/text rendering, because at the moment you have all of that gui changing stuff executed in your timer function) it has got to complete in less time than the time you set your periodic timer (which at the moment from your code is set at 5ms. I assume you are not planning on running this on a 33MHz 486 then :wink: )

I also took a quick look. Raul - I have to say, for a realtime script, you are actually being quite wasteful…

if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string ~= '---' then
  -- do something
end
if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string == 'OFF' then
  -- do something
end
-- etc...

There is nothing “wrong” here per se, but several things you can do better.
As per my third advice in the lua optimization thread, it’s good idea to store references to objects that you are going to call repeatedly.

First of all, it’s very simple to avoid several function calls by storing a reference to the line you’re working on:

local ln = song:pattern( spi ):track( sti ):line( stpl )

This way, you avoid the three API functions you are calling - pattern(), track() and line() - each time you need to access a note column.
And since you are using the line above a total of 48 times, this is 48*3 == 144 function calls avoided, with one simple change.

It makes the code a lot easier to read too :slight_smile:

Another improvement is to use the if … else syntax to simplify comparisons:

if (ln:note_column( 1 ).note_string ~= '---') then
-- do something
elseif (ln:note_column( 1 ).note_string == 'OFF') then
-- do something else
end

Or better still, using the previous technique and storing a reference to the note-column (additionalcalls avoided)

local col = ln:note_column( 1 )
if (col.note_string ~= '---') then
-- do something
elseif (col.note_string == 'OFF') then
-- do something else
end

Using if…else is more efficient, since the second statement - looking for the (“OFF”) - will never be evaluated if the first part was found to be true.

Now, with such a simple example there might not be a big impact. But imagine that your code was more complex than this? Skipping parts as early as possible can be a HUGE performance booster.

And both are general principles which can be applied to any programminglanguage :slight_smile:

Also remember that you don’t want overruns, so whatever buffering/caching system you come up with (including button/text rendering, because at the moment you have all of that gui changing stuff executed in your timer function) it has got to complete in less time than the time you set your periodic timer (which at the moment from your code is set at 5ms. I assume you are not planning on running this on a 33MHz 486 then :wink: )

I never used the timer, so I don’t have much experience with that.To me, idle_notifier is the natural choice, as it simply runs when Renoise has time for it.