Help: copy in clipboard the selection in PE & paste separately?

Hi

I have understood that it is possible to copy and paste with the same function a specific selection in the Pattern Editor (PE).Is it possible to do it separately in two functions?

  1. Function 1 (button 1): select a area in pattern editor and only copy in clipboard. End.
  2. Function 2 (button 2): in the selected new position by the user, paste what copied in clipboard.

It implies having control of the clipboard. Does the API allow this?I know it can be done in one function.But with a function you can not use 2 separate buttons, one to copy and one to paste.

Any help here? Thanks!

I wish you a happy new year!!! :slight_smile:

Well… It’s like this (Renoise 3.1):

  • The API doesn’t have access to the clipboard.

  • You could, of course, make your own custom “clipboard” in your tool that would only be used from within your tool.

  • The API does not currently provide access to granular selections, so it would be kind of half-assed anyway.

I’ve read things in the forums from 2010 of users asking for something similar.I wondered if the API had made progress here.

Right now I have built a function that allows you to select a specific area within the pattern editor:

function amic_CH05_1A( song, spi, sti, sli, snci, sii ) --Select a block in track. And copy??
  song = renoise.song()
  spi, sti, sli, snci, sii = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index, song.selected_instrument_index
  song.selection_in_pattern = { start_line = sli, start_track = sti, end_line = sli + 16, end_track = sti } --select 16 lines down
end 

function amic_CH05_2A( song, spi, sti, sli, snci, sii ) --Select a block in track. And copy??
song = renoise.song()
spi, sti, sli, snci, sii = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index, song.selected_instrument_index
song.selection_in_pattern = { start_line = sli, start_track = sti, end_line = sli + 32, end_track = sti } --select 32 lines down
end

But what good is that?The data to copy would only be extracted from the pattern editor section.

You could, of course, make your own custom “clipboard” in your tool that would only be used from within your tool.

Is this complicated to do? Any examples?I want to build a small tool with 3 elements:

  1. popup to select a area inside the pattern editor (always a single column with variable lines).
  2. button1 to copy the selection.
  3. button2 to paste in selected position of cursor (the user has the control).

The user will use these 3 steps.

Depending on the exact behavior that you’re after, you could perhaps skip any clipboard behavior and just rely on using a loop with note_column:copy_from() ? That’s quite simple.

Otherwise you will have to make a scheme for storing/restoring pattern data to/from a table.

With note_column:copy_from() the user does not have the control for select the position of destiny.I mean, it depends on the code of the function, where it will paste the data.That is, it is not possible to use a copy button and a paste button, separately.

I guess it is necessary to create a table to store the selected data.The data to be copied using button1 “copy” would be those selected by the popup ( using song.selection_in_pattern = {,} ), and paste the data with button2 “paste” in a equal area, from the new cursor position established by the user.The same function of paste would prevent the paste if the number of lines of the destiny is smaller (or paste only the available lines in the destiny, there would be a condition for deploying subcolumns if necessary).

The most basic example would be this:

  1. The user places the cursor on: pattern 01, track 01, line 16 (no matter what subcolumn position, is the line 16)
  2. With popup “select”, select line 16 and line 17 in pattern 01, track 01 (two complete lines inside this track, with note, instr, vol, pan, dly, sample fx and 8x fx master).I suppose that the selection serves for a visual markup, so the user understands what happens. This popup use various song.selection_in_pattern = {,} , from 1, “2”, 4, 8, 16, 32, 64, 128 lines of selection.
  3. Button1 “copy”. A table to copy lines 16 and 17 of pattern 01, track 01 (a clipboard inside the tool).Depends on the selection using the popup “select” ( song.selection_in_pattern = {,} ), in this case, the value “2”.This table only works for a single track at all times (can not copy data from multiple tracks, so as not to complicate).
  4. The user places the cursor on: pattern 01 track 02, line 01 (a concrete position).
  5. Buttón 2 “paste”. Function which retrieves the data of the table and paste from pattern 01, track 02, line 01.

Schematic of elements:

  • [POPUP “select”][BUTTON1 “copy”][BUTTON2 “paste”]
  • the popup values: 1, 2, 4, 8, 16, 32, 64 (for example, value 16, select 16 lines).

Really, the scheme I have in mind is broader:

  • [POPUP “select”] [x2] [x4] [x8] [x16] [x32] [x64] [x128] [BUTTON1 “copy”] [BUTTON2 “paste”]
  • the popup values: range 1-128 (128 values)
  • x2 to x128 are seven “multiplier” buttons, that simply change the value of the popup.
  • This could work with the mouse, with midi input and withkeyhandler.

My problem is to create the table which depends on the popup selection value and that works with the two buttons, the button1 “copy”, and the separate button2 “paste”.

Do you see all this possible? What would this table look like with this three elements, popup, button copy and button paste?

I believe it’s simplest to use note_column:copy_from().

Here is the simple principle (edge cases not included) showing how to copy a selection to the selected line position.

function copy_block_to_cursor()

  local selection = rns.selection_in_pattern

  local selection_data = {
    start_line = selection.start_line,
    end_line = selection.end_line,
    track_index = selection.start_track,
    ncol_index = selection.start_column,
    range = selection.end_line - selection.start_line
  }

  local patterntrack = rns.selected_pattern:track(selection_data.track_index)

  -- destination index minus source index
  local offset = rns.selected_line_index - selection_data.start_line

  for pos = selection_data.start_line, selection_data.end_line do
    local src_ncol = patterntrack:line(pos):note_column(selection_data.ncol_index)
    local dest_ncol = patterntrack:line(pos + offset):note_column(selection_data.ncol_index)
    dest_ncol:copy_from(src_ncol)
  end
  
end

I believe it’s simplest to use note_column:copy_from().

Here is the simple principle (edge cases not included) showing how to copy a selection to the selected line position.

function copy_block_to_cursor()

local selection = rns.selection_in_pattern

local selection_data = {
start_line = selection.start_line,
end_line = selection.end_line,
track_index = selection.start_track,
ncol_index = selection.start_column,
range = selection.end_line - selection.start_line
}

local patterntrack = rns.selected_pattern:track(selection_data.track_index)

-- destination index minus source index
local offset = rns.selected_line_index - selection_data.start_line

for pos = selection_data.start_line, selection_data.end_line do
local src_ncol = patterntrack:line(pos):note_column(selection_data.ncol_index)
local dest_ncol = patterntrack:line(pos + offset):note_column(selection_data.ncol_index)
dest_ncol:copy_from(src_ncol)
end
  
end

Hi joue. Thanks!

I tried the following with two buttons:

  1. button for selection
function amic_CH05_1A( song, spi, sti, sli, snci, sii ) --Select a block in track
  song = renoise.song()
  spi, sti, sli, snci, sii = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index, song.selected_instrument_index
  song.selection_in_pattern = { start_line = sli, start_track = sti, end_line = sli + 16, end_track = sti } 
end
  1. button for copy&paste, the previous selection in the new selected position of cursor (your function)
function amic_CH05_2A() --function copy_block_to_cursor()
  local song = renoise.song()
  local selection = song.selection_in_pattern -- does this detect the previous button selection?
  local selection_data = {
    start_line = selection.start_line,
    end_line = selection.end_line,
    track_index = selection.start_track,
    ncol_index = selection.start_column,
    range = selection.end_line - selection.start_line
  }
  local patterntrack = song.selected_pattern:track(selection_data.track_index)
  -- destination index minus source index
  local offset = song.selected_line_index - selection_data.start_line
  for pos = selection_data.start_line, selection_data.end_line do
    local src_ncol = patterntrack:line(pos):note_column(selection_data.ncol_index)
    local dest_ncol = patterntrack:line(pos + offset):note_column(selection_data.ncol_index)
    dest_ncol:copy_from(src_ncol)
  end 
end

I do not know if I’m doing the right steps:

  1. Press button of selection ( function amic_CH05_1A() ).The function selects 16 lines of the selected track.
  2. select a new position of cursor (without deselecting the previous selection). I select the track 01, line 01, same pattern.
  3. Press button of copy/paste ( function amic_CH05_2A(), your function ).

But it does nothing.Have I understood the operation correctly?Where is the error?Is the initial selection?

I have tried other simple functions with " :copy_from() ", as the following simple function (and others):

function amic_CH05_6B( song, a, b ) --copy first line in consecutive track (same position)
  song = renoise.song()
  a = song.selected_pattern:track(1):line(1) --first track
  b = song.selected_pattern:track(2):line(1) --consecutive track
  b:copy_from(a)
end

This is another much simpler history (it helps me understand how works the :copy_front() ). I understand that " :copy_from() " does not allow to separate in two buttons, first for copy and last for paste.The function solves everything at once.Perhaps the initial problem is the detection of the initial selection (which will be variable according to the user’s selection).

Not sure what you’re doing wrong.

  1. The function I posted as the simplest example, will only work when copying within the same track. You will have to make the proper modification to make dest_ncol be in the correct track index (rns.selected_track_index).

  2. However, if you plan to copy across pattern boundaries, this approach will not suffice anyway. In that case you need to make your own custom scheme with its own “clipboard”.

Not sure what you’re doing wrong.

  1. The function I posted as the simplest example, will only work when copying within the same track. You will have to make the proper modification to make dest_ncol be in the correct track index (rns.selected_track_index).

  2. However, if you plan to copy across pattern boundaries, this approach will not suffice anyway. In that case you need to make your own custom scheme with its own “clipboard”.

He tried several things, but I do not get it to work:

local function copy_block_to_cursor()
  local song = renoise.song()
  local selection = song.selection_in_pattern
  local selection_data = { --the table of selection in pattern editor
    start_line = 1, --selection.start_line,
    end_line = 16, --selection.end_line,
    track_index = 1, --selection.start_track,
    ncol_index = 1, --selection.start_column,
    range = selection.end_line - selection.start_line
  }
  local patterntrack = song.selected_pattern:track(selection_data.track_index)
  -- destination index minus source index
  local offset = song.selected_line_index - selection_data.start_line
  for pos = selection_data.start_line, selection_data.end_line do
    local src_ncol = patterntrack:line(pos):note_column(selection_data.ncol_index)
    --local dest_ncol = patterntrack:line(pos + offset):note_column(selection_data.ncol_index)
    --local spi, sti, sli, snci = song.selected_pattern_index, song.selected_track_index, song.selected_line_index, song.selected_note_column_index
    --local dest_ncol = song.selected_pattern:track(sti):line(sli) --local dest_ncol = song.patterns[spi].tracks[sti].lines[sli].note_columns[snci]
    local patterntrack_dest = song.selected_pattern:track(song.selected_track_index) -- this is correct???
    dest_ncol = patterntrack_dest:line(pos + offset):note_column(selection_data.ncol_index) -- this is correct???
    --local dest_ncol = song.selected_track_index
    dest_ncol:copy_from(src_ncol)
  end 
end
local patterntrack_dest = song.selected_pattern:track(song.selected_track_index)

Here I have changed the destiny.But it still does not copy anything.Top I changed the values of “selection_data” by numbers to try it too.I put the cursor on another track, but it does not copy.

What you pasted works perfectly fine here, as it should. (Except for the minor issue on line 20 where you’ve removed “local”).

It copies the content of track 1:line 1-16 to where the cursor is currently at.

PS. just a side note: renoise.song().selected_pattern_track can be used as shortcut to renoise.song():pattern(renoise.song().selected_pattern_index):track(renoise.song().selected_track_index)

What you pasted works perfectly fine here, as it should. (Except for the minor issue on line 20 where you’ve removed “local”).

Wow, this has been a slip when copying. I had another problem, in my function that I have already solved.

Ok, work perfect!:

function amic_CH05_2A() --function copy_block_to_cursor()
  local song = renoise.song()
  local selection = song.selection_in_pattern
  local selection_data = { --the table of selection in pattern editor
    start_line = selection.start_line,
    end_line = selection.end_line,
    track_index = selection.start_track,
    ncol_index = selection.start_column,
    --range = selection.end_line - selection.start_line
  }
  local patterntrack = song.selected_pattern:track(selection_data.track_index) --origin patterntrack
  local patterntrack_dest = song.selected_pattern_track --destiny patterntrack_dest
  local offset = song.selected_line_index - selection_data.start_line -- destination index minus source index
  for pos = selection_data.start_line, selection_data.end_line do
    local src_ncol = patterntrack:line(pos):note_column(selection_data.ncol_index)
    local dest_ncol = patterntrack_dest:line(pos + offset):note_column(song.selected_note_column_index)
    dest_ncol:copy_from(src_ncol)
  end 
end

I suppose the line of “range=” is to include another kind of order within the function.It does not seem necessary here, I believe.

PS. just a side note: renoise.song().selected_pattern_track can be used as shortcut to renoise.song():pattern(renoise.song().selected_pattern_index):track(renoise.song().selected_track_index)

Ok thanks!, I have changed the corresponding line by this:

local patterntrack_dest = song.selected_pattern_track

I will try to look at this kind of shortcuts. I guess he will not be the only one.

Joule, this function is wonderful. With a popup for selection and a button for copy/paste (only a buton with this function) is possible control the editing using MIDI input, accompanied by navigation buttons.There is only two small drawbacks:

  1. The user must remember the selection, if the position of destiny is on a track far away, which cause the selection to exit the screen.
  2. The usercan not change of pattern of selection, must remain on the same pattern. If the user change of pattern, the selection change of pattern also.What a pity!

For these two reasons, I needed 2 buttons, one of copy, and another of paste to avoid these two points.But it seems that the API does not allow it.

Thank you very much for the help! With this, it is possible to work in the same pattern, at least.

Note:A little trick to use with the mouse alone:

  1. Go directly to the destiny position (left click).
  2. Select the area to copy inside a note_column.
  3. Press button to copy (the button of this function).

This is very fast and comfortable :slight_smile:

A small advance:

7187 amic-paste-selection.gif

Only the mouse is used here. But this can be controlled from a MIDI keyboard or MIDI pad.

For these two reasons, I needed 2 buttons, one of copy, and another of paste to avoid these two points.But it seems that the API does not allow it.

The API does not prevent you in any way to make your own “clipboard” system (and two separate copy / paste buttons).

The limitation is that this would not work in sync with any native clipboard operations, meaning the user could not use the native copy shortcut and then your custom paste button, for example.

The limitation is that this would not work in sync with any native clipboard operations, meaning the user could not use the native copy shortcut and then your custom paste button, for example.

Then this would be no problem.As it stands now, it would only be necessary to separate the function in 2, for copy, and for paste.But how?Is it possible to take advantage this last function?

The idea is always the same: 1) select (no problem, with popup), 2) copy (button1), 3 paste (button2).

Nope, this function is pretty useless in that case.

Make sure you know the basics of 1) lua tables and how they work 2) how to iterate pattern lines. This knowledge is required to make the function saving all relevant pattern data from the selection into a table for later pasting.

Nope, this function is pretty useless in that case.

Make sure you know the basics of 1) lua tables and how they work 2) how to iterate pattern lines. This knowledge is required to make the function saving all relevant pattern data from the selection into a table for later pasting.

I’ll take a deep eye, because a lot of things are mixed here.All this is thought for be able to handle everything only through a MIDI keyboard, without using another peripheral.The selection would be somewhat limited.For this the most agile is to use the keyboard and mouse.But I’m curious to what extent it is possible to control Renoise from buttons of a MIDI pad or a MIDI keyboard.

When I have time,I’m going to try a simple case, to copy just one note with a button using a table, and paste with another button, to see if I can. If it works, the rest is adding more things.

Documentation “table” :https://github.com/renoise/xrnx/blob/master/Documentation/Lua.Standard.API.lua#L123

table.rcopy is not usable here as it does not work on renoise objects.

PS. Here is a video tutorial I would probably recommend for learning lua, btw:

table.rcopy is not usable here as it does not work on renoise objects.

PS. Here is a video tutorial I would probably recommend for learning lua, btw:

www.youtube.com/watch?v=Us46grT9wsA&list=PL0o3fqwR2CsWg_ockSMN6FActmMOJ70t_

Thanks for this video (videos)!I’ll have to watch it subtitled.My native language is Spanish, and I am in continuous learning with English.

I have noted table.rcopy to study it later. Sometimes I add things like thatin the threads instead of writing it down on paper :slight_smile:

Video tutorials using the Renoise API would be super.But it’s always the same. It costs a lot of time, and there are not many people interested in the Renoise API.

EDIT :Perhaps it is possible to pin up a topic with video tutorials about LUA, and other about “LUA GUIDES” and “API GUIDES” at the written level with lots of function examples…

You can also check out xNoteColumn. At least for inspiration - because it sounds like you’re headed more or less in that direction, coming up with some kind of clipboard/virtual pattern model:

https://github.com/renoise/xrnx/blob/master/Tools/com.renoise.xLib.xrnx/source/xLib/classes/xNoteColumn.lua

From the description:

Unlike the renoise.NoteColumn, this one can be freely defined, without 
the need to have the line present somewhere in an actual song

That is exactly your problem right now - you need to store the data somewhere, but note-columns in Renoise need to exist somewhere in the song.

The xNoteColumn is a virtual representation of the note-column, and of course, is accompanied by a matching xEffectColumn. Add 12 notecolomns and 8 effectcolumns together, and you have a representation of a single line. Add a number of lines, and you got yourself a pattern-track snippet. And so on…

As for Renoise API tutorials, having a good understanding of the lua table is paramount - no amount of “Renoise API tutorials” will (or should) teach you those basics.