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

Hi!I’m a bit confused with a function to do the following:

The function (you can copy to test):

function check_note( song, spi, sti, sli, snci )
  song = renoise.song()
  spi, sti, sli, snci = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index

  --song.patterns[spi]:add_line_notifier(check_note)

  --if song.patterns[spi].tracks[sti].lines[sli].note_columns[snci].is_empty == true then
  if song.patterns[spi].tracks[sti].lines[sli].note_columns[snci].note_value ~= 0 then --> C-0
    vb.views['XXX_BT_01'].color = { 0x70,0x00,0x00 } --red
    print("red")
    else
    vb.views['XXX_BT_01'].color = { 0x00,0x70,0x00 } --green
    print("green")
  end

  --if song.patterns[spi]:has_line_notifier(check_note) then
    --song.patterns[spi]:remove_line_notifier(check_note)
  --end

end
--renoise.song().patterns[renoise.song().selected_pattern_index]:add_line_notifier(check_note)
renoise.tool().app_new_document_observable:add_notifier(check_note)

XXX_BT_01 = vb:button {
  id = 'XXX_BT_01',
  tooltip = 'C-0',
  width = 32,--13,
  height = 32,
  --color = { 0x00,0x00,0x70 },
  --pressed = function() end,
  --released = function() end,
}

--------------------------------------------------------------------------------
-- GUI - TOOL 09
--------------------------------------------------------------------------------
TL09 =
vb:column {
  --- ---
  vb:horizontal_aligner {
    vb:text { text = 'Check Notes' }
  },
  --- ---
  XXX_BT_01
}

What I want to do is a function “check_note” that chases notes written in the pattern editor in the selected note column with the follow player is enabled,with the objective of changing the color of a button “XXX_BT_01”. Something simple.

For the moment, the “check_note” function detects correctly the note (is configured to detect the note with “0” value, the “C-0”).When the cursor of the pattern editor is selecting a row with the note C-0 already written previously, the “XXX_BT_01” button changes to green.If it is another note or empty value, the “XXX_BT_01” button is red.

The problem is that it only works when the tool (with this function) is loaded (“Tools/Reload all Tools” Menu), not in real time, while playing the pattern editor.What lines are missing for the “notifier” to work well?

It is necessary to use these lines?

--song.patterns[spi]:add_line_notifier(check_note)

&

--if song.patterns[spi]:has_line_notifier(check_note) then
  --song.patterns[spi]:remove_line_notifier(check_note)
--end

How would it be correct?

If the function works properly, I could solve many options that I have in mind…

Note : how it should work properly, to check:

  1. Run the function (the tool)
  2. Write randomly several notes inside a track.Some notes should be the “C-0” (note value = 0).
  3. Play the pattern editor with follow player activated (the cursor follows the playback).
  4. What should happen:If the notes are different from C-0, the button is red color. If the notes are = C-0, the button change to green in real time.

My intention is a little broader: check in real time the 12 note colums on selected track, basically chase the notes inside only a track (for good performance)…

Please, help!!!

Thanks! :smiley:

A similar question/idea? -> https://forum.renoise.com/t/help-return-a-function-when-a-note-play-in-pattern-editor/47135

A similar question/idea? → https://forum.renoise.com/t/help-return-a-function-when-a-note-play-in-pattern-editor/47135

Thanks :D! Yes, but this does not help.Long ago I raised something similar, but the conclusion seemed to indicate that it could not be done. However discussing in other forums later with topics related to creating a pianoroll tool,Joule corrected me stating that it is possible to do so. Here I do not want to build a pianoroll tool, but another small thing to identify notes already written in a GUI.

I suppose it is necessary to do something with notifiers, to reactivate on each line or something like that.At the moment, I did not know I could read the notes already writteneven.I am aware that it can influence performance.But there must be some way, and in theory it should be very simple.

The previous function “check note” notify only once, when loading the tool the first time.I suppose I would need to reactivate the notifier when the cursor changes of line in real time (following playback or changing manually the line on each track with the keyboard or mouse), but I do not really know how to do it.

Digging a little bit why I want to solve this issue…

If I can “read” in real time (or with a little delay) a written note in the pattern editor during playback, I can throw any function, for example:

  • Change a button’s color.
  • Register a name, as the name of the note.
  • Stop playback to mark that note through a GUI.
  • Correct or change values (volume, panning, delay…).
  • With a 12 buttons GUI, watch the playback of a melody regardless of the instruments used to detect errors or change melodies.

This line:

renoise.tool().app_new_document_observable:add_notifier(check_note)

Charge function once.Would it be correct to create a function that overrides this notifier and reloads it when the position of the line is changed?Would it be right to approach it like this?

The position of the line is where the cursor is, include pattern, track, note column and line.

Raul, let me see if I can try to explain this as I understand it… Let us say simply you want to read a line from a track ‘in real time’ with what is under the cursor as the Renoise transport is playing, and according to if there is a note or not under the cursor, flash a button (or ‘piano key’) that represents said note. Sounds simple doesn’t it? And you would think that the Renoise LUA api would supply a notifier that when the Renoise transport moves from one line to the next a function is called. Now AFAIK there isn’t such a notifier (as Danoise points out in the other thread. Probably due to slowing the Renoise transport down awful?) So, what to do? Well the only other real idea is to use one of two notifiers. Either the ‘idle notifier’ or use a fast ‘timer notifier’ (probably <20ms) where it is continuously calling a function and in that function we keep extracting what is under the editing cursor or what line information is on a track. However there is a small problem with this approach. We are ‘out of sync’ with the Renoise transport (the closest you can get to getting ‘in sync’ with the transport is in the Formula device.) Lower BPM/LPB you would get the notes, but at higher BPM/LPB you would probably start to miss lines/notes (not to mention flashing buttons and things and I have no idea how long that would take to execute. Probably a loooonnnggg time.) It’s gimmicky now. Joule/Danoise I would imagine would not really entertain the idea of writing a fairly serious tool that does this because of the inaccuracy and because the performance of Renoise LUA sucks. When you want things like ‘real time’ in Renoise LUA, alarm bells ring really loud Raul :slight_smile:

Renoise LUA when all is said and done is just a slow scripting add-on to Renoise. You see if I thought of the idea: ‘Ah! I’ll write a tool that reads in a line from the Renoise pattern as it is playing and flash buttons etc…’ Then suddenly I’d think ‘No wait! I can write the code, but the goddamn computer won’t like it!’

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.

Raul, let me see if I can try to explain this as I understand it… Let us say simply you want to read a line from a track ‘in real time’ with what is under the cursor as the Renoise transport is playing, and according to if there is a note or not under the cursor, flash a button (or ‘piano key’) that represents said note. Sounds simple doesn’t it? And you would think that the Renoise LUA api would supply a notifier that when the Renoise transport moves from one line to the next a function is called. Now AFAIK there isn’t such a notifier (as Danoise points out in the other thread. Probably due to slowing the Renoise transport down awful?)

Yes, in 2010-2011 are several comments from other users that deal with this issue.What I have observed…

-- The currently edited pattern. Never nil.
renoise.song().selected_pattern, _observable
  -> [read-only, renoise.Pattern object]
renoise.song().selected_pattern_index, _observable
  -> [number]

-- The currently edited pattern track object. Never nil.
-- and selected_track_observable for notifications.
renoise.song().selected_pattern_track, _observable
  -> [read-only, renoise.PatternTrack object]

-- The currently edited sequence position.
renoise.song().selected_sequence_index, _observable
  -> [number]

-- The currently edited line in the edited pattern.
renoise.song().selected_line
  -> [read-only, renoise.PatternLine object]
renoise.song().selected_line_index --<------ Does not exist "_observable"!!!!
  -> [number]

It’s probably a decision to avoid dropping performance…

… Either the ‘idle notifier’ …

How can this case be?

On the other hand. creating a function with a timer that bombs constantly could be very beast, right?

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!