[Solved] swap note_column. Where to save data to keep them?


(Raul (ulneiz)) #8

It’s more than ten times faster here, so if you access it a lot in a big tool it might make a difference. I just wanted to note it since it’s so easy to do it slightly better :slight_smile:

You can have a global rns = renoise.song() and update it via a new document observable.

Where and how to put the “new document observable”?

This is the typical structure that I’m used to use in my recent tools:

--
-- tool name
--

--main globals
-------------------------------------------------------------------------------------------------
dialog = nil
vb = renoise.ViewBuilder()
vws = vb.views
song = renoise.song() -------------------> where and how to put the "new document observable"?????????????
rnt = renoise.tool()
rna = renoise.app()
rns_version = renoise.RENOISE_VERSION
api_version = renoise.API_VERSION

--other globals
-------------------------------------------------------------------------------------------------

--require
-------------------------------------------------------------------------------------------------
require ("lua/xxx") -- more song ???????????????????, include many functions and more windows
require ("lua/yyy") -- more song ???????????????????, include many functions and more windows
require ("lua/zzz") -- more song ???????????????????, include many functions and more windows

--functions (many functions)
-------------------------------------------------------------------------------------------------
function name_function_01()
  local sti = song.selected_track_index --song ???????????????????
  local sii = song.selected_instrument_index --song ???????????????????
  --...
  --...
end
---
function name_function_90()
  rna.window.active_middle_frame = renoise.ApplicationWindow.MIDDLE_FRAME_INSTRUMENT_PHRASE_EDITOR
  song.selected_instrument.phrase_editor_visible = true --song ???????????????????
  song.selected_instrument.phrase_playback_mode = renoise.Instrument.PHRASES_PLAY_KEYMAP --song ???????????????????
  --...
  --...
end

--main content gui
-------------------------------------------------------------------------------------------------
TOOL_GEN_CONTENT = vb:row {
  vb:button {
    text = "Button"
  },
  --...
  --...
}

--show dialog
-------------------------------------------------------------------------------------------------
function show_tool_dialog()
  if ( dialog and dialog.visible ) then dialog:show() return end
  dialog = rna:show_custom_dialog( pht_title, TOOL_GEN_CONTENT, pht_keyhandler )
end

--register menu entry
-------------------------------------------------------------------------------------------------
renoise.tool():add_menu_entry {
  name = "Main Menu:Tools:Name_Tool...",
  invoke = function() show_tool_dialog() end
}
Click to view contents

– tool name

–main globals


dialog = nil

vb = renoise.ViewBuilder()

vws = vb.views

song = renoise.song() -------------------> where and how to put the “new document observable”???

rnt = renoise.tool()

rna = renoise.app()

rns_version = renoise.RENOISE_VERSION

api_version = renoise.API_VERSION

–other globals


–require


require (“lua/xxx”) – more song???, include many functions and more windows

require (“lua/yyy”) – more song???, include many functions and more windows

require (“lua/zzz”) – more song???, include many functions and more windows

–functions (many functions)


function name_function_01()

local sti = song. selected_track_index --song ???

local sii = song. selected_instrument_index --song ???

–…

–…

end


function name_function_90()

rna.window.active_middle_frame = renoise.ApplicationWindow.MIDDLE_FRAME_INSTRUMENT_PHRASE_EDITOR

song. selected_instrument.phrase_editor_visible = true --song ???

song. selected_instrument.phrase_playback_mode = renoise.Instrument.PHRASES_PLAY_KEYMAP --song ???

–…

–…

end

–main content gui


TOOL_GEN_CONTENT = vb:row {

vb:button {

text = “Button”

},

–…

–…

}

–show dialog


function show_tool_dialog()

if ( dialog and dialog.visible ) then dialog:show() return end

dialog = rna:show_custom_dialog( pht_title, TOOL_GEN_CONTENT, pht_keyhandler )

end

–register menu entry


renoise.tool():add_menu_entry {

name = “Main Menu:Tools:Name_Tool…”,

invoke = function() show_tool_dialog() end

}

In all my previous tools I have used local song = renoise.song() within each function. But I would like to know how to make a global one to song = renoise.song(), and then within each function use song. or song: where necessary.

So, where to place and how to place the “new document observable” to avoid the loading error?

Note: inside GlobalMidiActions.lua and GlobalOSCActions.lua is usedoutside the functions a local song = renoise.song , and inside the functions is used song(). or **song():**why? Is it 10 times slower?


(danoise) #9

@Raul: just create the observable anywhere in your main.lua, just like you’re adding the “renoise.tool():add_menu_entry” ,etc.

You can have a global rns = renoise.song() and update it via a new document observable.

Yes, if you are planning to make an auto-launching tool [1], this is needed as the song is not necessarily available at startup time.

With auto-launching tools, there are two particular scenarios to be aware of (that need testing):

  • Starting Renoise with the tool already installed

  • Installing the tool for the first time

I’ve forgotten either one from time to time. Eventually, someone notices and sends me a bug report :smiley:

[1] By “auto-launching”, I mean a tool that doesn’t wait for the user to take some kind of action, but actively “does stuff” on startup.


(joule) #10

Yeah… it’s a tounge-in-cheek operation indeed. I thought of adding this to the speed optimization thread. Do you think it’s correct, or am I missing something in regards to the installation scenario?

song = nil

-- Tool code here.

function setup()
  song = renoise.song()
end

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

-- Or tool code here. It doesn't matter.

-- Optional segment below, showing the simplest way to avoid errors with tools
-- that need "auto-launching".

function main()
  -- Do stuff when the tool is loaded.
  -- Set some global bool and check, if you only want main() being executed once.
end

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

EDIT: The installation issue might be due to the bad habit of executing main() top-level in the tool? It might be better practice to do that via app_new_document_observable as well (and store a bool if you only want to do it once)

EDIT 2: This should be a safe boiler-plate for any scenario? Note that the order in which notifiers are registered is also reflected in the order by which they are executed, which is nice.


(Raul (ulneiz)) #11
local song

-- Tool code here.

function setup()
  song = renoise.song()
end

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

As I understand with this code, “song” is a local, that only serves inside the main.lua file. If you have other files through require, this “local song” would not work. I believe (this I would have to prove it).

The issue would be to find an easy way to define “song = renoise.song()” and that song is a global one that works for all attachments by require( “lua/file_1” )

The mother folder of the tool would have these files:

  • main.lua
  • manifest.xml
  • preferences.xml
  • lua (folder):
    • file_1.lua
    • file_2.lua
    • file_9.lua

Maybe for a global, should it be like that?:

song = nil

function setup()
  song = renoise.song()
end

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

-- Require files

-- Tool code here

:huh:.This should work for all the required files as well, I suppose.I understand that renoise.tool().app_new_document_observable:add_notifier(setup), only runs once when loading a new song, or when loading renoise (which creates a new song as well).

All this is true?I have always thought that in the function setup(), “song” could only be used within this function, not outside (because it must be a local).But I suppose that to declare “song” before, outside as global, will work for all functions inside the entire tool (all the files .lua).

All of that is very important. All the functions of the tool depend on it. And so it would be very simple to invoke " song." or " song:" within all the functions of the entire tool, including the .lua files invoked from require().

I would like this whole thing to be very clear.

Additional note: I would like to know why GlobalMidiAction.lua and GlobalOSCactions.lua use song(). or song(): defining the local previously like this: local song = renoise.song. It is not supposed that local song = renoise.song() is 10 times more faster???


(joule) #12

Ah sorry. I think you’re correct about song = nil being better. Fixed.

The examples you’re referring to are probably made that way not to complicate matters too much for new users - trying to stay on point. (You’ll also see examples using the pattern iterator, even though there are way faster ways to do things). More importantly, there is the rule about not overdoing optimizations. When only a few calls are made, the speed gain of optimizations are negligible.

But since you’re trying to cache renoise.song, I thought I’d share the best way of doing it.

Regarding where variables are accessible. That’s what’s called its “scope”, and is indeed important to understand. A variable will be accessible on the same “level” and all levels below from where it’s declared. For example, if you define local j inside a for loop, it is only accessible within that loop. If you define it right before the for loop, it will remain accessible after the loop (set to the last value). I think you’re correct that “song = nil” will make song accessible from all files. Fun fact: it’s really stored in the _G table as _G.song (G implying global). That’s your tools “environment” table, which is a table with its top level listing everything that is “global”.

http://lua-users.org/wiki/ScopeTutorial


(Raul (ulneiz)) #13

Ah sorry. I think you’re correct about song = nil being better. Fixed.

The examples you’re referring to are probably made that way not to complicate matters too much for new users - trying to stay on point. (You’ll also see examples using the pattern iterator, even though there are way faster ways to do things). More importantly, there is the rule about not overdoing optimizations. When only a few calls are made, the speed gain of optimizations are negligible.

But since you’re trying to cache renoise.song, I thought I’d share the best way of doing it.

Regarding where variables are accessible. That’s what’s called its “scope”, and is indeed important to understand. A variable will be accessible on the same “level” and all levels below from where it’s declared. For example, if you define local j inside a for loop, it is only accessible within that loop. If you define it right before the for loop, it will remain accessible after the loop (set to the last value). I think you’re correct that “song = nil” will make song accessible from all files. Fun fact: it’s really stored in the _G table as _G.song (G implying global). That’s your tools “environment” table, which is a table with its top level listing everything that is “global”.

http://lua-users.org/wiki/ScopeTutorial

Thanks!

Ok,I just built a tool template that has just the specific scheme that I would like to use always in my future tools:

template tool:8071 com.ulneiz.Rodent.xrnx

See how all the global ones are ordered, which can be used in all the .lua files of the tool.Finally, the global song is defined as follows:

rnt = renoise.tool()
---
song = nil
function song_obs()
  song = renoise.song()
end
rnt.app_new_document_observable:add_notifier( song_obs )

In this way, it works correctly when loading renoise for the first time, and also when loading any new song afterwards, but return an error after installing the tool, when executing any function**( …failed in one of its notifiers. main.lua ?? attempt to index global “song” a nil value ) :blush:** :blush: :blush:.What is missing here???If you reload the tools (Tools/Reload all Tools) this error disappears. :unsure: :unsure: :unsure:

In summary:

  1. start renoise: ok!
  2. new song: ok!
  3. install tool: fail!!!

Thus, within each function, it is possible to use song. or song: without defining a previous location (local song = renoise.song ()).

As it is now defined “song” as global, would you get the maximum performance (lower number of calls)?

As I understand it, the global “song” is defined only once, and it must be above the code, at the beginning.

But, how to fix the tool to avoid any error, maintaining the structure and the global song?


(Raul (ulneiz)) #14

Ok**rnt.app_new_document_observable:add_notifier( song_obs )**it only acts when loading a new song, but not when installing a new tool (here it is not loading a new song).Something is missing that says that when you install the tool, update song by reading the function song_obs()

Solving this, everything would be solved, I believe.


(joule) #15

Cool… forgot one thing it seems. You need to run song_obs() ‘manually’ ONCE from the first function you call to catch this edge case.

In your case, it would be a good choice to run it someplace in top of the function that shows your GUI.


(Raul (ulneiz)) #16

Cool… forgot one thing it seems. You need to run song_obs() ‘manually’ ONCE from the first function you call to catch this edge case.

In your case, it would be a good choice to run it someplace in top of the function that shows your GUI.

How? I can not do this:

rnt = renoise.tool()
---
song = nil
function song_obs()
  song = renoise.song()
end

song_obs() -->>>>>>>>>>>>>>>>>>>>> return error!

rnt.app_new_document_observable:add_notifier( song_obs )

I think the GUI has nothing to do, since it does not perform any function where the glogal “song” is required. The problem is executing the function song_obs()

Is not there any _observable to run once after installing the tool?


(joule) #17

Unfortunately not.

Put it at the top of rdn_main_dialog(). Otherwise you’ll try to access song before it’s available. The point being you just have to make sure it’s being ran once (via what the user does), so it also catches this installation scenario.


(Raul (ulneiz)) #18

Unfortunately not.

Put it at the top of rdn_main_dialog(). Otherwise you’ll try to access song before it’s available. The point being you just have to make sure it’s being ran once (via what the user does), so it also catches this installation scenario.

If I do that, it will be necessary to invoke the song_obs() function within all the functions that are responsible for showing a new window (of the same tool).There must be some way to force the reading of a function immediately after installing a tool.

Would this work?

rnt = renoise.tool()
---
song = nil
function song_obs()
  song = renoise.song()
end

if ( song == nil ) and not ( rnt.app_new_document_observable:has_notifier( song_obs ) ) then
  rnt.app_new_document_observable:add_notifier( song_obs )
end

(Raul (ulneiz)) #19

Perfect!!! Work!!! :slight_smile: :slight_smile: :slight_smile:


(joule) #20

Nah… I don’t think so. Is your GUI+buttons really working even when installing now?

I think this is the practical solution… put at the top of main.lua.

song = nil

function song_obs()
  song = renoise.song()
end

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

if pcall(function() return renoise.song() end) then
  song = renoise.song() -- catching installation scenario
end

(Raul (ulneiz)) #21

This seems to be the most appropriate order:

-------------------------------------------------------------------------------------------------
--local global variables/tables
main_dialog = nil
local rdn_main_title = " Rodent"
pht_version = "1.1 build 0001"
rns_version = renoise.RENOISE_VERSION
api_version = renoise.API_VERSION
---
vb = renoise.ViewBuilder()
vws = vb.views
rna = renoise.app()
rnt = renoise.tool()
---
song = nil
function song_obs()
  song = renoise.song()
end
if ( song == nil ) and not ( rnt.app_new_document_observable:has_notifier( song_obs ) ) then
  rnt.app_new_document_observable:add_notifier( song_obs )
end

-------------------------------------------------------------------------------------------------
--require
require ( "lua/file_1" )
require ( "lua/file_2" )
--
require ( "lua/keyhandler" )

-------------------------------------------------------------------------------------------------
--main functions

--
--

song = nil – the global

function song_obs() – the function of conversation

song = renoise.song()

end

if ( song == nil ) and not ( rnt.app_new_document_observable:has_notifier( song_obs ) ) then – the condition: song == nil ?

rnt.app_new_document_observable:add_notifier( song_obs ) – the new document observable

end

If this works correctly, it would be most comfortable for a tool that includes several windows. song is only defined once.

Nah… I don’t think so. Is your GUI+buttons really working even when installing now?

Yes!


(Raul (ulneiz)) #22

You can try it yourself with this new template:8072 com.ulneiz.Rodent.xrnx

It seems to work well in all scenarios.


(joule) #23

Nope… not when installing by double-clicking.

Remove the “song_obs()” call from the dialog function. No cheating :slight_smile:

My solution above seems to work universally.


(Raul (ulneiz)) #24

Nope… not when installing by double-clicking.

Remove the “song_obs()” call from the dialog function. No cheating :slight_smile:

My solution above seems to work universally.

^_^I forgot to delete it :blink:

-------------------------------------------------------------------------------------------------
--local global variables/tables
main_dialog = nil
local rdn_main_title = " Rodent"
pht_version = "1.1 build 0001"
rns_version = renoise.RENOISE_VERSION
api_version = renoise.API_VERSION
---
vb = renoise.ViewBuilder()
vws = vb.views
rna = renoise.app()
rnt = renoise.tool()
---
song = nil
function song_obs()
  song = renoise.song()
end

-- catching new song / start renoise
rnt.app_new_document_observable:add_notifier( song_obs )

-- catching installation scenario
if pcall ( function() return renoise.song() end ) then
  song = renoise.song()
end

-------------------------------------------------------------------------------------------------
--require
require ( "lua/file_1" )
require ( "lua/file_2" )
--
require ( "lua/keyhandler" )

-------------------------------------------------------------------------------------------------
--main functions

Yes now!!! Thanks!

song = nil

**function song_obs()
song = renoise.song()
end

– catching new song / start renoise
rnt.app_new_document_observable:add_notifier( song_obs )

– catching installation scenario
if pcall ( function() return renoise.song() end ) then
song = renoise.song()
end**

Ufff, to have to get here.For me, this “song” issue has always been a nuisance. This should be done in the API in another way so as not to have so many problems with renoise.song ().But hey, this seems to be the best solution so far I’ve seen.


(Raul (ulneiz)) #25

Then the final tool template looks like this (work fine!):8073 com.ulneiz.Rodent.xrnx

Look at this!!!.. To define some important globals:

  • vb = renoise.ViewBuilder() --ok, easy!
  • vws =vb.views --ok, easy!
  • rna = renoise.app()–ok, easy!
  • rnt = renoise.tool()–ok, easy!
  • song = renoise.song() --no ok, problems for this global!!! In addition, in most tools is the most needed!

It would be nice to detail this whole thing in the thread of renoise tool speed optimization initiative :https://forum.renoise.com/t/renoise-tools-speed-optimization-initiative/45510

Especially in the type of more complex tools, like this template, you can invoke several internal windows in the same tool, including several files through require(“file”).


(joule) #26

Yeah I’ll make a post there eventually.


(Raul (ulneiz)) #27

Yeah I’ll make a post there eventually.

Perfect! :slight_smile: