[Solved] Help LUA: function to create a Group with two Tracks, one col

function insert_gt()
local song = renoise.song()
insert_track(song.selected_track_index, "TRACK 1", false)
insert_track(song.selected_track_index, "TRACK 2", true)
song:insert_group_at(song.selected_track_index + 2) <-----------------------------------------------
song:add_track_to_group(song.selected_track_index + 1 , song.selected_track_index + 2)
song:add_track_to_group(song.selected_track_index, song.selected_track_index + 2)

--Additional lines
song.tracks[1].group_parent.color_blend = 40
song.tracks[1].group_parent.collapsed = true

end

Hi 4Tey. I agree the two additional lines:

song.tracks[1].group_parent.color_blend = 40
  song.tracks[1].group_parent.collapsed = true

But only served if the first pattern is selected. Ifis selected, for example, the pattern 20 not work.I’m trying to implement with various groups and subgroups…

Is there any way to add the line marked up?song:insert_group_at(song.selected_track_index + 2) HERE!

Per example: song:insert_group_at(song.selected_track_index + 2).name = “MAIN GROUP”

".name = “MAIN GROUP” work!

“.color_blend= 40” and _“.collapsed = true”_doesn’t work in this line

With joule solution not I get to add more major groups encompassing more subgroups.I’m trying things.

Raul I only gave that as a crude example to show ‘group_parent’ and one way to modify the background colour of the group you have created. If you wanted it to work on another track (apart from a send track) you could sort of do:

--Additional lines
song.tracks[song.selected_track_index].group_parent.color_blend = 40
song.tracks[song.selected_track_index].group_parent.collapsed = true

But I suspect that this isn’t what you are looking for anyway :slight_smile:

Now if you simplify Joules example code to say (again wont work on a send track):

local track_settings = {

Setting_1 = {
  group_name = "Main Group",
  group_blend = 50,
  group_collapse = true,
  { name = "TRACK 1", collapsed = false, blend = 20 },
  { name = "TRACK 2", collapsed = true, blend = 20 },
     }
}

function insert_track(track_index, group_index, name, is_collapsed, blend)
  renoise.song():insert_track_at(track_index)
  local track = renoise.song():track(track_index)
  track.color_blend = blend                     
  track.name = name
  track.visible_effect_columns = 0
  track.visible_note_columns = 12
  track.volume_column_visible = false
  track.collapsed = is_collapsed
  for note_column = 1, 12 do
    track:set_column_name(note_column, "NOTE " .. note_column)
  end
  renoise.song():add_track_to_group(track_index, group_index)
end
        
function insert_gt(track_setting)
  local song, i = renoise.song(), 1
  local group_index = song.selected_track_index
  song:insert_group_at(group_index)
  song:track(group_index).name = track_settings[track_setting].group_name
  song:track(group_index).color_blend = track_settings[track_setting].group_blend
  for val, track_setting in ipairs(track_settings[track_setting]) do
    local group_index = group_index + i
    insert_track(song.selected_track_index, group_index, track_setting.name, track_setting.collapsed, track_setting.blend)
    i = i + 1
  end
  song:track(group_index).group_parent.collapsed = track_settings[track_setting].group_collapse
end
     
insert_gt("Setting_1")

That will roughly give you what you have in your first post screenshot, plus you can define the structure of what tracks are created.

I’m not really sure what you are ultimately trying to write though. Whatever it is, it sounds complicated. Good luck Raul! :slight_smile:

Thanks 4Tey!!! :slight_smile:

All the codes serve me!You guys are helping me a lot!

A silly question…

Is it possible to apply the “color_blend” separately in each “note_column”?

Per example: (a octave)

note_column 01 in color_blend = 40

note_column 02 in color_blend = 10 #

note_column 03 in color_blend= 40

note_column 04 in color_blend= 10 #

note_column 05 in color_blend= 40

note_column 06 in color_blend= 40

note_column 07 in color_blend= 10 #

note_column 08 in color_blend= 40

note_column 09 in color_blend= 10 #

note_column 10 in color_blend= 40

note_column 11 in color_blend= 10 #

note_column 12 in color_blend= 40

I think Renoise does not support it,but I wonder if there is some trick.The approximate result:

6756 note_column_colors.png

Part of the code I’m using:

function insert_track(index, name, is_collapsed)
  renoise.song():insert_track_at(index)
  local track = renoise.song():track(index)
  track.color_blend = 40
  --track.color = {0xff,0xff,0xff}
  track.name = name
  track.visible_note_columns = 12
  track.visible_effect_columns = 0
  track.volume_column_visible = false
  track.panning_column_visible = false
  track.delay_column_visible = false
  track.sample_effects_column_visible = false
  for note_column = 1, 12 do
    track:set_column_name(1, "C do")
    track:set_column_name(2, "C#")
    track:set_column_name(3, "D re")
    track:set_column_name(4, "D#")
    track:set_column_name(5, "E mi")
    track:set_column_name(6, "F fa")
    track:set_column_name(7, "F#")
    track:set_column_name(8, "G sol")
    track:set_column_name(9, "G#")
    track:set_column_name(10, "A la")
    track:set_column_name(11, "A#")
    track:set_column_name(12, "B si")
    --track:set_column_name(note_column, "NOTE " .. note_column)
  end
  if is_collapsed then
    track.collapsed = true
  end 
end

Is it possible to apply the “color_blend” separately in each “note_column”?

No, as far as I’m aware Raul. You can only apply a colour (color) to whole tracks.

P.S. It looks way more complicated than MiniRoll. Good luck Raul! :slight_smile:

What a pity!

I also wonder if it would be possible more things via LUA:

  1. Forcing writing notes in the pattern as follows: range of 10 octaves = track 1 to track 10 (track 1 = octave 0, track 2 = octave 1… track10 = octave 9) andsort notes within 12 notes columns in each track,both play live from the MIDI keyboard or alphanumeric keyboard. This means that each note column can contain only one type of note and each track one type of octave. This only works within a group of 10 tracks, the group with a specific name. In all other cases it should not work.:
  • Create a Group with name “Piano Group”.
  • Create 10 Tracks inside with names “X Octave”.
  • Deploy 12 Note Columns in each track (120 note columns in total).
  • Assing in each Note Columns only one note of 120 possible notes, always inside in the Group named “Piano Group”.
  • Move the group into the Pattern Editor, and keep it running that way.
  • …(the template of all these points I already have made).
  1. If the above is not possible, at least to sort the notes before written with a tool, only work within te Group named “Piano Group”.I think there is a sort notes tool, but is not compatible with v3.1.

No matter if it sounds very beast.I just want to know if possible (in the end, is summarized as force only a one note in each Note Column).

  1. Short answer: no. Long answer: It would be technically possible to automatically move notes after they’re entered, but it’s very hackish. Not a feasable option IMO, especially due to the unsound effects it would have interface wise.

  2. Sorting the notes after you’ve entered them is not very difficult, but you would have to strike a shortcut manually each time you want to do it.

Does each group contain ten tracks, and each track 12 columns? I estimate the code to be about 15 lines :wink: By using note_value modulus 12 you can easily calculate what track and column a note should be moved to. Everything else is about simple iteration.

I’d imagine the maths being something like:

dest_track = first_octaves_track_index + math.floor(note_value/12) + 1

dest_column = note_value % 12

  1. Short answer: no. Long answer: It would be technically possible to automatically move notes after they’re entered, but it’s very hackish. Not a feasable option IMO, especially due to the unsound effects it would have interface wise.

  2. Sorting the notes after you’ve entered them is not very difficult, but you would have to strike a shortcut manually each time you want to do it.

Does each group contain ten tracks, and each track 12 columns? I estimate the code to be about 15 lines :wink: By using note_value modulus 12 you can easily calculate what track and column a note should be moved to. Everything else is about simple iteration.

I’d imagine the maths being something like:

dest_track = first_octaves_track_index + math.floor(note_value/12) + 1

dest_column = note_value % 12

Wow joule!!!This to me is the most exciting thing I’ve felt in months in these forums.If the Case 2 is possible and also the code is not long, I would like to provide you a code to use as a base.I will create exactly what I’d do, and if you are able to create the mathematical code missing, for me you would be a hero.

I guess on the mathematical code with small changes could also use groups with fewer tracks, for example: 10 tracks (the base), 8, 6 or 4 tracks, which would probably be the most used.

Action steps in Case 2) would be:

  1. Step 1: Use only a track (name “Track 1”) and you compose a complex melody with a MIDI keyboard (To test the mathematical code the melody occupies 10 octaves, to the beast!).In effect you need to drag the note, the value OFF associated, but also the volume and delay of note :wacko:.I do not know if this last complicate the mathematical code, but it would be necessary (and panning note).
  2. Step 2: Convert the “Track 1” in a Group (name = “Piano Group”) with 10 Tracks (name of tracks = “0 Octave”, “1 Octave”, … “8 Octave, 9 Octave”). Each Track contains 12 Note Columns (C-, C#, … A#, B )). The melody composed aboveappears intact on the track “0 Octave”, the first (better yet, take the track “4 Octave” always as the main octave column).
  3. A function in Pattern Editor Menu named “Sort Notes in Piano Group”, for order the notesmanually.
  4. Ready!

I’m going to create a main.lua f (a tool .xrnx) for Step 2 and a melody-10-octaves.rnsx for test of the Step 1, I can prove all this.I will share here the two files to make things easier. If you create mathematical code would be a God!!! :slight_smile:

Please, I would love you to help me on this issue, after providing the two files.You will understand immediately what I want to do.

NP, I think. Also, let me know if you want any kind of auto-collapsing of the octave tracks (either depending on which are empty, or which track is selected).

Hi joule! Thanks so much for the help!!! :slight_smile:

I’m working with the code to deadlines, when I have some time…

The two files of the PIANO GROUP TOOL :

  1. ulneiz-Piano-Group-v1_12-OCTAVES.xrns for test:6758 ulneiz-Piano-Group-v1_12-OCTAVES.xrns
  2. ulneiz.Piano.GroupV1.xrnx with the code:6759 ulneiz.Piano.GroupV1.xrnx

Instructions:

  1. Run the song XRNS and install the XRNX. Ready!
  2. In Track 02 selected, go:" Pattern Editor \ Track \ Create ‘Piano Group’ \ 10 Octaves \ Step 1 - Convert the ‘Track’ in ‘PIANO GROUP (Octaves 0-9)’ ", and run.
  3. The track selected is converted in “Piano Group” automatically.
  4. …From now on, need to create the mathematical code to sort the notes! ( Step 2 )

The Code (to see any user):

-- Name: Piano Group
-- Version: V1 (Work OK in R3.1 (W10 64bits)
-- Menu: Pattern Editor / Track / Create 'Piano Group'

-- FUNCTION TRACKS OCTAVES [5,6,7,8,9][0,1,2,3] ---------------------

function insert_track_2(index, name, is_collapsed)
  local song = renoise.song()
  song:insert_track_at(song.selected_track_index)
  local track = song:track(index)
  track.color_blend = 10
  track.name = name
  track.visible_note_columns = 12
  track.visible_effect_columns = 0
  track.volume_column_visible = false
  track.panning_column_visible = false
  track.delay_column_visible = false
  track.sample_effects_column_visible = false
  for note_column = 1, 12 do
    track:set_column_name(1, "C do")
    track:set_column_name(2, "C#")
    track:set_column_name(3, "D re")
    track:set_column_name(4, "D#")
    track:set_column_name(5, "E mi")
    track:set_column_name(6, "F fa")
    track:set_column_name(7, "F#")
    track:set_column_name(8, "G sol")
    track:set_column_name(9, "G#")
    track:set_column_name(10, "A la")
    track:set_column_name(11, "A#")
    track:set_column_name(12, "B si")
  end
  if is_collapsed then
    track.collapsed = true
  end 
end

-- FUNCTION ONLY "TRACK OCTAVE 4" (OCTAVE BASE!!!!!!) ------------------------

function insert_track_1(index, name, is_collapsed)
  local song = renoise.song()
  song:insert_track_at(song.selected_track_index + 5)
  local track = song:track(index)
  track.color_blend = 10
  track.name = name
  track.visible_note_columns = 12
  track.visible_effect_columns = 0
  track.volume_column_visible = false
  track.panning_column_visible = false
  track.delay_column_visible = false
  track.sample_effects_column_visible = false
  for note_column = 1, 12 do
    track:set_column_name(1, "C do")
    track:set_column_name(2, "C#")
    track:set_column_name(3, "D re")
    track:set_column_name(4, "D#")
    track:set_column_name(5, "E mi")
    track:set_column_name(6, "F fa")
    track:set_column_name(7, "F#")
    track:set_column_name(8, "G sol")
    track:set_column_name(9, "G#")
    track:set_column_name(10, "A la")
    track:set_column_name(11, "A#")
    track:set_column_name(12, "B si")
  end
  if is_collapsed then
    track.collapsed = true
  end 
end

-- FUNCTION TRACKS AUX ----------------------------------------

function insert_track_0(index)
  local song = renoise.song()
  song:insert_track_at(song.selected_track_index + 1)
end

-- FUNCTION I: CREATE NEW PIANO GROUP (OCTAVES 0-9), 1 GROUP WITH 10 TRACKS (EACH TRACK WITH 12 NOTE COLUMNS)

function convert_piano_group0_9()
  local song = renoise.song()
  insert_track_0(song.selected_track_index) -- insert initial track x4 (necessary for construction in cas exist only a track in Pattern Editor)
  insert_track_0(song.selected_track_index) -- NOTE: is necessary add 4 tracks because drag 4 positions the "object track" (cursor selected in Pattern Editor)
  insert_track_0(song.selected_track_index) -- ...
  insert_track_0(song.selected_track_index) -- ...
  
  insert_track_2(song.selected_track_index, "3 OCTAVE", true)
  insert_track_2(song.selected_track_index, "2 OCTAVE", true)
  insert_track_2(song.selected_track_index, "1 OCTAVE", true)
  insert_track_2(song.selected_track_index, "0 OCTAVE", true)

  song:select_next_track() -- tab nex x4
  song:select_next_track()
  song:select_next_track()
  song:select_next_track()
  

  insert_track_1(song.selected_track_index, "4 OCTAVE (Base)", false) -- convert the track selected in new track with 12 Note Columns (insert a new track)
  song:delete_track_at(song.selected_track_index + 5) -- ... the new track deleted!

  song:select_previous_track() -- tab previous x 4 (for insert tracks)
  song:select_previous_track()
  song:select_previous_track()
  song:select_previous_track()

  insert_track_2(song.selected_track_index, "9 OCTAVE", true)
  insert_track_2(song.selected_track_index, "8 OCTAVE", true)
  insert_track_2(song.selected_track_index, "7 OCTAVE", true)
  insert_track_2(song.selected_track_index, "6 OCTAVE", true)
  insert_track_2(song.selected_track_index, "5 OCTAVE", true)

  song:delete_track_at(song.selected_track_index + 7) -- delete track x4 (delete the previous tracks inserted)
  song:delete_track_at(song.selected_track_index) --
  song:delete_track_at(song.selected_track_index) -- +7 ???
  song:delete_track_at(song.selected_track_index) -- 

  -- Group
  song:insert_group_at(song.selected_track_index + 1).name = "PIANO GROUP (Octaves: 0-9)" -- the group "PIANO GROUP"
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)
  song:add_track_to_group(song.selected_track_index - 9, song.selected_track_index + 1)

  song.tracks[song.selected_track_index].group_parent.collapsed = true -- collapse group
  -- Colors (Gold) --
    song.tracks[song.selected_track_index].group_parent.color_blend = 27 -- blend group
    song.tracks[song.selected_track_index].group_parent.color = {0xFF,0xB5,0x23}
    song.tracks[song.selected_track_index - 9].color = {0xEF,0xA5,0x13} --0
    song.tracks[song.selected_track_index - 8].color = {0xAF,0x5B,0x00}
    song.tracks[song.selected_track_index - 7].color = {0xEF,0xA5,0x13}
    song.tracks[song.selected_track_index - 6].color = {0xAF,0x5B,0x00}
    song.tracks[song.selected_track_index - 5].color = {0xEF,0xA5,0x13}
    song.tracks[song.selected_track_index - 4].color = {0xAF,0x5B,0x00}
    song.tracks[song.selected_track_index - 3].color = {0xEF,0xA5,0x13}
    song.tracks[song.selected_track_index - 2].color = {0xAF,0x5B,0x00}
    song.tracks[song.selected_track_index - 1].color = {0xEF,0xA5,0x13}
    song.tracks[song.selected_track_index].color = {0xAF,0x5B,0x00} --10

  song:select_previous_track() -- tab previous x 5 (for insert tracks)
  song:select_previous_track()
  song:select_previous_track()
  song:select_previous_track()
  song:select_previous_track()
end

renoise.tool():add_menu_entry {
  name = "Pattern Editor:Track:Create 'Piano Group':10 Octaves:Step 1 - Convert the 'Track' in 'PIANO GROUP (Octaves 0-9)'",
  invoke = function() convert_piano_group0_9() end
}

-- MATHEMATICAL CODE I: SORT NOTES IN PIANO GROUP (OCTAVES 0-9) ========================================================
-- ... sort_piano_group0_9()
-- ...
-- ...
-- ...
-- ...

renoise.tool():add_menu_entry {
  name = "Pattern Editor:Track:Create 'Piano Group':10 Octaves:Step 2 - Sort the 'Notes' in 'PIANO GROUP (Octaves 0-9)'",
  invoke = function() sort_piano_group0_9() end
}

-- =====================================================================================================================

I know that this Code can be improved. But it works as I want.I had problems with the index, but I have solved. I guess there are more elegant ways to solve the same, but I want a code providing me quick edit control for all (collapse, colors, reorder, etc).Please understand that I’m new at this.If you want to review the code and find something better, it will be a pleasure to study it.

This Code basically converts (not insert) the Original Track in a Group with 10 Tracks,focusing the Original Track in the Track (octave 4 - OCTAVE BASE); Track number 5 inside the Piano Group.

On the "Pattern Editor \ Track \ Create ‘Piano Group’ \ 10 Octaves \ Step 2 - Sort the ‘Notes’ in ‘PIANO GROUP (Octaves 0-9)’ " invoke the mathematical function code missing for sort the notes.Could you do the rest of the code?

The logical steps of use for the composer are:

  1. Create a new song XRNS.
  2. Insert a instrument 01.
  3. With a loaded instrument 01 and keyboard MIDI record a complex melody within any track (name = Original Track, or “Track 01”).
  4. Use Step 1 to convert the track composed in a Piano Group automatically.
  5. Use Step 2 to Sort the Notes within the 10 Track Octaves automatically.If this works fine will be very great!!!
  6. Finish!

Notes:

  • Always see the Matrix Editor to understand!!!
  • The Piano Group tool should work well regardless of the initially selected track.
  • Later it could be reduced the code unlessoctaves (with 2 more mathematical codes):
    1. 6 octaves: 2 - 7
    2. 3 octaves: 3 - 5

Joule, if you create mathematical code , then I share the tool in the forum.and I will be eternally grateful!!! :slight_smile:

Thanks!!!

Edited:It would be super if when deploying the notes, the empty tracks automatically collapse and open tracks with notes.

“Explode octaves to tracks” would be the common sense name for such a shortcut.

Under that submenu, I suggest having just these two menu entries:

  1. 10 octaves

  2. Only octaves in use

I.e - no arbritrary number of octaves since that would confuse how out-of-range notes are handled.

It all seems pretty simple and straight-forward. I’ll just have to check whether note offs need to be handled some proper way during conversion. That’s probably the most tricky part code wise, but not very tricky.

PS.

Some details in my train of thought when planning it all:

  1. Make an iterator on each note to check its length.

  2. Use lines_in_song iterator is probably the simplest way to cross pattern boundaries when checking note lengths.

  3. I’ll start making the explode note_value function, which is very simple. After that I’ll also make sure that note lengths will follow.

Ok Joule! Any change is welcome!

  1. Only octaves in use. If this is possible it would be great. So the tool is as simple as possible.

:slight_smile: all this is music to my ears!!! :walkman:

Separate issue:

After all this process, I wonder if a third Step to accumulate all the notes displayed on a single track in a logical criterion, something like an inverted step, if is possible.Actually it is not necessary that the result is identical to the original, before deploying the notes. But keep the same sound.The step would be something like: “Step 3 - accumulating notes on a track.”

Not sure what you mean by accumulating all the notes?

Hmm… I’ll start from the code I posted last time since your code is a bit messy :slight_smile:

General principles i TRY to follow when making a script is to make it have a sane structure to maximize reusability, expandability and flexibility. Try to establish the most intelligent way to solve your problem in terms of what functions and data structures to use. This takes a little bit of experience. For example, when you start to understand how to pass and return data to/from functions a new world will open to you :slight_smile:

The second principle, which in some cases defies the first principle, is to aim for simplicity and maintainability :slight_smile: If you’re a skilled coder the code should ideally document/explain itself. (I am not).

Imo, there is a shortcut you can use that in most cases will make you follow these principles to a high enough degree: Fewer lines are better.

Not sure what you mean by accumulating all the notes?

For now, better leave this issue for later. Better focus attention in the “Piano Group Tool”, for calling him somehow.

Hmm… I’ll start from the code I posted last time since your code is a bit messy :slight_smile:

Of course. I am aware. Yet I have limited knowledge of LUA.You can transform the entire code. My intention was to show exactly the idea for the tool.

General principles i TRY to follow when making a script is to make it have a sane structure to maximize reusability, expandability and flexibility. Try to establish the most intelligent way to solve your problem in terms of what functions and data structures to use. This takes a little bit of experience. For example, when you start to understand how to pass and return data to/from functions a new world will open to you :slight_smile:

The second principle, which in some cases defies the first principle, is to aim for simplicity and maintainability :slight_smile: If you’re a skilled coder the code should ideally document/explain itself. (I am not).

Imo, there is a shortcut you can use that in most cases will make you follow these principles to a high enough degree: Fewer lines are better.

I will keep in mind all these comments when you have the new code that you are making.I think this is a great way to learn.I’m excited about all this! :slight_smile:

Here is the basic code that will explode the note_values and insert proper note offs.

Feel free to prettify anything and let me know if I missed anything. I can then add the proper menu entries (unvisible on send/master tracks).

There is a small bug I will fix later. I predict that an error will occur if the very last note is not a note_off :slight_smile:

local group_settings = {
ten_octaves = {
  name = "Octaves",
  color = { 0xFF, 0xFF, 0xFF },
  color_blend = 0,
  tracks = {
  { name = "Oct 0", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 5 },
  { name = "Oct 1", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 15 },
  { name = "Oct 2", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 5 },
  { name = "Oct 3", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 15 },
  { name = "Oct 4", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 5 },
  { name = "Oct 5", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 15 },
  { name = "Oct 6", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 5 },
  { name = "Oct 7", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 15 },
  { name = "Oct 8", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 5 },
  { name = "Oct 9", collapsed = true, color = { 0xFF,0xFF,0xFF }, color_blend = 15 },
  },
  },
}

local column_names = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
}

function insert_track(track_index, group_index, track_setting)
  renoise.song():insert_track_at(track_index)
  local track = renoise.song():track(track_index)
  track.visible_effect_columns, track.visible_note_columns, track.volume_column_visible = 0, 12, false
  for parameter_name, parameter_value in pairs(track_setting) do
   track[parameter_name] = parameter_value
  end
  for note_column = 1, 12 do
    track:set_column_name(note_column, column_names[note_column])
  end
  renoise.song():add_track_to_group(track_index, group_index)
end
        
function insert_gt(group_setting)
  local song = renoise.song()
  local i, group_index, group_setting = 1, song.selected_track_index + 1, group_settings[group_setting]
  song:insert_group_at(group_index)
  for parameter_name, parameter_value in pairs(group_setting) do
    if not (parameter_name == "tracks") then
      song:track(group_index)[parameter_name] = parameter_value
    end
  end
  for _, track_setting in ipairs(group_setting.tracks) do
    local group_index = group_index + i
    i = i + 1
    insert_track(song.selected_track_index, group_index, track_setting)
  end
end

function explode_pitches(track_index, first_octave_track_index)
  local song = renoise.song()
  local track = song:track(track_index)
  local pattern_iterator = song.pattern_iterator:note_columns_in_track(track_index, true) -- compact helper provided by the renoise api
  local notes = { } 
-- building a custom table with all actual notes in track makes the entire task much simpler. 
  for pos, note_column in pattern_iterator do
    if not note_column.is_empty then
      table.insert(notes, { note_column = note_column, pattern = pos.pattern, track = pos.track, column = pos.column, line = pos.line })
    end
  end
-- some fancy re-sorting of the custom table here (to per 'column_in_song'). possibly ugly but working. this will make note-off searching super easy later on (next tablepos)
  table.sort(notes, function(a,b) 
    if a.column < b.column then return true
    elseif a.column > b.column then return false
    elseif a.pattern < b.pattern then return true
    elseif a.pattern > b.pattern then return false
    elseif a.line < b.line then return true
    elseif a.line > b.line then return false
    end
    end)
-- all data is now prepared and providing everything we need. time to move notes to proper columns
  for i, note in ipairs(notes) do
    if not (note.note_column.note_value == 120) then -- don't try to move note_offs
      local dest_track = first_octave_track_index + math.floor(note.note_column.note_value/12)
      local dest_column = (note.note_column.note_value % 12) + 1
      song:pattern(note.pattern):track(dest_track):line(note.line):note_column(dest_column):copy_from(song:pattern(note.pattern):track(note.track):line(note.line):note_column(note.column))
-- time to find and insert proper note_off for every note (both from real note_offs or a new note cutting)
      if notes[i].column == notes[i+1].column then -- avoid some special cases.. make sure next table entry is in the same column (i e cutting previous entry)
        song:pattern(note.pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).note_value = 120
        song:pattern(note.pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).delay_value = notes[i+1].note_column.delay_value -- don't forget note off delay
      end
    end
  end
end
     
insert_gt("ten_octaves")
   
explode_pitches(renoise.song().selected_track_index, renoise.song().selected_track_index + 1)

Wow! Great Job!Some absurd observations:

  1. I think missing the collapse of the FX column in group “octaves”. Not?
  2. I guess that works by selecting a track with the notes composed previously and that automatically deletes the original composite track.
  3. In the menu, it should not be an option if the composer selects a group, the master track or track effect. The tool only should appear to select the track, for avoid errors.
  4. As I see working the tool, I comment you if the tool does what it intended…

EDITED: 5.Imagine that the composition occupies 2,3 and 4 octaves. The end result can leave the track selected octave 3, the middle? Or do you always select track of octave 2, always the first?Find the center of the compositionor something approaching…

I will try to study thoroughly to understand what I can.It’s nice to see the orderly code.I love the ordered code. Lets take potions and create similar things very easily. It is manipulable.With the mathematical code I lose myself. :wacko:

  1. You can actually add:

ten_octaves = {

collapsed = true,

Any parameter there will be applied to the group/track parameter with the exact same name.

  1. I haven’t deleted the source track but it’s easy to add. renoise.song().delete_track_at(any_track_index)

  2. That would be too much of a custom behavior imo. You always have to be wary of interface design, and make a tool as general and possible without assuming the user wants anything extra. Anyone not expecting such a behavior will react “wtf was that? is that a bug?” :slight_smile: Being highly “empathic” when designing anything is a must.

  1. Sorry! I meant the FX column of the group. With “collapsed=true” collapse all group, not only the FX column at the right.

  2. Ok

5.You could add the code as a hidden option to customize.If it is too cumbersome, the simpler the better.While the fine tool to work …

I was afraid of this problem:some OFF values disappear when jumping pattern, so the note still sounds when it should stop.I guess this will be harder to fix.

EDITED:

For example: in ulneiz-Piano-Group-v1_12-OCTAVES.xrns ,

Original Track:

=====================

Pattern 02:

Line 61: “F#2 00 65 F3”

Pattern 03:

Line 00: “OFF 96” <------

=====================

Converted Track:

=====================

Pattern 02

Line 00: “OFF 96” <------

Line 61: “F#2 00 65 F3”

=====================

The value OFF “jump” to pattern above.I guess what you have seen…

  1. Sorry! I meant the FX column of the group. With “collapsed=true” collapse all group, not only the FX column at the right.

  2. Ok

5.You could add the code as a hidden option to customize.If it is too cumbersome, the simpler the better.While the fine tool to work …

  1. That’s also available in the lua API. You can freely add any parameter to the table in the tool. Locate where the fx visibility parameter is in the lua API and add it to the table.

I wasn’t able to bugtest the tool with sound yesterday :slight_smile:

these lines:

song:pattern(note.pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).note_value = 120

song:pattern(note.pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).delay_value = notes[i+1].note_column.delay_value – don’t forget note off delay

should be:

song:pattern(notes[i+1].pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).note_value = 120

song:pattern(notes[i+1].pattern):track(dest_track):line(notes[i+1].line):note_column(dest_column).delay_value = notes[i+1].note_column.delay_value – don’t forget note off delay