Lua: Renoise.song().tracks[].members[].track_Index

There seems to be no way to get what track indexes are part of a group, the same way track index in .group_parent is not available.

These issues could perhaps both be resolved by adding:

renoise.song().tracks[]....track_index  

(Unless I’m missing some fundamental lua trick)

EDIT: Perhaps another option would be to add a renoise.song().groups[] structure. That approach may be beneficial coding wise in many cases.

yes, and also the track[].group_parent should return nil when the track has ran away from it’s parents.

I’m running into the same thing updating my Push Back tool. When the function is called from a group track I will have to

  • iterate over every track to the left of current track (type group)
  • go through it’s group_parent, possibly that one’s group_parent, etc. routing

to get the track indexes of subtracks (to be able to modify their delay_column data in this case).
With the current state I’m just gonna not complete the v1.0 of the tool, I might code it, but I’m not confident it’s gonna run properly on every sys with the turn of a dial you know? :) and it’s just double double inefficient code…
I would really like to know if there’s a better way.

You can create a small helper function to get the track index, by using “rawequal” to compare a track object with renoise.song().tracks. “rawequal” bypasses the object’s comparison operator (which in most cases is not defined anway), and tests object identity instead:

  
function get_track_index(_track)  
 assert(_track, "expected a valid track object")  
 for index, track in ipairs(renoise.song().tracks) do  
 if (rawequal(track, _track)) then   
 return index  
 end  
 end  
end  
  
-- test  
assert(get_track_index(renoise.song().tracks[2]) == 2)  
assert(get_track_index(renoise.song().selected_track) == renoise.song().selected_track_index)  
  

A sneaky way (just for fun): Classes in the renoise API are not read-only. So one can define new member functions for existing renoise.* classes, or replace existing ones.

One can also wrap a getter/setter function pair to define new properties this way. This is done with the global function “property()”:
property(getter, setter) takes a getter and setter (optional) function as parameter. Every time a property gets read, the getter function is called and it’s return value will be put on the stack. Every time you assign the property, the setter function is called with the given argument. The first argument of the passed functions will always be “self”.

In the example below, this is used to create a new “track_index” property to renoise.Track and GroupTrack. Because only a getter is present, assignment is disallowed too and will fire an error, which is exactly what we want.

  
-- Inject a new property "track_index" into the renoise.[Group]Track class.  
-- Define this as early as possible in your script to make the property  
-- as early as possible available. Once it's defined, the property will be   
-- available for every existing and new object of type "renoise.[Group]Track",  
-- so you don't have to define this for every new track that gets created   
-- within Renoise.  
local track_index_property = property(function(self)  
 for index, track in ipairs(renoise.song().tracks) do  
 if (rawequal(self, track)) then   
 return index  
 end  
 end  
end)  
-- and apply it to all track classes in the API  
renoise.Track.track_index = track_index_property   
renoise.GroupTrack.track_index = track_index_property   
  
-- test  
assert(renoise.song().tracks[6].track_index, 6)  
assert(renoise.song().selected_track.track_index, renoise.song().selected_track_index)  
  
-- assignment is not allowed (track_index is read-only)  
assert(pcall(function() renoise.song().tracks[2].track_index = 23 end) == false)   
  

Same thing could be done to create a “member_index” for tracks in groups of whatever else you need.


Oh and all this of course is not a lame excuse. track_index should be available by default in the API.

On the contrary, this is a crazy cool workaround and learning something too :D thx

Awesome !!

I can’t help myself but wonder, is there a way to actually attach a new property with both read and write support?
Simply doing:

renoise.NoteColumn.quantize_value = 0  

… works, but the value is read-only.

local quantize_value_property =   
 property(function(self) return self.quantize_value end,  
 function(self, set) self.quantize_value = set end)  
  
renoise.NoteColumn.quantize_value = quantize_value_property  

… doesn’t work. it gives a ```
*** TestPad.lua:12: C stack overflow

  
what does work is:  

local value = 0
local quantize_value_property =
property(function(self) return value end,
function(self, set) value = set end)

renoise.NoteColumn.quantize_value = quantize_value_property

.. but it doesn't really make any sense, because the property is not unique for each NoteColumn then.  
  
also tried:  

local values = {}
local quantize_value_property =
property(function(self) return values[self] end,
function(self, set) values[self] = set end)

renoise.NoteColumn.quantize_value = quantize_value_property

but that gives me: ```  
*** TestPad.lua:18: unknown property or function 'quantize_value'. can not assign new properties to an object of type 'NoteColumn'  

I know it’s quite a hack, but would be nice to get it working :)/>

I know it’s quite a hack, but would be nice to get it working smile.gif/>

Late reply, but I wanted to mention that this should be possible if you ‘cache’ your objects, which might seem slightly impractical but totally doable. If you don’t cache them, calls like :line() or .lines are technically returning new objects everytime (afaik).

An example that seems to work:

renoise.NoteColumn.MY_DEFAULT_VALUE = 10
renoise.NoteColumn._my_value = renoise.NoteColumn.MY_DEFAULT_VALUE

local my_property = property(
  function(self) return self._my_value end,
  function(self, val) self._my_value = val end
)

renoise.NoteColumn.my_property = my_property

-- Testing

local ncol_1 = renoise.song():pattern(1):track(1):line(1):note_column(1)
local ncol_2 = renoise.song():pattern(1):track(1):line(1):note_column(2)

print(ncol_1.my_property)
print(ncol_2.my_property) -- should print 10

ncol_1.my_property = 50
ncol_2.my_property = 30

print(ncol_1.my_property)
print(ncol_2.my_property)

This is kind of a pandoras box :wink: You could, for example, make custom getters/setters and observables that will bang due to very specific changes to song data (when edited from a tool).