Snippet: Render Song Separated By Instrument.

Needed this personally but was to lazy to make a full tool out of it. Maybe useful for someone else.

--[[----------------------------------------------------------------------------  
  
 Author : Alexander Stoica  
 Creation Date : 01/19/2012  
 Last modified : 01/20/2012  
  
 Renders a song separated by instrument.  
  
----------------------------------------------------------------------------]]--  
  
local render_options = {  
 sample_rate = 44100, -- 22050, 44100, 48000, 88200, 96000  
 bit_depth = 16, -- 16, 24, 32  
 interpolation = "cubic", -- cubic, sinc  
 priority = "high" -- realtime, low, high  
}  
  
--[[globals]]-------------------------------------------------------------]]--  
  
local thread = nil  
  
--[[functions]]-----------------------------------------------------------]]--  
  
function render()  
  
 local output_path = renoise.app():prompt_for_path(  
 "Select the destination folder:"  
 )  
  
 if not io.exists(output_path) then  
 print("Error: Invalid output folder specified, aborting!")  
 return  
 end  
  
 local rs = renoise.song()  
  
 -- cache volume levels  
  
 local cache = table.create()  
  
 for i, instrument in ipairs(rs.instruments) do  
  
 cache:insert(i, table.create())  
 cache[i] = {  
 pv = instrument.plugin_properties.volume,  
 sv = table.create()  
 }  
  
 for s, sample in ipairs(instrument.samples) do  
 if sample.sample_buffer.has_sample_data then  
 cache[i].sv:insert(s, sample.volume)  
 end  
 end  
  
 if cache[i].sv:is_empty() and  
 not instrument.plugin_properties.plugin_loaded then  
  
 cache[i] = nil  
 end  
  
 end  
  
 -- loop through each instrument  
  
 for t, vt in pairs(cache) do  
  
 local current_instrument = rs:instrument(t)  
  
 -- set volume of all instruments and their samples to 0  
  
 for i, _ in pairs(cache) do  
  
 local instrument = rs:instrument(i)  
  
 if instrument.plugin_properties.plugin_loaded then  
 instrument.plugin_properties.volume = 0  
 end  
  
 if not cache[i].sv:is_empty() then  
 for s, _ in pairs(cache[i].sv) do  
 local sample = instrument:sample(s)  
 sample.volume = 0  
 end  
 end  
  
 end  
  
 -- restore volume of the current instrument to render  
  
 if current_instrument.plugin_properties.plugin_loaded then  
 current_instrument.plugin_properties.volume = vt.pv  
 end  
  
 if not vt.sv:is_empty() then  
 for s, _ in pairs(vt.sv) do  
 local sample = current_instrument:sample(s)  
 sample.volume = vt.sv[s]  
 end  
 end  
  
 -- start rendering  
  
 local output_filename = output_path ..  
 string.format("%03d", t - 1) ..  
 string.format(" %03dBPM ", rs.transport.bpm) ..  
 string.gsub(current_instrument.name, "[<>:\"/\\|?*]", "")  
  
 output_filename = output_filename:match("^(.*%S)")  
  
 print("Rendering file: " .. output_filename .. ".wav")  
  
 rs.transport:panic()  
 rs:render(render_options, output_filename, render_done)  
  
 -- wait for renoise to finish rendering  
  
 coroutine.yield()  
  
 end  
  
 -- restore all volume levels  
  
 for i, vi in pairs(cache) do  
  
 local instrument = rs:instrument(i)  
  
 if instrument.plugin_properties.plugin_loaded then  
 instrument.plugin_properties.volume = vi.pv  
 end  
  
 if not vi.sv:is_empty() then  
 for s, _ in pairs(vi.sv) do  
 local sample = instrument:sample(s)  
 sample.volume = vi.sv[s]  
 end  
 end  
  
 end  
  
 print("Rendering completed!")  
  
end  
  
function render_done()  
  
 -- resume rendering thread  
  
 print("... done")  
 coroutine.resume(thread)  
  
end  
  
--[[entry point]]---------------------------------------------------------]]--  
  
thread = coroutine.create(render)  
coroutine.resume(thread)  
  
---------------------------------------------------------------------[[EOF]]--   

Updated the script… prompts for a destination path now and also has much improved output file naming.