This time I’ve made it with a Duplex application. Or this is what I suspect. My renoise crashes like so:
I open renoise, have this Duplex folder as my duplex folder. (Dropbox link to a zip archive of my current Duplex tool folder) It contains for TouchOSC a new control map, a new configuration, and my guessed main offender; a new Duplex Application called VolPanDel.lua.**
I open this song. 3753 MuBR4_4.xrns (Some other songs will crash with this behaviour too, but some others won’t… This one was short and had no external VSTs or VSTis)
I press play.*
When the song reaches the end of pattern and pattern would change, renoise crashes.
*My play is ‘space-bar’, will play from the start of the pattern, will have cursor follow on. IF I PLAY BY CLICKING WITH THE MOUSE FROM THE SEQUENCE LIST PLAY , I will not have pattern follow on and renoise will not crash.
**VolPanDel.lua is running by default when I open renoise.
More notes: I suspect this is related to my Duplex application because
a ) I’ve not seen anything like this with this or any other song before I started this wretched piece of code.
b )I already know My Duplex application is flawed. It will throw an error (sumthing about notifiers failing. again…) when renoise is loaded and one tries to change track. But it’s a clean error.
c ) Renoise will not crash if I first open the tool browser and disable Duplex altogether.
Are there T-shirts up for grabs for people crashing officially released renoise versions with say, 10 or so different methods?
Well, haven’t actually confirmed the bug here, but props for discovering another one - crash bugs needs to be squashed!!
But when you are saying some things are flawed in the application, what exactly are you thinking about?
I’m thinking perhaps you are doing something in an unexpected way, which is why the bug hasn’t been discovered… and this time, I haven’t had the time to look into the actual code
Hey, not a bad idea. A special 10xBUG combo edition
OK a quick glance at the log file reveal that something is up with the use of the variable “self”
Error Message: ./Duplex/Applications/VolPanDel.lua:264: attempt to index local 'self' (a nil value)
So I looked for the line in the application - this is what it looks like (of course, you already know about this, but if someone else is reading too…)
function VolPanDel:update_faders()
local faders = {
[1] = "pan",
[2] = "vol",
[3] = "del"
}
oprint(self)
for index, value_type in pairs(faders) do
local fader_name = "_"..value_type.."f" --e.g. "_panf"
local c = self[fader_name]
--print("updating "..value_type)
c.value = self.get_value(value_type) or 0 --c.value
c:force_update()
end
end
There’s some lua terms being mixed together here, let me try and clarify.
“self” is basically lua’s way of telling that you’re addressing the class itself - think about the class as a great big table of values (because, in lua that is what a class is) When you create the table holding the values, you are using the prefix “local” to avoid having to use the class.
This is good for several reasons: most often local variables (in this case, a table) both lead to faster code execution,
and you don’t have to add each and every variable you use in the application to the class itself. You are already doing this right in several other places.
(1) The error is thrown since you are iterating through a local variable, but the value is already made available to you through the iterator
local faders = {"pan","vol","del"}
for k,v in ipairs(faders) do
print(k,v)
end
This will output
1 pan
2 vol
3 del
(note that I don’t assign an index to the entries in the table?
This is not needed, as lua tables cannot contain gaps, they are always numbered consecutively, 1,2,3,4…)
(2) A few lines later, I can see that you’re calling self.get_value, I can tell you this will fail too, but it’s easy to fix. Just add a colon like this: self:get_value
Again, this is because lua is a little different when you’re writing for classes and not just plain functions.
(3) No need for the “do” statement in build_app - it might not be obvious, but build_app is automatically being invoked by the class you are extending, Application
A good practice would to only add the variables that need to be present throughout the time the application is running - in your case, it could be “last_update_at”
You can define those class variables in the __init method, this is the special method being called once the class is initialized -
Thanks for the swift reply. It’s excellent to get this kind of support for a program! I don’t think it gets mentioned enough so just getting that out. Now, for the issues:
I feel that I’ve got the main idea about classes, but since all this lua-renoise-tool-business as a whole IS my first dive into oo-programming, its very possible I’ve got some misunderstandings on the subject. Hope I’ll get the hang of it. But, yes, I still don’t get where the error comes from. I feel it’s something obvious but I just can’t see it.
On the ‘self’ and classes, wouldn’t that be that ‘self’ actually references to an object instance of that class? If not, maybe that’s something I’ve misunderstood… (BTW I’m under the impression that Lua is probably not the ‘golden standard’, when it comes to oo, classes and stuff like that. But I’m really fond of Lua’s flexibility. I’ve gathered the whole classes-thing is just an ‘Lua implementation’ on the subject, and it does not exist natively in Lua?)
Okay. I feel the answer is here. Somewhere. I’ll just have to bend my mind a little. (Well for starters, yep, I knew about the no-gaps, ‘auto-indexed’ tables. I’m not entirely sure why I chose to declare the table the way I did… I’m sure there was some reason. Or then it was just some weird illogical idea. But I think the main thing I don’t get in your reply here is: what is this local variable I’m iterating through, that is provided by the iterator? If I’ll get that, I think I’ll crack this nut.
For some background on the application: I’ve (well, think I’ve) stored pointers to the Duplex UISlider -components in the class(or the object instance of it) at creation time. These pointers (I think…) reside at my_volpandelobject._panf, my_volpandelobject._delf, my_volpandelobject_volf. (Substitute “my_volpandelobject” with whatever Duplex adresses it when creating the application) These are the pointers I’m trying to issue a ‘force_update()’ through. As I look at my code, I don’t see exactly why I’ve chosen to go over them by iteration. I think I could’ve simply used something like:
function VolPanDel:update_faders()
self._panf.value = self:get_value("pan") or 0
self._panf:force_update()
self._volf.value = self:get_value("vol") or 0
self._volf:force_update()
self._delf.value = self:get_value("del") or 0
self._delf:force_update()
end
This is SO much less obfuscated and tells much better what I’m actually trying to do with the function. 1)read current note’s pan/vol/del value from the note column into the UISlider’s ‘value’-field. 2)Update the UISlider according to the value. Now whether this is the right way to do it, I’m not sure. I don’t know. But this is what I’m trying to do with the function. Maybe this is where I’m doing something unexpected…?
I don’t get what exactly is going wrong with the (admittedly obfuscated) iteration method, though. As I see it, I’m just trying to do this:
self._delf:force_update()
But I’m doing it like this:
local pointer="_delf"
self[pointer]:force_update()
Is this not the same thing? And I’m really baffled at why the “self” is nil.
Yep… that’s a miss for me. If I’ve understood correctly, that could also work with: self.get_value(self, value_type), no? Or that self:get_value(value_type) is actually analoguous to self.get_value(self, value_type). Again, not sure, but this is how I think it goes…?
Ah, yes, those. Those are there PURELY for the sake of auto-indetation and indentation based code folding in Vim. Wrapping the long, tedious block into do-end chunk makes it auto-indent nicely. Actually, I’ve used it when defining classes too. I know that class functions (since they are by nature global ones) can be declared anywhere in the code, but it’s nice to keep them organized under the class declaration. So I’ve done something like this:
class "MyMegaClass"
do
function MyMegaClass:__init()
...blah
end
function MyMegaClass:do_something()
...blargh
end
...etc
end
which indents AND most importantly FOLDS in Vim nicely. I gather since the functions are global it’s not a big deal to wrap them in a do-end chunk.
Ah, you mean in the class (or object instance). Yes. I’ll check what’s going on with that, don’t exactly remember everything I added in there. But I think pointers to the UISlider-components fall into this category? Or is there some other method of accessing them? I’m not very educated in the matters of Duplex, I’m afraid…
Always! But I still don’t understand the error message properly… />/>/> Hope the bug gets sorted out. Any luck crashing?
Absolutely, yes - on reading my reply, I can see I was being unclear.
“self” is lua’s oo way of referencing to a class property/method from “within”, other languages have the similar notion of “this”.
To access a variable from the “outside” (a static property), you would write VolPanDelay.my_property_name (this is how the default options, mappings etc. are defined in most of my applications)
Yes, the lua language is not oo as such, but we are fortunate enough to have a lua runtime which has the basics covered thanks to the compiled-in luabind module
Tell me about it. Sometimes, I work with several languages in a day. Can make your head spin
The pointer will become nil only if you are calling the method statically - as in, from the “outside”…
I guess theoretically, they are the same, but the first example is just so much easier to read.
Indeed. It’s a matter of taste which one you prefer, the only exception being when you need to address the superclass (the class you are extending).
For example, this is how the Application is started from the similarly named method in your script:
Application.start_app(self)
Without the “self”, the Application would be called statically, and our VolPanDelay properties and methods would not be carried over.
If you are doing things “neatly”, you want to define your properties (class variables) in the __init method. But lua will allow you to define them at any point,
and not complain if you decide to define “self.my_class_variable” at a later point - it’s just a matter of preference really.
Sure, it’s a lot to chew on when moving into application-writing territory but the Application class is designed to be fairly simple, and allow you to focus on the logic.
Most of the complexity in Duplex deals with external communication with OSC, MIDI devices. The framework tries to hide that part, as it quickly can get messy
First, sorry for the quality of my English. Sorry for my incompetence in programming. Thank to the developers of Renoise and duplex these programs are excellent.
I post in this topic cause i think that my question is related to UiSlider values update.
I have a BCR-2000 and I tried to change sequencer.lua for use with, i have change a bit the behavior of the application.
This works quite well with two BCR-2000 in chain, it makes a 16-step sequencer.
changes:
-Line-level displays the edited line not the played line.
When a step is pressed, the edited line goes to this position, allowing for example to change the note with a keyboard in rec mode.
But this change create bad behavior when changing page, it automatically returns to the page that contains the edit step.
-The buttons are added to navigate through the lines or change the note of the step.
But a button increase the value and the other decrease it. And the values are not updated on sliders when they are changed in Renoise, so not very good.
-The note transpose buttons can go to value 120 note off, 121 clear note.
-I tried to create a line of slider as the line of steps to change the notes.
-I change that “bring focus to track” work when i hold keys, before this change it send to the track one and made it impossible to hold step buttons.
The biggest problem of all this is that I am not programmer and I’m not sure of what i did.
What i would like to improve but I do not understand at all:
-Update values of sliders
-Turn in a direction to increase their value, and the other way to decrease.
I have looked at the forums comparing with other applications. But i can’t understand how to do. This makes it all this change less interesting.
If someone can explain to me how to do that it would be great.
Thank to all of you, i hope to contribute a little bit the different file that i use are joined, if someone want to play with.
Sorry,
I just forget to mention that
the modifications made in the stepsequencer app are preceded and followed by —???
I create functions to update the new sliders but they are useless, i left them empty to avoid errors.
And another version to avoid errors when turning the Encoders4 to modify the notes
I have briefly looked into your application, and from what I can understand you’ve added a bunch of new mappings which control the editing line, the current note etc.
But, having created those methods in the application (increase edit position by one line, increase by 16 lines, etc.) then you assign such mappings to a fader/dial?
It would seem to me that such a type of mapping (increase by 1 or 16) would be best suited for being controlled by a UIButton type mapping instead of a UISlider?
Nevertheless, it would be quite simple to work with UISliders. As you have already worked a bit with them, you know how to create them, assign a method handler etc.
So, to answer your questions:
In order to update the value of a slider you simply call “set_value” with the value you wish to set. By default, a UISlider has a range between 0-1, and setting the value to anything higher will simply leave the slider set to the maximum possible value. Values below zero are not supported. Increase the “ceiling” if you need to work with a larger range (but often, it is a better approach to scale the value afterwards, as a range of zero to one is very easy to work with). There is an additional argument, “skip_event”, which will allow you to not update the physical/on-screen slider after having assigned a new value.
By default, a UISlider will adjust it’s value whenever it is being changed by the user (on the hardware, or via the on-screen control). So, this is really not something you need to do yourself. However, if you mean that you wish to detect which direction the slider is being moved (up or down), then you have no choice but to keep a local copy of the last value and compare it with the most recent value (a “hackish” way to achieve this would be to use the internal property “_cached_value” from the UISlider class, as this should in fact represent the very value we are looking for).
Feel free to ask more Duplex related questions, and I’ll happily attempt to answer
I waited for my answer, because I was hoping to achieve what I wanted.
This is not the case, but I managed to see the slider in the right position when the application starts using this:
----
function StepSequencer2_33:_build_select_grid_note()
if self.mappings.select_grid_note.group_name then
local orientation = self:_get_orientation()
for track_idx=1,self._track_count do
for line_idx=1,self._line_count do
local x,y = track_idx,line_idx
if not (orientation==VERTICAL) then
x,y = y,x
end
local note = renoise.song().selected_pattern.tracks[track_idx]:line(
line_idx).note_columns[1]
local newval = note.note_value
local position = newval/121
local c = UISlider(self.display)
c.group_name = self.mappings.select_grid_note.group_name
c.tooltip = self.mappings.select_grid_note.description
c:set_pos(self.mappings.select_grid_note.index or 1)
c.toggleable = false
c.flipped = true
c.value = position
c:set_orientation(self.mappings.select_grid_note.orientation)
c.x_pos = x
c.y_pos = y
c.active = false
c.on_change = function(obj)
if (not self.active) then
return false
end
local note = renoise.song().selected_pattern.tracks[track_idx]:line(
line_idx).note_columns[1]
local newval = note.note_value
if (newval >= 0 and newval <= 121) then
local noteval = self._select_grid_note.value*121
note.note_value = noteval
end
local msg = string.format(
"StepSequencer2_33: position changed to %X",note.note_value)---position)
renoise.app():show_status(msg)
end
self:_add_component(c)
self._select_grid_note = c
end
end
end
end
---
I can change correctly Uislider position while I turn left down right note it increases.
But if I quickly turn the knob on the BCR2000 Renoise slowdown.
with this:
function StepSequencer2_33:_build_select_note()
if self.mappings.select_note.group_name then
local c = UISlider(self.display)
c.group_name = self.mappings.select_note.group_name
c.tooltip = self.mappings.select_note.description
c:set_pos(self.mappings.select_note.index or 1)
c.toggleable = false
c.flipped = true
c.value = 0
c:set_orientation(self.mappings.select_note.orientation)
c.on_change = function(obj)
local positionactuel = self._select_note.value
if (not self.active) then
return false
end
local positionactuel = self._select_note.value
local note = renoise.song().selected_note_column
local newval = note.note_value
if (newval >= 0 and newval <= 121) then
local noteval = self._select_note.value*121
note.note_value = noteval
end
local msg = string.format(
"StepSequencer2_33: position changed to %X",note.note_value)---position)
renoise.app():show_status(msg)
end
self:_add_component(c)
self._select_note = c
end
end
----
But I can’t make the Slider changes its value if I change the note from Renoise.
Thank you for your explanation, it helps me to better understand what I do. And I realize that I have made a number of things without understanding it very well.
The final goal was to make an application that looks like an hardware sequencer a bit like this http://www.doepfer.de/maq.htm
with a line to change the notes in Renoise, another for Velocity…
a mix between the stepsequencer and NOW apps, but the goal seems far away.
The realtime tracking of notes changes is done automatically via line notifiers, but you need to look into the _update_grid() method to see if your dials are being assigned a new value
(and don’t forget to supply the second boolean argument to the dials - “skip_event” - or they will behave as if you manually turned them, creating a feedback loop)
N.O.W. is a beast with thousands of lines of code, so no wonder if you’re feeling a bit overwhelmed! The StepSeq is much more simple, and being able to control it with dials instead of buttons is definitely something that we should look into. As it was originally written by daxton for the Launchpad, so far it has been for grid controllers only