Notifier Feedback Loop Detected. Help! I'm Screwed.

I’ve coded myself into a dead end and I’m feeling a bit hopeless. Hopefully when I wake up tomorrow someone will have solved this for me? Pretty please?

The code (doesn’t do anything useful yet…)

Click to view contents
  
  
--[[Globals]]--  
  
local my_interface = nil  
local vb = nil  
local gridpie_idx = nil  
local x_pos = 1  
local y_pos = 1  
  
  
--[[Future]]--  
  
local matrix_width = 4  
local matrix_height = 4  
local matrix_cells = table.create{}  
local matrix_view = nil  
  
  
--------------------------------------------------------------------------------  
-- Keyboard input  
--------------------------------------------------------------------------------  
  
function key_handler(dialog, key)  
  
 if (key.name == "esc") then  
 dialog:close()  
 else  
 return key  
 end  
  
end  
  
  
--------------------------------------------------------------------------------  
-- Build GUI Interface  
--------------------------------------------------------------------------------  
  
function build_interface()  
  
 -- Init VB  
 vb = renoise.ViewBuilder()  
  
 -- Buttons  
 local button_view = vb:row {  
 vb:text {  
 text = "x:",  
 font = "mono",  
 },  
 vb:valuebox {  
 min = 1,  
 value = x_pos,  
 notifier = function(val)  
 x_pos = val  
 adjust_grid()  
 end  
 },  
 vb:text {  
 text = " y:",  
 font = "mono",  
 },  
 vb:valuebox {  
 min = 1,  
 value = y_pos,  
 notifier = function(val)  
 y_pos = val  
 adjust_grid()  
 end  
 },  
 }  
  
 -- Checkmark Matrix  
 local matrix_view = vb:row { }  
 for x = 1, matrix_width do  
 local column = vb:column { margin = 2, spacing = 2, }  
 matrix_cells[x] = table.create()  
 for y = 1, matrix_height do  
 matrix_cells[x][y] = vb:checkbox {  
 value = false,  
 notifier = function(val)  
 toggler(x, y, val)  
 end  
 }  
 column:add_child(matrix_cells[x][y])  
 end  
 matrix_view:add_child(column)  
 end  
  
 -- Racks  
 local rack = vb:column {  
 id = "my_interface",  
 uniform = true,  
 margin = renoise.ViewBuilder.DEFAULT_DIALOG_MARGIN,  
 spacing = renoise.ViewBuilder.DEFAULT_CONTROL_SPACING,  
  
 vb:column {  
 vb:horizontal_aligner {  
 mode = "center",  
 button_view,  
 },  
 },  
  
 vb:space { height = 10 },  
  
 vb:column {  
 vb:horizontal_aligner {  
 mode = "center",  
 matrix_view,  
 },  
 },  
  
 }  
  
 -- Show dialog  
 my_interface = renoise.app():show_custom_dialog("Grid Pie", rack, key_handler)  
  
end  
  
--------------------------------------------------------------------------------  
-- Abort  
--------------------------------------------------------------------------------  
  
function abort()  
 print("TODO: Abort!")  
end  
  
--------------------------------------------------------------------------------  
-- Access a cell in the matrix view  
--------------------------------------------------------------------------------  
  
function matrix_cell(x, y)  
 if (matrix_cells[x] ~= nil) then  
 return matrix_cells[x][y]  
 else  
 return nil  
 end  
end  
  
  
--------------------------------------------------------------------------------  
-- Initialize PM  
--------------------------------------------------------------------------------  
  
function init_pm()  
  
 local rns = renoise.song()  
 local tracks = rns.tracks  
 local sequencer = rns.sequencer  
 local total_tracks = #tracks  
 local total_sequence = #sequencer.pattern_sequence  
  
 for x = 1, total_tracks do  
 if  
 tracks[x].type ~= renoise.Track.TRACK_TYPE_MASTER and  
 tracks[x].type ~= renoise.Track.TRACK_TYPE_SEND  
 then  
 for y = 1, total_sequence do  
 renoise.song().sequencer:set_track_sequence_slot_is_muted(x , y, true)  
 end  
 end  
 end  
  
end  
  
  
--------------------------------------------------------------------------------  
-- Initialize Grid Pie Pattern  
--------------------------------------------------------------------------------  
  
function init_gp_pattern()  
  
 local rns = renoise.song()  
 local tracks = rns.tracks  
 local total_tracks = #tracks  
 local sequencer = rns.sequencer  
 local total_sequence = #sequencer.pattern_sequence  
 local last_pattern = rns.sequencer:pattern(total_sequence)  
  
 if rns.patterns[last_pattern].name ~= "__GRID_PIE__" then  
 -- Create new pattern  
 local new_pattern = rns.sequencer:insert_new_pattern_at(total_sequence + 1)  
 rns.patterns[new_pattern].name = "__GRID_PIE__"  
 gridpie_idx = new_pattern  
 else  
 -- Clear pattern, unmute slot  
 rns.patterns[last_pattern]:clear()  
 rns.patterns[last_pattern].name = "__GRID_PIE__"  
 for x = 1, total_tracks do  
 renoise.song().sequencer:set_track_sequence_slot_is_muted(x , total_sequence, false)  
 end  
 gridpie_idx = last_pattern  
 end  
  
 -- Cleanup any other pattern named __GRID_PIE__  
 for x = 1, total_sequence - 1 do  
 local tmp = rns.sequencer:pattern(x)  
 if rns.patterns[tmp].name == "__GRID_PIE__" then  
 rns.patterns[tmp].name = ""  
 end  
 end  
  
 -- Move playhead to last pattern  
 rns.selected_sequence_index = #sequencer.pattern_sequence  
 rns.transport.follow_player = false  
 rns.transport.loop_pattern = true  
  
 -- Set Observables  
 if not (rns.transport.follow_player_observable:has_notifier(abort)) then  
 rns.transport.follow_player_observable:add_notifier(abort)  
 end  
 if not (rns.transport.loop_pattern_observable:has_notifier(abort)) then  
 rns.transport.loop_pattern_observable:add_notifier(abort)  
 end  
  
 -- Start playback  
 rns.transport:start(renoise.Transport.PLAYMODE_RESTART_PATTERN)  
end  
  
--------------------------------------------------------------------------------  
-- Is garbage PM position?  
--------------------------------------------------------------------------------  
  
function is_garbage_pos(x, y)  
  
 -- Garbage position?  
 local rns = renoise.song()  
 local sequencer = rns.sequencer  
 local total_sequence = #sequencer.pattern_sequence  
 local last_pattern = rns.sequencer:pattern(total_sequence)  
  
 if rns.sequencer.pattern_sequence[y] == nil then  
 return true  
 elseif rns.tracks[x] == nil then  
 return true  
 elseif last_pattern == y then  
 return true  
 else  
 return false  
 end  
  
end  
  
--------------------------------------------------------------------------------  
-- Adjust grid  
--------------------------------------------------------------------------------  
  
function adjust_grid()  
  
 -- TODO: How do I avoid?  
 -- 'notifier feedback loop detected. do not change values you are listening to within your notifiers.'  
  
 local rns = renoise.song()  
 local cell = nil  
  
 for x = x_pos, matrix_height + x_pos - 1 do  
 for y = y_pos, matrix_width + y_pos - 1 do  
 cell = matrix_cell(x - x_pos + 1, y - y_pos + 1)  
 if cell ~= nil and not is_garbage_pos(x, y) then  
 local val = rns.sequencer:track_sequence_slot_is_muted(x, y)  
 cell.value = (not val)  
 end  
 end  
 end  
  
  
end  
  
  
--------------------------------------------------------------------------------  
-- Toggler  
--------------------------------------------------------------------------------  
  
function toggler(x, y, val)  
  
 local rns = renoise.song()  
  
 x = x + (x_pos - 1)  
 y = y + (y_pos - 1)  
  
 -- Change PM  
 for i = 1, matrix_height do  
 if not is_garbage_pos(x, i) then  
 if i == y and val == true then  
 rns.sequencer:set_track_sequence_slot_is_muted(x , i, false)  
 else  
 rns.sequencer:set_track_sequence_slot_is_muted(x , i, true)  
 end  
 end  
 end  
 adjust_grid()  
  
 -- TODO: Copy to gridpie_idx!  
  
end  
  
  
  
--------------------------------------------------------------------------------  
-- Main  
--------------------------------------------------------------------------------  
  
function main()  
  
 if (not my_interface or not my_interface.visible) then  
 build_interface()  
 init_pm()  
 init_gp_pattern()  
 end  
 run()  
  
end  
  
  
--------------------------------------------------------------------------------  
-- Idler  
--------------------------------------------------------------------------------  
  
function idler()  
  
 if (not my_interface or not my_interface.visible) then  
 stop()  
 return  
 end  
  
end  
  
  
--------------------------------------------------------------------------------  
-- Bootsauce  
--------------------------------------------------------------------------------  
  
function run()  
 if not (renoise.tool().app_idle_observable:has_notifier(idler)) then  
 renoise.tool().app_idle_observable:add_notifier(idler)  
 end  
end  
  
  
function stop()  
 if (renoise.tool().app_idle_observable:has_notifier(idler)) then  
 renoise.tool().app_idle_observable:remove_notifier(idler)  
 end  
 if (renoise.song().transport.follow_player_observable:has_notifier(abort)) then  
 renoise.song().transport.follow_player_observable:remove_notifier(abort)  
 end  
 if (renoise.song().transport.loop_pattern_observable:has_notifier(abort)) then  
 renoise.song().transport.loop_pattern_observable:remove_notifier(abort)  
 end  
  
 vb = nil -- Destroy vb  
end  
  
  
--------------------------------------------------------------------------------  
-- Menu Registration  
--------------------------------------------------------------------------------  
  
renoise.tool():add_menu_entry {  
 name = "Main Menu:Tools:GridPie...",  
 invoke = main  
}  
  

The problem:

  
  
*** std::runtime_error: 'notifier feedback loop detected. do not change values you are listening to within your notifiers.'  
*** stack traceback:  
*** [C]: ?  
*** [C]: in function '__newindex'  
*** [string "do..."]:22: in function   
*** main.lua:259: in function 'adjust_grid'  
*** main.lua:289: in function 'toggler'  
*** main.lua:80: in function <79><br>
*** [C]: ?<br>
*** [C]: in function '__newindex'<br>
*** [string "do..."]:22: in function <br>
*** main.lua:259: in function 'adjust_grid'<br>
*** main.lua:289: in function 'toggler'<br>
*** main.lua:80: in function <79><br>
<br>```

<br>
<br>
Basically, it's a matrix of checkmarks that controls the PM mute states. What need to happen. <br>
<br>
* Unlimited checkmarks on the X (horizontal) axis.<br>
* Only 1 checkmark on the Y (vertical) axis.<br>
<br>
Problem is that when when I change the values on the Y axis, it goes into the above feedback loop.<br>
<br>
I don't know how to solve this problem. I've been at it for hours and am stumped.<br>
<br>
Help!</79></79>

Never mind. I fixed it using buttons and colours. It looks better, too.

Still, an interesting problem if anyone wants to look at at?

Is your grid-pie thingie similar to this generative matrix?

edit, nevermind :) read the thread: New Tool (3.0): Grid Pie

It’s not a generative matrix, no.

It’s a way to map a grid controller to the pattern matrix so you can remix a song. Here’s a very alpha version, not finished, but the idea is working for those bored enough to try a very rough version. Happy it’s working now. Going to bed. :)

[EDIT: Grid Pie available in another thread]

copying your script in the testpad.lua and executing it gives me the following notice in the terminal:

…am probably doing something wrong here, or?

There are a lot of things I’ve found don’t work in the testpad and you actually have to create them into a real Tool. Think most (if not all) renoise.tool things, such as adding keybindings or menu entries. Had me stumped for a while…