Which pattern iterator has which order of iteration?

Hi,

currently I am using the Song.pattern_iterator:note_columns_in_pattern(patternIndex) iterator to convert all note data to a linear format. Now it turns out that iterating over all track’s note columns first and then the patterns brings a lot of disadvantages in my logic…

Ideally I need an iterator which iterates 1. over columns of a single track, 2. over the patterns, 3. then over next tracks…. So it ensures that a whole song track is iterated first, until it progresses with a next track. This because usually all pattern track’s data only is in context of this track, not referring to any other track.

So I was looking at the note_columns_in_song iterator…. Do you know in which order this one iterates?

Maybe this info would be a good addition for the docs…. :slight_smile: Imma try create a merge request later…. Well I am very confused by the pattern iterator’s behaviour all the time.

The pattern iterators are implemented in plain Lua. Here’s the impl. Shouldn’t be too hard to tweak or write your own ones based on this:

------------------------------------------------------------------------------
-- 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
1 Like