API fun finding 'hidden' code (pattern iterator)


(joule) #1

For inspiration and reference, here’s a hack that seems to extract some (most?) of the pattern iterator, which is lua code. I’ve also found some other lua code deep inside the API, but it wasn’t as interesting as this.

Click to view contents
tbl = { }
tbl_mt = {
  __index = function()
    rprint(debug.getinfo(2))
  end
}
setmetatable(tbl, tbl_mt)
for k, v in renoise.song().pattern_iterator.lines_in_song(tbl) do
end

Result:

Click to view contents
------------------------------------------------------------------------------
-- make_note_column_iter
local function make_note_column_iter(song, line_iter, visible_columns_only)
  assert(type(line_iter) == 'function')
  visible_columns_only = visible_columns_only or false
  local pos, line = line_iter()
  if (pos == nil or line == nil) then
    return nil
  end
  local columns = line.note_columns
  local column_count = #columns
  if visible_columns_only then
    column_count = song.tracks[
      pos.track].visible_note_columns
  end

  -- we start by increasing the column
  pos.column = 0
  return function()
    pos.column = pos.column + 1
    if (pos.column <= column_count) then
      return pos, columns[pos.column]
    end
    -- loop until we found a line with visible columns
    while true do
      pos, line = line_iter()
      if (pos == nil or line == nil) then
        return nil
      end
      columns = line.note_columns
      column_count = #columns
      if visible_columns_only then
        column_count = song.tracks[
          pos.track].visible_note_columns
      end
      if (column_count > 0) then
        pos.column = 1
        return pos, columns[pos.column]
      end
    end
    return nil
  end
end

-- make_effect_column_iter
local function make_effect_column_iter(song, line_iter, visible_columns_only)
  assert(type(line_iter) == 'function')
  visible_columns_only = visible_columns_only or false
  
  local pos, line = line_iter()
  if (pos == nil or line == nil) then
    return nil
  end
  local columns = line.effect_columns
  local column_count = #columns
  if visible_columns_only then
    column_count = song.tracks[
      pos.track].visible_effect_columns
  end
  -- we start by increasing the column
  pos.column = 0
  return function()
    pos.column = pos.column + 1
    if (pos.column <= column_count) then
      return pos, columns[pos.column]
    end
    -- loop until we found a line with visible columns
    while true do
      pos, line = line_iter()
      if (pos == nil or line == nil) then
        return nil
      end
      columns = line.effect_columns
      column_count = #columns
      if visible_columns_only then
        column_count = song.tracks[
          pos.track].visible_effect_columns
      end
      if (column_count > 0) then
        pos.column = 1
        return pos, columns[pos.column]
      end
    end
    return nil
  end
end

------------------------------------------------------------------------------
-- renoise.PatternIterator:lines_in_song
------------------------------------------------------------------------------
function renoise.PatternIterator:lines_in_song(visible_patterns_only)
  visible_patterns_only = visible_patterns_only or true
  local pattern_order = {}
  if visible_patterns_only then
    local pattern_sequence = self.song.sequencer.pattern_sequence
    local referenced_patterns = {}
    for seq_index, pattern_index in pairs(pattern_sequence) do 
      if not referenced_patterns[pattern_index] then
        referenced_patterns[pattern_index] = true
        pattern_order[#pattern_order + 1] = pattern_index
      end
    end
  else
   for pattern_index = 1,#self.song.patterns do
     pattern_order[#pattern_order + 1] = pattern_index
   end
  end
  local pattern_order_index = 1
  local start_pos = { pattern = pattern_order[1], track = 1, line = 1 }
  local pos = { pattern = pattern_order[1], track = 1, line = 1 }
  local patterns = self.song.patterns
  local pattern = patterns[pos.pattern]
  local pattern_tracks = pattern.tracks
  local pattern_track = pattern_tracks[pos.track]
  -- we start by increasing the line
  start_pos.line = start_pos.line - 1
  pos.line = pos.line - 1
  local function line_iter()
    pos.line = pos.line + 1
    if pos.line > pattern.number_of_lines then
      pos.line = 1; pos.track = pos.track + 1
      if pos.track > #pattern_tracks then
        pos.track = 1; pattern_order_index = pattern_order_index + 1 
        if pattern_order_index > #pattern_order then
          -- completed: reset and stop
          pattern_order_index = 1
          pos.pattern = start_pos.pattern
          pos.track = start_pos.track
          pos.line = start_pos.line
          pattern = patterns[pos.pattern]
          pattern_tracks = pattern.tracks
          pattern_track = pattern_tracks[pos.track]
          return nil
        else
          -- new pattern
          pos.pattern = pattern_order[pattern_order_index]
         pattern = patterns[pos.pattern]
          pattern_tracks = pattern.tracks
          pattern_track = pattern_tracks[pos.track]
        end
      else
        -- new track
        pattern_track = pattern_tracks[pos.track]
      end
    else
      -- new line
    end
    return pos, pattern_track:line(pos.line)
  end
  return line_iter, self
end

-- note_columns_in_song
function renoise.PatternIterator:note_columns_in_song(visible_only)
  return make_note_column_iter(self.song, self:lines_in_song(
    visible_only), visible_only)
end

-- effect_columns_in_song
function renoise.PatternIterator:effect_columns_in_song(visible_only)
  return make_effect_column_iter(self.song, self:lines_in_song(
    visible_only), visible_only)
end

------------------------------------------------------------------------------
-- renoise.PatternIterator:lines_in_pattern
------------------------------------------------------------------------------
function renoise.PatternIterator:lines_in_pattern(pattern_index)
  assert(type(pattern_index) == 'number', ('pattern_index parameter: ' ..
    'expected an index (a number), got a \'%s\' object'):format(type(pattern_index)))
  local start_pos = { pattern = pattern_index, track = 1, line = 1 }
  local pos = { pattern = pattern_index, track = 1, line = 1 }
  local pattern = self.song.patterns[pos.pattern]
  local pattern_tracks = pattern.tracks
  local pattern_track = pattern_tracks[pos.track]
  -- we start by increasing the line
  start_pos.line = start_pos.line - 1
  pos.line = pos.line - 1
  local function line_iter()
    pos.line = pos.line + 1
    if pos.line > pattern.number_of_lines then
      pos.line = 1; pos.track = pos.track + 1
      if pos.track > #pattern_tracks then
        -- completed: reset and stop
        pos.track = start_pos.track
        pos.line = start_pos.line
        
        pattern_track = pattern_tracks[pos.track]
        return nil
      else
        -- new track
        pattern_track = pattern_tracks[pos.track]
      end
    else
      -- new line
    end
    return pos, pattern_track:line(pos.line)
  end
  return line_iter, self
end

-- note_columns_in_pattern
function renoise.PatternIterator:note_columns_in_pattern(pattern_index, visible_only)
  return make_note_column_iter(self.song, self:lines_in_pattern(
    pattern_index), visible_only)
end

-- effect_columns_in_pattern
function renoise.PatternIterator:effect_columns_in_pattern(pattern_index, visible_only)
  return make_effect_column_iter(self.song, self:lines_in_pattern(
    pattern_index), visible_only)
end

------------------------------------------------------------------------------
-- renoise.PatternIterator:lines_in_track
------------------------------------------------------------------------------
function renoise.PatternIterator:lines_in_track(track_index, visible_patterns_only)
  assert(type(track_index) == 'number', ('track_index parameter: ' ..
    'expected an index (a number), got a \'%s\' object'):format(type(track_index)))
  visible_patterns_only = visible_patterns_only or true
  
  local pattern_order = {}
  if visible_patterns_only then
    local pattern_sequence = self.song.sequencer.pattern_sequence
    local referenced_patterns = {}
    for seq_index, pattern_index in pairs(pattern_sequence) do 
      if not referenced_patterns[pattern_index] then
        referenced_patterns[pattern_index] = true
        pattern_order[#pattern_order + 1] = pattern_index
      end
    end
  else
   for pattern_index = 1,#self.song.patterns do
     pattern_order[#pattern_order + 1] = pattern_index
   end
  end
  local pattern_order_index = 1
  local start_pos = { pattern = pattern_order[1], track = track_index, line = 1 }
  local pos = { pattern = pattern_order[1], track = track_index, line = 1 }
  local patterns = self.song.patterns
  local pattern = patterns[pos.pattern]
  local pattern_tracks = pattern.tracks
  local pattern_track = pattern_tracks[pos.track]
  -- we start by increasing the line
  start_pos.line = start_pos.line - 1
  pos.line = pos.line - 1
  local function line_iter()
    pos.line = pos.line + 1
    if pos.line > pattern.number_of_lines then
      pos.line = 1; pattern_order_index = pattern_order_index + 1 
      if pattern_order_index > #pattern_order then
        -- completed: reset and stop
        pattern_order_index = 1
        pos.pattern = start_pos.pattern
        pos.line = start_pos.line
        pattern = patterns[pos.pattern]
        pattern_tracks = pattern.tracks
        pattern_track = pattern_tracks[pos.track]
        return nil
      else
        -- new pattern
        pos.pattern = pattern_order[pattern_order_index]
        pattern = patterns[pos.pattern]
        pattern_tracks = pattern.tracks
        pattern_track = pattern_tracks[pos.track]
      end
    else
      -- new line
    end
    return pos, pattern_track:line(pos.line)
  end
  return line_iter, self
end

-- note_columns_in_track
function renoise.PatternIterator:note_columns_in_track(track_index, visible_only)
  return make_note_column_iter(self.song, self:lines_in_track(
    track_index, visible_only), visible_only)
end

-- effect_columns_in_track
function renoise.PatternIterator:effect_columns_in_track(track_index, visible_only)
  return make_effect_column_iter(self.song, self:lines_in_track(
    track_index, visible_only), visible_only)
end

------------------------------------------------------------------------------
-- renoise.PatternIterator:lines_in_pattern_track
------------------------------------------------------------------------------
function renoise.PatternIterator:lines_in_pattern_track(pattern_index, track_index)
  assert(type(pattern_index) == 'number', ('pattern_index parameter: ' ..
    'expected an index (a number), got a \'%s\' object'):format(type(pattern_index)))
  assert(type(track_index) == 'number', ('track_index parameter: ' ..
    'expected an index (a number), got a \'%s\' object'):format(type(track_index)))
  local start_pos = { pattern = pattern_index, track = track_index, line = 1 }
  local pos = { pattern = pattern_index, track = track_index, line = 1 }
  local pattern = self.song.patterns[pos.pattern]
  local pattern_tracks = pattern.tracks
  local pattern_track = pattern_tracks[pos.track]
  -- we start by increasing the line
  start_pos.line = start_pos.line - 1
  pos.line = pos.line - 1
  local function line_iter()
    pos.line = pos.line + 1
    if pos.line > pattern.number_of_lines then
      -- completed: reset and stop
      pos.line = start_pos.line
      return nil
    else
      -- new line
    end
    return pos, pattern_track:line(pos.line)
  end
  return line_iter, self
end

-- note_columns_in_pattern_track
function renoise.PatternIterator:note_columns_in_pattern_track(
    pattern_index, track_index, visible_only)
  return make_note_column_iter(self.song, self:lines_in_pattern_track(
    pattern_index, track_index), visible_only)
end

-- effect_columns_in_pattern_track
function renoise.PatternIterator:effect_columns_in_pattern_track(
 pattern_index, track_index, visible_only)
  return make_effect_column_iter(self.song, self:lines_in_pattern_track(
    pattern_index, track_index), visible_only)
end

(Ledger) #2

I remembered taktik posted a link to these before, but link seems forward to a GitHub root directory now? and I couldn`t find TestPatternIterators.lua there, to compare with your printout.

Anyway some good hacking! I may be back with some questions!

Original thread and quote:
https://forum.renoise.com/t/column-by-column-iterator/31113

Renoises pattern iterators are written in Lua. See https://code.google.com/p/xrnx/source/browse/trunk/UnitTests/TestPatternIterators.lua for the source

Should be relatively easy to copy and/or adopt to fit your needs?

EDIT:
Another post where he posted them up, though the formatting has been lost in the spoiler box:

https://forum.renoise.com/t/phrase-iterator-object/41211


(joule) #3

EDIT:
Another post where he posted them up, though the formatting has been lost in the spoiler box:

https://forum.renoise.com/t/phrase-iterator-object/41211

Oh, thanks! I had no idea. It seems to be the same!