New Tool (2.7): Split Into Separate Tracks

Hi,

I really love this feature, it’s awesome ! I would kill to have the same thing working with instruments, and not notes ; that would save me so much time !

Thanks a lot anyway, and I didn’t find any bugs :)

You are lucky, I just added that functionality!
I hope I didn’t break anything while adding this, so please test extensively!

New version is now available in the first post.

New features:

  • Can now also split according to instrument number instead of note value

fladd

Thnx for that addition! :drummer:

Yes, this is so cool ! With all these tools Renoise becomes really handy, I really prefer it more than Ableton Live and Logic to make electro now…

Huge thanks, I’ll test that, and post if I find any bug :)

Okay, I now implemented “selection only” as requested by Jonas(?)!

New version is available in the first post

New features:

  • Can now optionally run on selection only instead of whole track
  • Copying effects is now optional
  • Original track will be automatically muted

Again, please test!

There is one thing I really don’t like about this tool right now: It is extremely slow!
I tested it on the first track of the demo song “Complex life forms” over the whole song, with effects and it took 11 minutes!!! That of course also means that I had to deal with the warning dialogue (that comes after 10 sec) 66 times!!! This is very annoying and I am unsure how to further optimize the script. I know that it loops over all the sings several times, but according to me this should still be way faster. So if anyone has some input on this I would appreciate it.

fladd

dont know if this will help you,but beatslaugter posted a snippet of code,that would change the lpd of tracks,and then taktik posted a modification of that code,that really speed it up,and sort of solved the warning dialogue

but dont know if the code is somehow useble in your script

heres the topic where beatslaugter posted the snippet

cheerd m8! Will test it out tonight.

Some inspiration:

[luabox]
–[[============================================================================
Split into separate tracks

Author: Florian Krause siebenhundertzehn@gmail.com
Version: 0.4
============================================================================]]–

function split(what, implicit_off, copy_effects, selection_only)

– Define some consts
local EMPTY_PANNING = renoise.PatternTrackLine.EMPTY_PANNING
local EMPTY_VOLUME = renoise.PatternTrackLine.EMPTY_VOLUME
local EMPTY_NOTE = renoise.PatternTrackLine.EMPTY_NOTE
local NOTE_OFF = renoise.PatternTrackLine.NOTE_OFF

– Define some locals
local current_song = renoise.song()
local track = current_song.selected_track_index

local notes = table.create()
local cache = table.create()

renoise.app():show_status(“Splitting…”)

– Loop over lines
for pos,line in current_song.pattern_iterator:note_columns_in_track(track, true) do

– Check what to process, whole track or just selection
local process = false
if selection_only == true and line.is_selected == true then
process = true
elseif selection_only == false then
process = true
end

– If line should be processed
if process == true then

local what_value, note_pos
local off_pos, off_item, off_line

– If line is not empty
if line.note_value ~= EMPTY_NOTE then

– If note value is not a note-off
if line.note_value ~= NOTE_OFF then

– Check what value to look at, note value or instrument value
if what == 1 then
what_value = line.note_value
elseif what == 2 then
what_value = line.instrument_value
end

note_pos = notes:find(what_value)

– Create a new track if necessary
if not note_pos then
notes:insert(what_value)
current_song:insert_track_at(track+#notes)
current_song.selected_track_index = track
note_pos = #notes
current_song.tracks[track+#notes].name =
current_song.tracks[track].name…" - Split "…note_pos
end
cache:insert({pos.column, line, note_pos})

– If note value is a note-off
elseif line.note_value == NOTE_OFF then

– Check where to put the note-off
for _,item in ripairs(cache) do
if item[1] == pos.column then
note_pos = item[3]
break
end
end
end

– Check where to put the implicit note-off
local second = false
for _,item in ripairs(cache) do
if item[1] == pos.column then
if second == false then
second = true
else
off_pos = item[3]
off_item = item[2]
break
end
end
end

– If note pos was found
if note_pos ~= nil then
local note_pos_track =
current_song.tracks[pos.track+note_pos]
local note_pos_pattern_track =
current_song.patterns[pos.pattern].tracks[pos.track+note_pos]
local note_pos_note_column =
note_pos_pattern_track:line(pos.line).note_columns[pos.column]

– Copy the note value from the original track to the splitted one
note_pos_note_column:copy_from(line)

– Check how many note columns have to be visible in the splitted track
if pos.column > note_pos_track.visible_note_columns then
note_pos_track.visible_note_columns = pos.column
end

– Check if to show the volume column in the splitted track
if note_pos_track.volume_column_visible == false then
if note_pos_note_column.volume_value ~= EMPTY_VOLUME then
note_pos_track.volume_column_visible = true
end
end

– Check if to show thee panning column in the splitted track
if note_pos_track.panning_column_visible == false then
if note_pos_note_column.panning_value ~= EMPTY_PANNING then
note_pos_track.panning_column_visible = true
end
end

– Check if to show the delay column in the splitted track
if note_pos_track.delay_column_visible == false then
if note_pos_note_column.delay_value > 0 then
note_pos_track.delay_column_visible = true
end
end

– Set implicit note-off if enabled
if implicit_off == true and off_pos ~= nil then
local off_pos_track =
current_song.tracks[pos.track+off_pos]
local off_pos_pattern_track =
current_song.patterns[pos.pattern].tracks[pos.track+off_pos]
local off_pos_note_column =
off_pos_pattern_track:line(pos.line).note_columns[pos.column]

if off_pos_note_column.note_value == EMPTY_NOTE then
off_pos_note_column.note_value = NOTE_OFF

– Set possible delay value for implicit note-off
if line.delay_value > 0 then
off_pos_note_column.delay_value = line.delay_value
if off_pos_note_column.delay_value > 0 then
off_pos_track.delay_column_visible = true
end
end
end
end
end
end
end
end

– Loop again over all lines to copy effect columns
if copy_effects == true then
for pos,line in current_song.pattern_iterator:lines_in_track(track, true) do
– Check what to process, whole track or just selection
local process = false
if selection_only == true then
for column=1,12 do
if line.note_columns[column].is_selected == true then
process = true
break
end
end
elseif selection_only == false then
process = true
end
if process == true then
for index,effect_column in pairs(line.effect_columns) do
if effect_column.is_empty == false then
local orig_number = effect_column.number_value
local orig_amount = effect_column.amount_value

for t=1,#notes do
local other_track =
current_song.tracks[pos.track+t]
local other_pattern_track =
current_song.patterns[pos.pattern].tracks[pos.track+t]
local other_effect_column =
other_pattern_track:line(pos.line).effect_columns[index]

other_effect_column.number_value = orig_number
other_effect_column.amount_value = orig_amount

if index > other_track.visible_effect_columns then
other_track.visible_effect_columns = index
end
end
end
end
end
end
end

renoise.app():show_status(“'”…current_song.tracks[track].name…
“’ was split into “…#notes…” tracks”)
end

– Crate GUI
function gui()
local DIALOG_MARGIN = renoise.ViewBuilder.DEFAULT_DIALOG_MARGIN
local DIALOG_SPACING = renoise.ViewBuilder.DEFAULT_DIALOG_SPACING
local DIALOG_BUTTON_HEIGHT = renoise.ViewBuilder.DEFAULT_DIALOG_BUTTON_HEIGHT
local CONTROL_SPACING = renoise.ViewBuilder.DEFAULT_CONTROL_SPACING

local dialog
local vb = renoise.ViewBuilder()

local dialog_content = vb:horizontal_aligner{
margin=DIALOG_MARGIN,
spacing=DIALOG_SPACING,
mode=‘center’,

vb:column{
spacing = CONTROL_SPACING,
vb:chooser{id=‘what’, items={“Notes”, “Instruments”}, value=1},

vb:space{ height=DIALOG_SPACING },

vb:horizontal_aligner{
vb:checkbox{id=‘implicit_off’, value=true},
vb:text{text=“Set implicit Note-Offs”}
},
vb:horizontal_aligner{
vb:checkbox{id=‘copy_effects’, value=false},
vb:text{text=“Copy effects”}
},
vb:horizontal_aligner{
vb:checkbox{id=‘selection_only’, value=false},
vb:text{text=“Selection only”}
},

vb:space{ height=DIALOG_SPACING },

vb:horizontal_aligner{
mode = ‘center’,
vb:button{
id=‘split_button’,
height=DIALOG_BUTTON_HEIGHT,
width=80,
text=‘Split’,
released=function()
dialog:close()
split(vb.views.what.value, vb.views.implicit_off.value,
vb.views.copy_effects.value, vb.views.selection_only.value)
end
},
}
}
}

dialog = renoise.app():show_custom_dialog(‘Split into Tracks’, dialog_content)
renoise.song().selected_track:mute()
end

– Register the tool
renoise.tool():add_menu_entry{
name = ‘Pattern Editor:Track:Split into Separate Tracks…’,
invoke = gui
}

– Add key bindings
renoise.tool():add_keybinding{
name = “Pattern Editor:Track Operations:Split into Separate Tracks”,
invoke = gui
}
[/luabox]

More locals, prefer “:line(x)” over “lines”, prefer “k,v pairs” over “for i=1,#t
Avoid calling renoise.some_table whenever possible is most important.

Also have done some GUI cosmetics, just because I like doing GUI cosmetics.

One thing I wondered about: Why do you do “renoise.song().selected_track:mute()” when opening the dialog and not in split?

Wow, that is significantly faster! Thanks a lot Taktik! The first track of “Complex life forms” now splits in about 15 sec (only one warning dialogue)!
I knew that I was doing something extremely inefficient :slight_smile: It just took way too long. I was already trying to find something on Lua optimization and also read about using locals where possible. Using :line(x) instead of lines[x] now also seem plausible to me.
The GUI changes are also nice (I wanted to improve it anyway, might even not be possible anymore now).

About the mute(): That was just a copy paste error :slight_smile: I didn’t even realize that before you mentioned it.

Thanks again!
fladd

If using processes, what is the preferred way?

  1. Yielding often (like every 100ms or even more often). Doing this will make the tool a lot slower! But the GUI will be responsive.
  2. Yielding rarely, just to prevent the warning dialoge (so yield every 5 sec or so). Doing this will make the tool fast (as fast as without processes)! But the GUI updates will be very slow of course.

fladd

The thing is that the status update will of course also only be updated at each yield! So in my example after every 5 seconds. I could probably also go for a blocking version with a status in the Renoise status bar. I still have to test if the blocking dialogue entirely blocks or if the script continues in the background, even if you leave the dialogue open, without saying “No”.

fladd

@Taktik: Is there any way to know the number of selected note columns? For my process bar I need to know what 100$ is, when I want to show the status for the case that only selected notes should be split.
In the other case (split whole song) I briefly loop over all patterns in the beginning and get the number of lines (and multiply it by visible_note_columns) to get to know what 100% is.

fladd

Not sure what exactly you mean with “selected” note columns. Does this help?

renoise.song().tracks.visible_note_columns, _observable
→ [number, 0 OR 1-12, depending on the track type]

If you mean the selection line range, you’ll have to query it first:
See GitHub - renoise/xrnx: The official Renoise Lua Scripting repository

Thanks Taktik, I meant the range, but this works now (although it might needs some time to calculate). Can you (or anyone else) think of any further changes to improve the overall performance of the script even more? I mean it is already way better now after your improvements, no question, but I am just wondering how much we could still get out there (for large songs/patterns it can still take up to a minute or even longer, actually I can create a single pattern that will take over a minute). Anyway, I uploaded a new version.

Version 0.5 can now be downloaded from the first post!

New features:

  • The progress (an estimation) is now shown in the status bar
  • Speed improvements (thanks to Taktik!)

Please test!

fladd

Great teamwork :) , thanks for implementing the selection possibility!

For a future update I wish you also allow the selection to be cut out of the original track during splitting, instead of muting the complete track and leaving it in.

Right now I always go back to the original track and ctrl+x it manually, no big thing, but maybe it is easy for you to implement?

Also, add the option to split additional selections, into the same newly added tracks (or better yet, in a selection window where you can pick the destination tracks) through previous splitting (does this make sense in English? :) ).

Hi Jonas,

I think I don’t understand your last point. Additional selections from where? As far as I understood it from one of the scripts Taktik pointed me to, to get the number of selected lines), you cannot have a selection in one pattern and another in another.

fladd

No, I don’t mean at the same time! :)

Lets say after some time you decide to do another split of the same original track, instead of inserting the hits in the previously new created tracks (from a previous split), the script runs and batch creates more tracks.

Would be great if it could somehow remember where it previously put sounds, or if you could specify where the sounds should go, instead of always creating new tracks.

I’m happy the way it works now, no question, but thought I request anyway :wink: .

Well, the way it works right now, it is not straight forward to implement something like this. So I am afraid that it is quite unlikely that I will implement it. The original idea to make this tool also came from the fact that in Renoise you cannot record Midi into more than one track at once. Thus, I wrote the tool in order to split the recording out afterwards. In such a situation the current feature set is adequate I think.

fladd

Can this one be made available for 2.7 please? :)

Sure, might do so tonight.