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

Just some other opinions here, since I like to promote the API :slight_smile:

  1. catching all lines in high BPM is possible even If lines are skipped inbetween two “idle bangs”. You’d process a range of lines and it won’t miss anything. However, I’d suspect it might be a very good idea to cache the pattern data instead of constantly accessing renoise.song() and note_columns. For caching, tostring(), lines_in_range and some pattern matching can be used for quickly building an internal “cache”. Now any searches here will be very fast.

  2. Flashing buttons with a my_button_class:flash() method isn’t heavy, afaik. Use idle_notifier and os.clock comparison when setting the lightness. I think you’d have to add a lot of buttons before noticing large “steps” in the color fadings.

The main problem with explaining all of this, is that it requires quite a bit of prior knowledge from anyone trying to implement it.

PS. I think LUA is very fast! Accessing renoise.song() can be a bit slow, but as explained, this can be dealt with in many cases.

It seems that the solution is very complicated in this case. As I see it, the API does not have the opportune _observable to create it. Then is necessary try looking for tricks outside the API to build such a tool.

My idea was to build a simple GUI with 12 buttons representing only one octave. This octave would cover all notes (the 120 notes) and each button would automatically change the name of the note (octave number). Thus, constantly the 12 buttons would be updated only analyzing a single selected track with its 12 columns of note, only the visible note columns, nothing more (if only exist a note column visible, only analyze that note column).

Depending on the note, it would light one of the 12 corresponding buttons (green color), and off (gray color) with the note-off or other different note.This would be a step later. What I do not know how to build is to create a cache or use the correct notifiers, even if the tool is heavy. The issue is that I have not even seen a tool that works similar, and bewilders me :).

I am aware that there are several users in recent years who have tried to build tools reading data already written in the pattern editor without success.

Does anyone dare to build a single button that reacts to a single note already written?It could serve as a starting point and understand the operation.

It seems that the solution is very complicated in this case. As I see it, the API does not have the opportune _observable to create it. Then is necessary try looking for tricks outside the API to build such a tool.

On the contrary, you can totally track notes for visualizing and it doesn’t have to be complicated.

But realtime performance isn’t a simple thing to explain. Takes a bit of practice and experimentation to get into that mindset.

For example, the general idea that lua has “low precision” and is “slow” is mostly down to the fact that lua, when minding it’s own business (calling itself via the idle notifier) is evaluated perhaps 10-20 times per second.

This is not nearly enough for audio precision - but more than enough for some flashing button.

But it’s not that lua can’t be evaluated faster. Things are very different when lua responds to notifiers - they are triggered near-instantaneous.

Otherwise, stuff like observing parameters or responding to MIDI input would also feel very sluggish.

Lua is fast enough to do some pretty crazy stuff 10-20 times per second. It’s just that you need to avoid bottlenecks, or you will loose the performance.

My advice for this particular task would be to work with the idle notifier - make it call a function that checks the currently playing line and updates the button state.

That’s really all you will need for a basic working solution, and if you do things right it would have a very small impact on the CPU.

I’ve attached a very very crude and simple example of using a 20ms timer to read off and display Pattern 1 Track 1 Column 1 when the transport is playing. But Raul, don’t listen to me, I’m a pessimist when it comes to computing anyway :slight_smile:

I’ve attached a very very crude and simple example of using a 20ms timer to read off and display Pattern 1 Track 1 Column 1 when the transport is playing. But Raul, don’t listen to me, I’m a pessimist when it comes to computing anyway :slight_smile:

I will experiment with this as well.At the moment, it looks like the code works very very fast!If I can implement the buttons, it will go very fast.

I’ve already done a test with 12 note columns, and it works very fluid:7368 com.4Tey.Readermod.xrnx

Thank you very much!

Modify away Raul. Remember though ymmv with regards to missing notes at higher LPB, if you can implement some sort of caching system (as Joule suggests) etc… then you could probably improve it still further (but much more tricky to code) :slight_smile:

Just as a side note I always found that your best question is still -> https://forum.renoise.com/t/help-detect-son-member-inside-group-that-is-other-group-for-clone/47061

Modify away Raul. Remember though ymmv with regards to missing notes at higher LPB, if you can implement some sort of caching system (as Joule suggests) etc… then you could probably improve it still further (but much more tricky to code) :slight_smile:

Just as a side note I always found that your best question is still → https://forum.renoise.com/t/help-detect-son-member-inside-group-that-is-other-group-for-clone/47061

Maybe I have a problem with my PC. He has a powerful CPU. Even if you try these tools, you may not see if they actually impact performance. I will have to test on a slower laptop.I have not yet built a code with idle notifier.But I suppose it must be fast too.Adding a timer does not seem to affect performance at all.With its modified tool I have set the highest values in BPM and LPB and returns the values without delay or jump or do anything strange.Maybe with changing viewbuilder features, as change the color of buttons or name it will also work very fast.

I will have to look at how to avoid changing color when a line is empty, without a note. What I think:

color | note name

  • note values 0 to 119 change color button to green | change button name
  • note value = 120 (note off) change color button to gray |nothing
  • note value empty = 121,nothing |nothing (if button is green, does not change of color)

I have not even thought about the code, but it should be possible if I can constantly update the line.Even the tool could have 2 modes, to analyze a single note column or the 12 note columns inside the selected track.

Some general advice here.

This is where it comes in real handy to start using classes. If you don’t make a button class, you will have to resort to “borderline spaghetti code”. In this case you’d have to keep track of what buttons are currently lit in a global table. If you use an idle/timer-notifier to turn off the light after 500 ms, you would still also have to keep track of which buttons are currently lit, in case you try to light up a button that is already lit.

With a class and its methods, you will be able to manage these behaviors and special cases from within a class and easily just call some single function from the rest of your code - like my_button:flash(). You will also find that this is a more future-proof and expandable and reusable way of structuring your code. (Eventually want your button to be lit more and more intensively on consecutive notes? Totally possible here, with much less code mess).

Hello everyone!

Thank you very much everyone for the help!I have been able to test 2 different tools, separately and integrated into my tool GT16-Colors.

I will comment on some impressions…From what I understand, we have 2 ways of being able to continually call a particular function:

  1. By means of an “idle_notifier”:renoise.tool().app_idle_observable:add_notifier(the_name_function). According to the documentation: _“the exact interval is undefined and can not be relied on, but will be around 10 times per sec.”_10 times per sec. = 100 ms.
  2. By means of an “timer”:renoise.tool():add_timer(the_name_function, 5). According to the documentation: “The exact interval your function is called will vary a bit, depending on workload; e.g. when enough CPU time is available the rounding error will be around +/- 5 ms.”

I have been able to test both solutions in two separate tools.With app_idle_observable it is very slow to update the buttons.It seems to work for some 100ms , very slow.This solution is discarded.

On the other hand, the solution with add_timer is more flexible, allowing time control. It seems that the top is between 5 and 10 ms.However,testing the tool, I have deduced that it is impossible to obtain 100% reliable results due to the desynchronization (does not work in real time).In some reproductions of the track the function is not able to always read the information written on the line, forgetting some values at random, very punctually.

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?

So, I can only ask if there is really a method for it to work in real time, to avoid synchronization problems.Joule, does the method you propose work in real time?Would not there be punctual errors in reading?

I am a little desolate, because the tool I have built, helps me a lot to correct melodies and to modify them to qualify. You really do not realize until you see the tool in action.This weekend, I am going to look if I have time to publish a sketch of the tool, so that you can see.The tool is very basic, only shows 12 + 1 buttons, but what he shows is very useful to read more nice and understand the parameters of the pattern editor. Here a image:

7374 track-notes-checker-v01.png

It seems silly, but such a tool helps a lot with the melodies. You compare at all times what you hear with what is written through keys (buttons) that light up.When the pattern editor does not stop moving, it is difficult to read.This tool greatly alleviates reading…

Note :I really wish this tool worked perfectly, if is possible.If the tool make small mistakes, it’s not much help.

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?