I Need Help With Tracks_Observable

Create a song with more than 1 pattern/sequence.

Execute the following in TestPad.Lua:

  
function tracks_changed(notification)  
 if (notification.type == "insert") then  
 for i = 1, #renoise.song().sequencer.pattern_sequence do  
 renoise.song().sequencer:set_track_sequence_slot_is_muted(notification.index , i, true)  
 print("[DEBUG] Mute slot: " .. notification.index .. ", " .. i)  
 end  
 end  
  
end  
  
if not (renoise.song().tracks_observable:has_notifier(tracks_changed)) then  
 renoise.song().tracks_observable:add_notifier(tracks_changed)  
end  
  

Open terminal.

Select a track.

Press CMD+T

Press CMD+T

Press CMD+T

-=-=-

The code snippet is supposed to PM Mute the track.

First CMD+T mutes a track, but gets it wrong. Subsequent calls don’t work?

PS: The strange behaviour is more obvious if you start the PM with every slot muted. (My bug is related to Grid Pie, but i created a smaller snippet for discussion)

2392 more_obvious.png

Don’t you have to pass an argument to “add_notifier(tracks_changed)” ?

No, not according to the documentation. The ‘notification’ variable exists. That’s not the problem.

More like it’s an “atomic” issue, seen before.

Or, I just don’t understand something and it needs to be explained to me.

Ooooh, notification is actually an argument forced by the method. Ok, thanks for the tip, I’ve used add_notifier on tracks_observable but I didn’t need this at all.

Notifiers often provide their own arguments, but not always.
If you want to know what’s going on, the easiest is to simply rprint those arguments to the console (it’s always a table, or nil).

For something like the track_observable, this might yield

[type] => “swap”
[index1] => 1
[index2] => 6

Something like a track swap will actually generate a lot of notification events (depending on the number of tracks in your song), since it’s not just two tracks switching but a cascading action:

Let’s assume that I swapped track one with track six? This is what actually happens

track at index: 1 and 2 swapped their positions
track at index: 2 and 3 swapped their positions
track at index: 3 and 4 swapped their positions
track at index: 4 and 5 swapped their positions
track at index: 5 and 6 swapped their positions

The track I dragged to position 6 is actually “crawling” up to it’s new position, one track at a time.

For the same reason, it’s good practice to raise a flag (set a boolean variable) on such occasions, and handle it in the idle-time instead of trying to handle each and every notification. Not because it wouldn’t work, but it’s just more CPU efficient this way.

Hi Danoise.

I agree. But look at my code in the first post, line 2.

  
if (notification.type == "insert") then  
  

I’m only running this on insert.

From the documentation.

  
if (notification.type == "insert") then  
 print(("new track was inserted at index: %d"):format(notification.index))  
  

The index returned is the index of the new track. Perfect. This is what i want. So I do (i replaced with 1, for brevity)

  
renoise.song().sequencer:set_track_sequence_slot_is_muted(notification.index, 1, true)  
  

Well, the resulting mute is on the wrong track…

Furthermore, if I do a -1 to compensate:

  
renoise.song().sequencer:set_track_sequence_slot_is_muted(notification.index-1, 1, true)  
  

It’s also on the wrong track?

Ah, now I understand…Almost forgot about this, but the API does have some properties that doesn’t update in linear time, and I suspect it’s an inherent behavior of the lua interpreter to reflect this. I really should have remembered, because this was one of my first stumbling blocks in Duplex. The solution is to use the beforementioned “modified flags” approach (in this case it smell a bit like a workaround, but the approach is sound enough - in some situations you get a huge performance boost).

I’m not even sure if this is something that can/should be fixed in the API? Linear time (and “time” in programming in general) is one of the most confusing aspects of programming (Time Complexity on Wikipedia for the full story). On the other hand, this might not even be related to such matters, or simple to fix - in which case you should just ignore my ravings

Ah, that is indeed more likely to be the case, yes. I still regard myself a beginner in programming theory

[quote=“Bantai”]
2393 masterslotmuted.png

Whatever the explanation, this can’t be right?

The problem is that “inserting a track” is not an atomic operation, and the Lua notifiers get called some when in between a complex operation:

  
InsertTrack in Renoise, internally is:  
 ** a the "real" track (the thing you see in the mixer, the thing with has DSP devices, renoise.song().tracks in Lua) to the song  
 * add a new track into each pattern  
 * add a new track into each slot in the pattern sequence (where the track mutes live)  
 * do a shitload of other track related stuff here and there  
  

** is where your Lua notifier gets called.
At this time there is a new track in the song already, but not in the other places. And that’s why the track index refers to the previous track in the matrix, when being accessed !within! the notifier.

Actually it’s pretty evil that we do send out notifiers to Lua while the document is in inconsistent state. What we should do instead, is sending the notification as soon as all the shit is done, at the end of the internal track inserting stuff. Will try to do so for the next release.


Actually it’s pretty evil to modify the document itself while it’s firing notifications about document changes (muting a track while its being inserted). The track insert notifier is also called when cloning a track or when undo/redoing. There you don’t want to do such things and this will do strange things.

What we’d need instead, is a hook which is called as soon as the user adds a new track, and only then. Something like a post “initialize” function. This would for example also allow to do proper custom DSP or instrument preset customization.


Conner: For now as a workaround postphone the stuff to a timer (as usual), so it’s called after all the internal shit is done. The OneShotIdle class may help to do so:
[luabox]

– delay a function call by the given amount of time into a tools idle notifier

– for example: ´OneShotIdleNotifier(100, my_callback, some_arg, another_arg)´
– calls “my_callback” with the given arguments with a delay of about 100 ms
– a delay of 0 will call the callback “as soon as possible” in idle, but never
– immediately

class “OneShotIdleNotifier”

function OneShotIdleNotifier:__init(delay_in_ms, callback, …)
assert(type(delay_in_ms) == “number” and delay_in_ms >= 0.0)
assert(type(callback) == “function”)

self._callback = callback
self._args = arg
self._invoke_time = os.clock() + delay_in_ms / 1000

renoise.tool().app_idle_observable:add_notifier(self, self.__on_idle)
end

function OneShotIdleNotifier:__on_idle()
if (os.clock() >= self._invoke_time) then
renoise.tool().app_idle_observable:remove_notifier(self, self.__on_idle)
self._callback(unpack(self._args))
end
end
[/luabox]

It works with OneShotIdleNotifier(), cheers!

  
function tracks_changed(notification)  
 if (notification.type == "insert") then  
 OneShotIdleNotifier(0, function()  
 for i = 1, #renoise.song().sequencer.pattern_sequence - 1 do  
 renoise.song().sequencer:set_track_sequence_slot_is_muted(notification.index , i, true)  
 end  
 end)  
 end  
end  
  

That would indeed make cause for less confusion, but doesn’t change the fact that “best practice” in many cases is still to handle changes in an idle notifier of some kind?
Here, I’m especially thinking about track swapping, and when a pattern has been edited. This one is potentially invoked hundreds of times if you delete a pattern’s contents.

Hey guys, hey taktik,

regarding your answer below, has this topic been fixed in latest Renoise 3 version ?

I recently stumbled upon strange side effects which are related to a “track index change listener” which I use in my FaderPort driver. It seems that there occur errors which seem to be caused by race conditions / inconsistent notifications about modified track indexes. Problem is, that this behaviour is not consistently reproducable, means: sometimes errors occur, sometimes not. It feels pretty random.

It drives my crazy, because so far I couldn’t find an error in the LUA code. So I guess it’s a problem of the API. This thread reinforces this impression.

Actually it’s pretty evil that we do send out notifiers to Lua while the document is in inconsistent state. What we should do instead, is sending the notification as soon as all the shit is done, at the end of the internal track inserting stuff. Will try to do so for the next release.


Actually it’s pretty evil to modify the document itself while it’s firing notifications about document changes (muting a track while its being inserted). The track insert notifier is also called when cloning a track or when undo/redoing. There you don’t want to do such things and this will do strange things.

What we’d need instead, is a hook which is called as soon as the user adds a new track, and only then. Something like a post “initialize” function. This would for example also allow to do proper custom DSP or instrument preset customization.