[Solved] Create a temporal buffer to copy a entire pattern-track

I need to create a temporary buffer to copy a pattern-track object to it (the selected pattern-track). I know it is necessary to put the information somewhere. How to do it?

The copied pattern-track will be in the buffer, and the original can be modified without changing the temporary buffer. In this way, you could have access to a specific line range to paste it into another new pattern-track.

What I am trying to recreate are the famous buttons to cut, copy and paste.It seems that the difficulty lies in creating “a new object”(buffer object type pattern-track) and then copying the object of the pattern-track inside it. How to do that?Of course, it would only have use within the tool…

Thanks!

-- Access to the pattern tracks. Each pattern has #renoise.song().tracks amount
-- of tracks.
renoise.song().patterns[].tracks[]
  -> [read-only, array of renoise.PatternTrack]

-- Copy contents from other pattern tracks, including automation when possible.
renoise.song().patterns[].tracks[]:copy_from(
  other renoise.PatternTrack object)

I would recommend creating a lua table and then simply do a tostring() of all the pattern lines.

That way, your copied data are completely detached from the pattern, and it’s faster than parsing each and every property

(joule would approve of this approach, I’m sure :wink: )

Note that the copying includes all columns, even the hidden ones.

When you need to write it back to the pattern you will need a way to parse the string. There’s some code in the xLinePattern class that might be helpful -

nevermind that the function I link to is reading from the pattern, writing would theoretically be the same process but in reverse:

https://github.com/renoise/xLib/blob/master/classes/xLinePattern.lua#L258

I would recommend creating a lua table and then simply do a tostring() of all the pattern lines.

That way, your copied data are completely detached from the pattern, and it’s faster than parsing each and every property

(joule would approve of this approach, I’m sure :wink: )

Note that the copying includes all columns, even the hidden ones.

When you need to write it back to the pattern you will need a way to parse the string. There’s some code in the xLinePattern class that might be helpful -

nevermind that the function I link to is reading from the pattern, writing would theoretically be the same process but in reverse:

https://github.com/renoise/xLib/blob/master/classes/xLinePattern.lua#L258

Thanks you!!!

Yes, I finally solved it using a global table :D.

The following code that I have built is quite interesting to me. I have been guided by your advice, from Danoise and Joule, and also from ffx. It serves to establish a range of lines inside the selected pattern track, which is obtained through a specific GUI, to save it in the table and access it at any time. In fact, the code is ready to add several clipboards. It would be very easy to add.

-------------------------------------------------------------------------------------------------
--temporaty clipboard (personal buffer)
PRE_TEMP_PATT_TRK = { 1, 1, {} } -- { p_range_1, p_range_2, lines_range_table }

--copy a block of lines to temporaty clipboard
function pre_copy_lines_to_temp_clipboard( lne_1, lne_2 )

  for lne = lne_1, lne_2 do
    local line = song.selected_pattern_track:line(lne)
    
    local t_line = { ["note_columns"] = {}, ["effect_columns"] = {} }
    for col = 1, 12 do
      t_line["note_columns"][col] = {
        ["delay_value"] = 0,
        ["effect_amount_value"] = 0,
        ["effect_number_value"] = 0,
        ["instrument_value"] = 255,
        ["note_value"] = 121,
        ["panning_value"] = 255,
        ["volume_value"] = 255,
        ["is_empty"] = true
      }
    end
    for eff = 1, 8 do
      t_line["effect_columns"][eff] = {
        ["number_value"] = 0,
        ["amount_value"] = 0,
        ["is_empty"] = true
      }
    end
    
    if not line.is_empty then
      ---
      for ncol_index, ncol in ipairs( line.note_columns ) do
        if ( not ncol.is_empty ) then
          local t_ncol = t_line.note_columns[ncol_index]
          local row_n = { "delay", "effect_amount", "effect_number", "instrument", "note", "panning", "volume" }
          t_ncol.is_empty = false
          for n, val in ipairs(row_n) do
            t_ncol[val.."_value"] = ncol[val.."_value"]
          end
        end
      end
      ---
      for ecol_index, ecol in ipairs( line.effect_columns ) do
        if ( not ecol.is_empty ) then
          local t_ecol = t_line.effect_columns[ecol_index]
          local row_e = { "number", "amount" }
          t_ecol.is_empty = false
          for e, val in ipairs(row_e) do
            t_ecol[val.."_value"] = ecol[val.."_value"]
          end
        end
      end
      ---
    end
    PRE_TEMP_PATT_TRK[3][lne] = t_line
  end
end

--copy the block of lines from them temporaty clipboard
function pre_copy_lines_from_temp_clipboard( lne_1, lne_2, range_1 )
  local num = 0
  for lne = lne_1, lne_2 do
    num = num + 1
    local line = song.selected_pattern_track:line( range_1+num )
    ---
    for ncol_index, ncol in ipairs(PRE_TEMP_PATT_TRK[3][lne].note_columns) do
      local line_ncol = line:note_column(ncol_index)
      if ncol.is_empty then
        if ( not line_ncol.is_empty ) then
          line_ncol:clear()
        end
      else
        local row_n = { "delay", "effect_amount", "effect_number", "instrument", "note", "panning", "volume" }
        for n, val in ipairs(row_n) do
          line_ncol[val.."_value"] = ncol[val.."_value"]
        end
      end
    end
    ---
    for ecol_index, ecol in ipairs( PRE_TEMP_PATT_TRK[3][lne].effect_columns ) do
      local line_ecol = line:effect_column(ecol_index)
      if ecol.is_empty then
        if ( not line_ecol.is_empty ) then
          line_ecol:clear()
        end
      else
        local row_e = { "number", "amount" }
        for e, val in ipairs(row_e) do
          line_ecol[val.."_value"] = ecol[val.."_value"]
        end
      end
    end
    ---
  end
end

--cut or copy entire block
function pre_cut_or_copy_block( bol )
  if ( PRE_ON_OFF == true ) then
    local sli = song.selected_line_index
    local nol = song.selected_pattern.number_of_lines
    local lpb = vws.PRE_SLOTS_LPB.value
  
    local range = {}  
    local range_1, range_2 = {}, {}
    
    local function check_range_1()
      local blocks = nol/lpb
      for div = 0, blocks-1 do
        range_1[div+1] = div*lpb
        range_2[div+1] = div*lpb+lpb-1
        if ( sli-1 >= range_1[div+1] ) and ( sli-1 <= range_2[div+1] ) then
          range[1] = range_1[div+1]
          range[2] = range_2[div+1]
          return
        end
      end
    end
    check_range_1()
    PRE_TEMP_PATT_TRK[1] = range[1]+1
    PRE_TEMP_PATT_TRK[2] = range[2]+1
    --print("PRE_TEMP_PATT_TRK") rprint(PRE_TEMP_PATT_TRK)

    pre_copy_lines_to_temp_clipboard(PRE_TEMP_PATT_TRK[1], PRE_TEMP_PATT_TRK[2])

    --for cut
    if ( bol == true ) then
      if ( range[2] ~= nil ) then
        for range = range[1], range[2] do
          song.selected_pattern_track:line(range+1):clear()
        end
      else
        --print("no range!")
      end
      vws.PRE_BT_CUT.active = false
      vws.PRE_BT_COPY.active = false
    end
  end
end

--paste entire block
function pre_paste_block()
  if ( PRE_ON_OFF == true ) then
    local sli = song.selected_line_index
    local nol = song.selected_pattern.number_of_lines
    local lpb = vws.PRE_SLOTS_LPB.value
  
    local range = {}  
    local range_1, range_2 = {}, {}
    
    local function check_range_1()
      local blocks = nol/lpb
      for div = 0, blocks-1 do
        range_1[div+1] = div*lpb
        range_2[div+1] = div*lpb+lpb-1
        if ( sli-1 >= range_1[div+1] ) and ( sli-1 <= range_2[div+1] ) then
          range[1] = range_1[div+1]
          range[2] = range_2[div+1]
          return
        end
      end
    end
    check_range_1()
    if ( range[2] ~= nil ) and ( song.selected_note_column ) then
      pre_copy_lines_from_temp_clipboard(PRE_TEMP_PATT_TRK[1], PRE_TEMP_PATT_TRK[2], range[1])
    end
    if ( vws.PRE_BT_CUT.active == false ) then vws.PRE_BT_CUT.active = true end
    if ( vws.PRE_BT_COPY.active == false ) then vws.PRE_BT_COPY.active = true end
  end
end
---
function pre_cut_copy_paste()
  local buttons = vb:row { spacing = -1, margin = 1, style = "plain",
    vb:button {
      id = "PRE_BT_CUT",
      active = false,
      height = 19,
      width = 23,
      bitmap = "ico/cut_ico.png",
      notifier = function() pre_cut_or_copy_block( true ) end,
      tooltip = "Cut the entire selected block.\n[CTRL + X]"
    },
    vb:button {
      id = "PRE_BT_COPY",
      active = false,
      height = 19,
      width = 23,
      bitmap = "ico/copy_ico.png",
      notifier = function() pre_cut_or_copy_block( false ) end,
      tooltip = "Copy the entire selected block.\n[CTRL + C]"
    },
    vb:button {
      id = "PRE_BT_PASTE",
      active = false,
      height = 19,
      width = 23,
      bitmap = "ico/paste_ico.png",
      notifier = function() pre_paste_block() end,
      tooltip = "Paste the entire copied block.\n[CTRL + V]"
    }
  }
  return buttons
end

Some time ago I made a similar query. But I did not understand very well how to build a custom buffer. Now that I have more knowledge, I have finally been able to build it. The performance is instantaneous, since it is prepared to use a range between 2 and 32 lines, by blocks.

Looking at this whole matter, it would be very interesting if the API could offer the ability to match a typical Renoise object to a global one, such as a track, a track pattern or a line. Something like:

MY_GLOBAL_CLIPBOARD = renoise.type.PatternTrackObject (or something similar)

renoise.PatternTrackObject should have all the items for PatternTrack set and it should be an empty object, with no data. In this way, it could be copied with :copy_from()…

MY_GLOBAL_CLIPBOARD:copy_from(song.selected_pattern_track) something like this?

I guess what you’re suggesting is:

local my_track = renoise.PatternTrack()

You can actually run this code, but the object returned cannot be ‘initialized’ as an independent object with all properties working. The way these classes are designed and working with luabind, is that their objects are tied to actual song data in memory. There would probably (?) be some hassle for the Dev with breaking up and redesigning the luabind stuff involved for achieving what you’re suggesting. Maybe it would even affect the access speed, I’m not sure.

And it’s quite easy to make your own “copy_from” method that will accept whatever class structure you design. For syntactic fun, it’s even possible to place it as a method inside the native patterntrack class, and make the syntax ‘almost transparent’.

function renoise.PatternTrack:custom_copy_from(obj)
  if type(obj) == "PatternTrack" then
    self:copy_from(obj)
  elseif type(obj) == "myPatternTrackClass" then
    -- assumes you have designed your custom class for storing patterntrack data.
    
    -- do all the value copying here..
  else
    error("custom_copy_from method requires a native PatternTrack object or myPatternTrackClass object") 
  end
end