It seems to me like everyone is forgetting the biggest optimization of them all here - line.is_empty. This happens so frequently and is so fast to check, that a special case in the if statement should be made for this condition.
Also, if line is not empty, initially checking note_column.is_empty should probably be faster than making a string comparison on each and every ncol. The only difference between “—” and note_colum.is_empty is that the latter includes dly/pan/fx-column et c. That doesn’t matter in this case, though… You kind of gain by adding a check for “not note_column.is_empty” outside here, just because it happens so often.
Do these two to avoid a lot of extra calls… The other suggestions are good as well, but these are the real heavy ones.
^^ ^^ ^_^,I thought of a patch which solves the continuous error of the first line.I do not like the result of the set code, but at least it works:
--IMPORTANT NOTE, Renoise v3.1 not read correctly the fist line of each pattern using a timer or idle_obserble. Is necessary use a filter for line 01.
--check first line only of each pattern (filter)
function tnc_check_pattern_first_line( song, tool, spi, sti, stpl )
song = renoise.song()
tool = renoise.tool()
if vb.views['TNC_CB'].value == true then
local function check_pattern()
spi, sti, stpl = song.selected_pattern_index, song.selected_track_index, song.transport.playback_pos.line
if song.selected_pattern_index == spi then
--print ( "spi:", spi )
--print( "note line 01", song:pattern( spi ):track( sti ):line( 1 ):note_column( 6 ).note_string )
--- ---
for i = 1, 12 do
if not ( song:pattern( spi ):track( sti ):line( 1 ).is_empty ) then
-- line 01, note column i
--if song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string ~= '---' then
if not ( song:pattern( spi ):track( sti ):line( 1 ):note_column( i ).is_empty ) then
if ( song:pattern( spi ):track( sti ):line( 1 ):note_column( i ).note_value < 120 ) then -- 120 = OFF, 121 = empty
--vb.views["TNC_NC_"..i..""].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string
vb.views["TNC_NC_"..i..""].text = song:pattern( spi ):track( sti ):line( 1 ):note_column( i ).note_string
vb.views["TNC_NC_"..i..""].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
end
else
end
--if song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string == 'OFF' then
if song:pattern( spi ):track( sti ):line( 1 ):note_column( i ).note_string == 'OFF' then
vb.views["TNC_NC_"..i..""].text = 'OFF'
vb.views["TNC_NC_"..i..""].color = { 0x40,0x00,0x00 }
end
end
vb.views["TN_NC_NM_"..i..""].text = string.format(" %s", song:pattern( spi ):track( sti ):line( 1 ):note_column( 1 ).note_string )
end
--- ---
end
end
song.selected_pattern_index_observable:add_notifier( check_pattern ) --observable for pattern
tool.app_new_document_observable:add_notifier( tnc_check_pattern_first_line )
else
if tool.app_new_document_observable:has_notifier( tnc_check_pattern_first_line ) then
tool.app_new_document_observable:remove_notifier( tnc_check_pattern_first_line )
end
end
end
--- ---
--tnc_change_buttons ( with timer: line_timer() ). Read all the lines except the first.
function tnc_change_buttons( song, spi, sti, stpl, i, note_01 )
song = renoise.song()
spi, sti, stpl = song.selected_pattern_index, song.selected_track_index, song.transport.playback_pos.line
if song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER then
--[[
-- note column 1. Example:
if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string ~= '---' then
vb.views["TNC_NC_01"].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string
vb.views["TNC_NC_01"].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
end
if song:pattern( spi ):track( sti ):line( stpl ):note_column( 1 ).note_string == 'OFF' then
vb.views["TNC_NC_01"].color = { 0x40,0x00,0x00 }
end
]]
--- ---
if not (song.selected_line_index == 1 ) then
for i = 1, 12 do
if not ( song.selected_line.is_empty ) then
-- note column i
--if song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string ~= '---' then
if not ( song.selected_line:note_column( i ).is_empty ) then
--if ( song.selected_line:note_column( i ).note_string ~= '---' ) then
if ( song.selected_line:note_column( i ).note_value < 120 ) then
--vb.views["TNC_NC_"..i..""].text = song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string
vb.views["TNC_NC_"..i..""].text = song.selected_line:note_column( i ).note_string
vb.views["TNC_NC_"..i..""].color = tnc_tr_color()--{ 0x00,0x70,0x00 }
end
else
end
--if song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string == 'OFF' then
if song.selected_line:note_column( i ).note_string == 'OFF' then
vb.views["TNC_NC_"..i..""].text = 'OFF'
vb.views["TNC_NC_"..i..""].color = { 0x40,0x00,0x00 }
end
end
vb.views["TN_NC_NM_"..i..""].text = string.format(" %s", song:pattern( spi ):track( sti ):line( stpl ):note_column( i ).note_string )
end
end
--- ---
vb.views["TNC_TXT_LNE"].text = string.format( "%.3d", stpl - 1 ) --for read number of line
end
end
--activate_tnc (check_timer, ENABLED/DISABLED)
TNC_CB = vb:checkbox { id = 'TNC_CB', value = false, notifier = function() tnc_check_pattern_first_line() line_timer() end, tooltip = "Enable/Disable TNC.\n" } --checkbox hidden
---
function tnc_cb_bt_ntf( song )
song = renoise.song()
if vb.views['TNC_CB'].value == false then
vb.views['TNC_CB'].value = true
vb.views['TNC_CB_BT'].color = { 0x00,0x70,0x00 }
vb.views['TNC_CB_BT'].text = 'ENABLED'
--- ---
vb.views['TNC_TXT_TR'].text = song.tracks[song.selected_track_index].name
vb.views['TNC_TXT_TR'].color = song.tracks[song.selected_track_index].color
else
vb.views['TNC_CB'].value = false
vb.views['TNC_CB_BT'].color = { 0x40,0x00,0x00 }
vb.views['TNC_CB_BT'].text = 'DISABLED'
end
end
---
TNC_CB_BT = vb:button {
id = 'TNC_CB_BT',
width = 74,
height = 22,
text = 'DISABLED',
color = { 0x40,0x00,0x00 },
notifier = function() tnc_cb_bt_ntf() end,
tooltip = 'Enable/Disable TNC.\n'
}
local CurrentLine = -1
function tnc_check_playback_line( song, spi, stpl, stel )
song = renoise.song()
--print("pos.line: ", song.transport.playback_pos.line )
---
if ( CurrentLine == song.transport.playback_pos.line ) then
return --<-- Important for not repeat the same function unnecessarily. Run the "tnc_change_buttons()" function only once for each line.
else
CurrentLine = song.transport.playback_pos.line
--print("pos.line: ", song.transport.playback_pos.line )
tnc_change_buttons()
end
--[[
if (CurrentLine == song.transport.edit_pos.line) then
return
else
CurrentLine = song.transport.edit_pos.line
print("x")
end
]]
---
end
function line_timer()
if vb.views['TNC_CB'].value == true then
if not renoise.tool():has_timer( tnc_check_playback_line ) then
renoise.tool():add_timer( tnc_check_playback_line, 5 ) --timer (ms) 5 = fast max, 20 = low, 40 = very low
end
--- ---
--renoise.tool().app_new_document_observable:add_notifier( tnc_tr_chng )
--renoise.tool().app_new_document_observable:add_notifier( tnc_tr_color )
--renoise.tool().app_new_document_observable:add_notifier( tnc_check_pattern_first_line )
--- ---
else
if renoise.tool():has_timer( tnc_check_playback_line ) then
renoise.tool():remove_timer( tnc_check_playback_line )
end
--- ---
--- ---
--tnc_df() --default values
end
end
How it works:
- An observable for the pattern is responsible for reading only the notes of the first line: tnc_check_pattern_first_line().
- Separately, a timer ( line_timer() ) is responsible for reading the rest of lines ( if not (song.selected_line_index == 1 ) then ), through the functiontnc_change_button().
Thus, point 1 changes the button properties only on line 1, point 2 changes the button properties of the rest of the lines throughout the song.Both functions include a switch (a checkbox to manually enable or disable).
I have used .is_emty also:
(1) if not ( song:pattern( spi ):track( sti ):line( 1 ).is_empty ) then
(2) for i = 1, 12 do ...
if not ( song:pattern( spi ):track( sti ):line( 1 ):note_column( i ).is_empty ) then
-------------
(1) if not ( song.selected_line.is_empty ) then
(2) for i = 1, 12 do
if ( song.selected_line:note_column( i ).note_value < 120 ) then
With these lines inserted into the codeI guess it will free up more CPU processes.
…
To verify that the tool works well with the “timer (up to 5ms or more) + CPU used and speed (BPM and LPB)”, I do this (print with the terminal):
local CurrentLine = -1
function tnc_check_playback_line( song, spi, stpl, stel )
song = renoise.song()
print("pos.line: ", song.transport.playback_pos.line ) --<----- ACTIVATING THIS LINE
---
if ( CurrentLine == song.transport.playback_pos.line ) then
return --<-- Important for not repeat the same function unnecessarily. Run the "tnc_change_buttons()" function only once for each line.
else
CurrentLine = song.transport.playback_pos.line
--print("pos.line: ", song.transport.playback_pos.line )
tnc_change_buttons()
end
end
When run the tool, looking the terminal, he prints the value of each line several times in sequence, then I can do tests by increasing the values of BPM and LPB.If a value (pos.line: X) is skipped, I understand that the tool may return an error, which may not coincide in time with this print, but there will probably be some error reading the data. Example:
Work ok! :
------------------------------------------(the terminal)
pos.line: 1
pos.line: 1
pos.line: 1
pos.line: 2
pos.line: 2
pos.line: 3
pos.line: 3
pos.line: 3
pos.line: 4
pos.line: 5
pos.line: 6
pos.line: 6
pos.line: 7
pos.line: 7
pos.line: 7
…
Will work with random errors:
------------------------------------------ (the terminal)
pos.line: 1
pos.line: 1
pos.line: 1
pos.line: 2
pos.line: 2
pos.line: 3
pos.line: 3
pos.line: 3
pos.line: 5 <<----(looking)------pos.line: 4 not exist!!! (the tool will probably return errors)
pos.line: 6
pos.line: 6
pos.line: 7
pos.line: 7
pos.line: 7
…
The second case demonstrates the lack of precision when using a fast timer:
- Number 1 or 7 is repeated three times
- Number 2 or 6 is repeated two times
- Number 3 or 5 is repeated a time
- Numeber 4 not exist! The tool will probably return random errors already.
…
Compiling everything, the whole situation is a sucks.
- To chase the line during the playback some kind of timer is needed…
- This timer will return errors at high speeds, at least > 200 BPM and > LPB.
- Under the hood of Renoise, using the timer reading the first line of each pattern returns errors, in 95% of cases.It is necessary to analyze the first line separately.The resulting code is not very elegant and mixes things with rare tricks.
Maybe Taktik could go deeper into this subject under the hood of Renoise to offer us a playback_pos.line _observable reliable enough to use at a reasonable speed, at least for those using powerful CPUs.Most related searches on the forums are from 2010/2011.It’s 2017.Have a playback_pos.line _observable would open the doors to a lot of interesting tools!
Danoise, you could discuss this with Taktik for an upcoming version of Renoise?