Idea: Unison Generator

Sorry, my fault for reading via the New Content tab and not paying attention to which forum we are in. Obvious now I realise it’s the Tool forum, ignor me completely above ;)

i really want this! :dribble:

Yes, it could be done.

Look for renoise.song().instruments[].samples[]:copy_from(), renoise.song().instruments[]:insert_sample_mapping(), and friends in:

You could also inspire yourself from the Generate Custom Wave script, but some API functions have changed…

I was thinking of starting my own unison tool thread, however, one exists…

I think there are a lot of ppl who would love a tool like this, not the least of which, me.

Coding is definitely not one of my strengths or even close to it for that matter, however, I am sure there are people on this forum who could just whip this one out and even see the use in a tool like this themselves.

So lets revive this thread!! :drummer: :guitar: :yeah:

*2

Well, some ideas like this had been in another thread, but I think it’s easier to do it manually… Also, I didn’t come very far yet with Renoises ViewBuilder… nonetheless, with probably the simplest tool ever, Layer All, the workflow could be:

  1. go to instr settings
  2. alt-click in the samples window (alternatively right-click and ‘set focus here’)
  3. press ctrl-D a bunch of times
  4. right-click one of the samples in the list, click “Layer All”
  5. now scroll through the samples and adjust the individual detune settings until something sounds sweet!

It is absolutely NOT easier to do it manually.

That is why it is a built-in function on mostly all vsti synths… it takes around 1 second to double, quadruple osc voices with the unison function as opposed to almost a min or more in renoise, esp if you want 8 or more voices.

Now, not to say I don’t appreciate the work you have put in your tool…which still is not an alternative but aims to at least save some of the time involved.

My only issue, is that it resets the base note of the samples to c-4…most of my samples are in other keys, thus making me have to reset the base note individually for each sample…defeating the time-saving this tool is supposed to perform.

Thx again for your hard work.

Tarek

Been working slightly on this and got something that is currently very destructive but some interesting results from a quick play last night.

Load a sample into a Instrument Sample slot. Create as many empty sample slots as you need (couldn’t work out how to do this with the API and ViewBuilder currently defeats me too!)

Running this script from the Terminal will then copy the sample from current slot to all available slots and give each a different Vol, Pan and Finetune setting (not truly as currently have no internet at home so only way I could think to generate spread but restrained values was trig functions on os.clock…

[details=“Click to view contents”] --[[===================================================================================
Unison Generator

Author: Dale Powell (Kazakore) <dj_kaza>

Version: 0.1

===================================================================================]]–



– Variables





– Locals within Function



local rns = renoise.song()

local ins = rns.selected_instrument_index

local samp = rns.selected_sample_index

local unison = #rns.instruments[ins].samples





–[[ Create Slices - CAN NOT BE MAPPED ACROSS KEYBOARD!

for i=0 , unison do

rns.instruments[ins].samples[1]:insert_slice_marker(1)

end ]]–



–[[Create New Instrument (at current +1) -COME BACK TO THIS LATER

rns.insert_instrument_at(rns.instrument[ins+1])



–]]



– Copy Sample across multiple sample slots

for i=1 , unison do

rns.instruments[ins].samples[i]:copy_from(rns.instruments[ins].samples[samp])

– Add keyzone for each sample

– rns.instruments[ins]:insert_sample_mapping(rns.instruments[ins].samples[i])

– Add unique Pan to each Sample.

rns.instruments[ins].samples[i].panning = (math.cos(i * os.clock(i))/2 +0.5) --How can I find random without internet!

– Add Volume to Each

rns.instruments[ins].samples[i].volume = (math.sin(i * os.clock(i)) + 1.2)

– Add Finetune to each.

rns.instruments[ins].samples[i].fine_tune = (math.cos(i * os.clock(i)) *127)

end


[/details]



And for niceness here is a blank, 8 slot Instrument will full Zone spread for each sample:

Click to view contents <?xml version="1.0" encoding="UTF-8"?>



Samples

Unison Blank

0

0





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false





1.0

0.5

48

0

0

false

false

false

16

Cut

Cubic

Off

false

0

0

true

false











0

true

true

48

0

119

0

127





1

true

true

48

0

119

0

127





2

true

true

48

0

119

0

127





3

true

true

48

0

119

0

127





4

true

true

48

0

119

0

127





5

true

true

48

0

119

0

127





6

true

true

48

0

119

0

127





7

true

true

48

0

119

0

127











false

Curve

true

12

0

71

Off

128



Curve

97

0.0

Unipolar



0,1.0

12,1.0

24,0.40000000596046448

48,0.40000000596046448

96,0.0







Off

3

27

0





Off

3

27

0







false

Curve

false

0

0

48

Forward

128



Curve

49

0.0

Bipolar



0,0.5

12,1.0

36,0.0

48,0.5







Off

3

27

0





Off

3

27

0







false

Curve

false

0

0

48

Off

128



Curve

49

0.0

Bipolar



0,1.0

2,0.5

48,0.5







Off

3

27

0





Off

3

27

0





3



false

Curve

false

0

0

71

Off

128



Curve

97

0.0

Unipolar



0,0.75







Off

3

27

0





false

20

80

100







false

Curve

false

0

0

71

Off

128



Curve

97

0.0

Unipolar



0,0.20000000298023224







Off

3

27

0





false

20

80

100









0

0

1.0





true

Bus #01

L+R

true

-1





true

-1

-1,-1





false

-1

-1





false

0

ext. MIDI

0

-1

-1

0

8000







Now I do need help with getting the API to create empty Sample Slots as I couldn't do it.

Also how to generate new ZoneMaps completely flummoxed me too! The below I thought was meant to do something along those lines:
[quote="E.H.V.A.H. SDC, post:10, topic:31924"]
Well, some ideas like this had been in another thread, but I think it's easier to do it manually.. Also, I didn't come very far yet with Renoises ViewBuilder.. nonetheless, with probably the simplest tool ever, Layer All, the workflow could be:

[/quote]

But my experiments with it it doesn't seem to be doing as I expected it would!



So please how to use the "rns.instruments[ins]:insert_sample_mapping()" command as I really couldn't quite get my head around it.


Then maybe some help with a GUI and tightening up the maths so people can select their own ranges ;)



(EDIT: Changed Code for Spoiler tags.)

Hi. I found no way of using the api to “create a drum kit”. So what I did do was add a instrument box script a few days ago that creates about 64 empty slices. They only show up after you click drumkit (then they show up as empty sample zones).

  
renoise.tool():add_menu_entry {  
 name = "Instrument Box:Create empty sample slices",  
 invoke = function() emptyslices()  
 end  
 }  
  
function emptyslices()  
local si=renoise.song().selected_instrument  
local ss=renoise.song().selected_sample  
local ssi=renoise.song().selected_sample_index  
ssi=1  
for i=1,64 do  
si:insert_sample_at(i)  
end  
  
for i=1,64 do   
renoise.song().selected_instrument.samples[i].name="empty_sampleslot" .. i  
end  
renoise.song().selected_instrument.name=("multiloopersampler_instrument" .. renoise.song().selected_instrument_index)  
w.active_middle_frame= 3  
--  
end  
  

Cool the insert_sample_at works fine so once I know how to make a GUI I should be able to create it to the amount of Unison voices selected…

Don’t suppose you’ve had any joy on the adding/configuring Zones?

-- Insert a new sample mapping for the given sample by index. Optionally  
-- pass initial base note, note and velocity ranges in a table. When no initial  
-- ranges are specified, newly created mappings have a velocity range of (0-127)   
-- and a note range of (0-119). Default base note is 48 (C-4).  
-- Note that mappings are internally sorted by sample index, so don't rely on  
-- that the mapping gets appended to the 'sample_mappings' property.  
-- Manipulating sample lists (removing swapping) will automatically update  
-- existing mappings. Sample mappings of sliced samples are read-only: can   
-- not be modified. Test for `#samples[1].slice_markers > 1` to find out if the   
-- instrument is sliced.  
renoise.song().instruments[]:insert_sample_mapping(  
 layer, sample_index [, base_note] [, note_range] [, vel_range])  
 -> [new renoise.SampleMapping object]   

Have it!!

for i=1, unison do  
renoise.song().selected_instrument:insert_sample_mapping(1, i)  
end  

:D

If we could at first learn how to copy note-on sample keyzones to note-off sample keyzones we’d have a good start.
Then it’d just be a case of:
Load sample, define c1-c9 as keynote
create empty sample_mapping and copy data from sample_mapping#1 to sample_mapping#2 inside the table…

Getting a Gui Button or a valuebox to talk to the script via providing numbers (and reading current numbers) would be a major breakthrough for my gui skills at least. It’d be good to get this out of one’s system.

I will air an opinion of a person like me, one who knows nothing about code and is kind of afraid of it: The Gui Example Tool is not informative and not geared to set stuff up. I might think completely differently if I re-read through it, but there is just something about the layout that just makes sure you don’t get it.

The Renoise LUA GUI is a hurdle I encounter everytime I try to do anything, and I just know I’m not alone, I’ve encountered at least four people who are as confused (or more, if possible) as me with it.

Thing is now we can create blank sample spaces and create the mappings we need to be able to Clear the whole instrument first, while copy and pasting the sample back into it, otherwise you run it a few times and you get many, many mappings (overflowing) of the same samples quite quickly…

And don’t talk to me about GUI at the moment!

OK here is version 0.1 of the Tool.

Currently only Regenerate mode works, no New (Instrument) mode, although the menu entries are there.

Currently no GUI at all!

Notes to use:
Load a sample (single waveform being most obvious)
Add empty sample slots by click the +Plus sign in the Sample List.
Make sure the actual Sample is selected.
Right click either Sample List or Instrument Box
Select Unison Generator -> Regenerate.

Give me some feedback ;)

To come (slowly):
New mode, to create new Instrument.
GUI to set amount of Unison voice and ranges for the distribution.
(Maybe something a little more real random rather than trig function.)

But for now that will do, especially as I have no 'net at home and reading up on things with only the API documentation is slow!

Hi, downloaded your script and made some alterations. Here’s the code and the descriptions:

[details=“Click to view contents”] ```
–[[===================================================================================
Unison Generator

Author: Dale Powell (Kazakore) <dj_kaza>

Version: 0.1

===================================================================================]]–



– Variables



panmax = 0.8

panmin = 0.2

tunemax = 0.3

tunesmin = 0.6

volmin = 0.4

volmax = 0.9



local rns = renoise.song()

local t = renoise.tool()



–[[ Locals within Function

local rns = renoise.song()

local t = renoise.tool()

local ins = rns.selected_instrument_index

local samp = rns.selected_sample_index

local unison = #rns.instruments[ins].samples ]]–



–[[Create New Instrument (at current +1) -COME BACK TO THIS LATER

rns.insert_instrument_at(rns.instrument[ins+1])

–]]



t:add_menu_entry{

name = “Sample List:Unison Generator Regenerate”,

invoke = function() regen() end}



t:add_menu_entry{

name = “Instrument Box:Unison Generate, Ready”,

invoke = function() regen()

regen()

renoise.song().selected_instrument.name=renoise.song().selected_instrument.name … “Unison_loaded”

for i=2,8 do renoise.song().selected_instrument.samples[i].volume=0.9

–renoise.song().selected_instrument.samples[i].auto_fade=true

end





end}



function regen()

local ins = rns.selected_instrument_index

local samp = rns.selected_sample_index

local unison = #rns.instruments[ins].samples

local slices = #renoise.song().selected_instrument.sample_mappings[1]

local si=renoise.song().selected_instrument

local ss=renoise.song().selected_sample

local ssi=renoise.song().selected_sample_index



if (table.count(renoise.song().selected_instrument.samples)) >1 then renoise.app():show_status(“Panic!”)

else

ssi=1

for i=2,8 do

si:insert_sample_at(i)

end

end

for i=1, #renoise.song().selected_instrument.sample_mappings[1] do

rns.instruments[ins]:delete_sample_mapping_at(1, (slices+1-i))

end



for i=1 , unison do

rns.instruments[ins].samples[i]:copy_from(rns.instruments[ins].samples[samp])

– Add keyzone for each sample

rns.instruments[ins]:insert_sample_mapping(1, i)-- else end

– Add unique Pan to each Sample.

rns.instruments[ins].samples[i].panning = (math.cos(i * os.clock(i))/2 +0.5) --How can I find random without internet!

– Add Volume to Each

rns.instruments[ins].samples[i].volume = (math.sin(i * os.clock(i)) + 1.2)

– Add Finetune to each.

rns.instruments[ins].samples[i].fine_tune = (math.cos(i * os.clock(i)) *127)

end



for i=2,8 do

renoise.song().selected_instrument.samples[i].name=“Unison” … i

end



end

function diskbrowserload()

local MYFILE = nil

MYFILE = renoise.app():prompt_for_filename_to_read({“wav”}, “Pick Your Filename”)



if ( MYFILE == “” ) then return false

else

print (MYFILE)

renoise.song().selected_sample.sample_buffer:load_from(MYFILE)

renoise.app():show_status("Sample Loaded: " … MYFILE)

end

regen()

regen()

for i=2,8 do renoise.song().selected_instrument.samples[i].volume=0.9

end

end


```

[/details]



1) Instrument Box now has "Unison Generate, Ready" in the rightmousebutton context menu. The selected instrument will receive 8 new sampleslots, the currently selected sample will be copied to all 8 new sampleslots, and they will be renamed accordingly (Unison2->Unison8). The detuning, pan and vol is unfortunately done twice, but that can be tweaked by someone who reorders how this script currently works. Also this script tries to lower the volume of the sample setting, so that it does not automatically mess up. Also, on load, the "current_instrument_name" becomes "current_instrument_nameUnison_loaded".
2) But what if you decide to cut one sample up, and would like the unison be generated out of that? Right-mouse-button on Instrument Settings -> Sample Box -> Unison Generator Regenerate. What happens now is the currently selected sample gets copied to all 8 slots.

currently this will do for me (there's a commented away autofade=true (in preparation for 2.8 API updates?) for each sample, so the click at the start will be eliminated if you're cutting stuff up destructively.

[quote=“esaruoho, post:18, topic:31924”]
Hi, downloaded your script and made some alterations. Here’s the code and the descriptions:

[details=“Click to view contents”] ```
–[[===================================================================================
Unison Generator

Author: Dale Powell (Kazakore) <dj_kaza>

Version: 0.1

===================================================================================]]–



– Variables



panmax = 0.8

panmin = 0.2

tunemax = 0.3

tunesmin = 0.6

volmin = 0.4

volmax = 0.9



local rns = renoise.song()

local t = renoise.tool()



–[[ Locals within Function

local rns = renoise.song()

local t = renoise.tool()

local ins = rns.selected_instrument_index

local samp = rns.selected_sample_index

local unison = #rns.instruments[ins].samples ]]–



–[[Create New Instrument (at current +1) -COME BACK TO THIS LATER

rns.insert_instrument_at(rns.instrument[ins+1])

–]]



t:add_menu_entry{

name = “Sample List:Unison Generator Regenerate”,

invoke = function() regen() end}



t:add_menu_entry{

name = “Instrument Box:Unison Generate, Ready”,

invoke = function() regen()

regen()

renoise.song().selected_instrument.name=renoise.song().selected_instrument.name … “Unison_loaded”

for i=2,8 do renoise.song().selected_instrument.samples[i].volume=0.9

–renoise.song().selected_instrument.samples[i].auto_fade=true

end





end}



function regen()

local ins = rns.selected_instrument_index

local samp = rns.selected_sample_index

local unison = #rns.instruments[ins].samples

local slices = #renoise.song().selected_instrument.sample_mappings[1]

local si=renoise.song().selected_instrument

local ss=renoise.song().selected_sample

local ssi=renoise.song().selected_sample_index



if (table.count(renoise.song().selected_instrument.samples)) >1 then renoise.app():show_status(“Panic!”)

else

ssi=1

for i=2,8 do

si:insert_sample_at(i)

end

end

for i=1, #renoise.song().selected_instrument.sample_mappings[1] do

rns.instruments[ins]:delete_sample_mapping_at(1, (slices+1-i))

end



for i=1 , unison do

rns.instruments[ins].samples[i]:copy_from(rns.instruments[ins].samples[samp])

– Add keyzone for each sample

rns.instruments[ins]:insert_sample_mapping(1, i)-- else end

– Add unique Pan to each Sample.

rns.instruments[ins].samples[i].panning = (math.cos(i * os.clock(i))/2 +0.5) --How can I find random without internet!

– Add Volume to Each

rns.instruments[ins].samples[i].volume = (math.sin(i * os.clock(i)) + 1.2)

– Add Finetune to each.

rns.instruments[ins].samples[i].fine_tune = (math.cos(i * os.clock(i)) *127)

end



for i=2,8 do

renoise.song().selected_instrument.samples[i].name=“Unison” … i

end



end

function diskbrowserload()

local MYFILE = nil

MYFILE = renoise.app():prompt_for_filename_to_read({“wav”}, “Pick Your Filename”)



if ( MYFILE == “” ) then return false

else

print (MYFILE)

renoise.song().selected_sample.sample_buffer:load_from(MYFILE)

renoise.app():show_status("Sample Loaded: " … MYFILE)

end

regen()

regen()

for i=2,8 do renoise.song().selected_instrument.samples[i].volume=0.9

end

end


```

[/details]
[/quote]



Haven’t actually looked into your code itself yet.




[quote]
1) Instrument Box now has “Unison Generate, Ready” in the rightmousebutton context menu. The selected instrument will receive 8 new sampleslots, the currently selected sample will be copied to all 8 new sampleslots, and they will be renamed accordingly (Unison2->Unison8). The detuning, pan and vol is unfortunately done twice, but that can be tweaked by someone who reorders how this script currently works. Also this script tries to lower the volume of the sample setting, so that it does not automatically mess up. Also, on load, the “current_instrument_name” becomes “current_instrument_nameUnison_loaded”.
[/quote]



Yeah sorry been lazy with the renaming thing. The Unison Generator:New I would going to create a new Instrument and make one with a fixed 8 slots (for now) out of the selected sample of the current instrument, so not too different from your Ready option really… I know the volume is going to be far too high but also number of voices (samples) is always going to make quite a big difference to this too.


[quote]
2) But what if you decide to cut one sample up, and would like the unison be generated out of that? Right-mouse-button on Instrument Settings -> Sample Box -> Unison Generator Regenerate. What happens now is the currently selected sample gets copied to all 8 slots.
[/quote]



You mean keeping all the loaded sample as is and just redoing the “random” values? I did think about that as well but wasn’t sure what to call it or what I thought the best cause of action was with blank spaces in the sample list (whether to ignore them or try filling them on some round robin type method.) But yeah I do see there being a want for this too, so you could load a sine, square and triangle, give yourself 9 sample slots total and get it to distribute it so each waveform is randomly adjusted three times. But as it uses different waveforms it’s thus not true Unison so again put to back-burner to think about for a while.





Thanks for adding Renoise and Tool Version numbers to the file. Realised just as I uploaded it that I’d forgotten :)







PS. Those variables at the beginning currently do sweet FA! Don’t think separate Min/Max is going to be the way to go, but rather a single Spread value (with a possible Offset one if I can get it to still remain within the limits without driving myself crazy!)</dj_kaza>

Well, the effect the script has on the volume could easily be made to fit the amount of sampleslots created by the script. Things would stay in nice balance. Can be done.

No, absolutely not. What I meant was what if one sample becomes the “lead sample you want to make the unison out of”. Say, you load a drumloop in, and create an unison out of that. Then you edit one to be just a hihat, then you just re-created the unison instrument from the currently_selected_sample (which will be edited to for instance have a specific sound you want to create an unison out of).
This was a different feature.

Bantai’s (?) export xrnx tool does all that automatically.

Yeah. I think the pan, vol, tuning could be altered quite a bit in this script so everything’s not so “max right, max left”. It’s sweet tho, I’m finding that really odd samples end up being really cool samples with the unison generator.
Let’s progress with this, but since its your puppy, I won’t start hacking in a gui or anything like that.
(You seemed to get good advice from Bantai for that bpm -amount-changer-GUI so maybe you’ll be able to hack this further…)
(What I’m trying to say is that this now does what I want it to and I’ll keep using this unless if you introduce some cool new features or functionalities)

Whole thing with Randomness is you can never guarantee this. When first playing with it I went through a load and they were quite nice and moving then randomly hit one with all very similar values and so much louder and stronger than before.

Although did think after my last posting I might try a -(minus)number_of_samples / 10 to the Volume to bring it a little lower (although this risks pushing it out of range at times, although maybe I should add for if/then safety checks) to the Volume value which is strangely from 0-4 (not 0-1)

If I understand that is what it currently does in Regenerate mode. You can load/edit a new sample and hit Regenerate and it will use the current sample to create the Unison. New mode will generate a new Instrument from that sample (probably with a fixed 8 layers on first outing.)

Been meaning to install and test that as renaming and then installing the zips is a pain! In fact been meaning to check if I can just edit the version actually loaded within Renoise but am currently doing it the most long-winded way possible!

Will have to look into math functions, or something, to try and get the spreads a bit different. Sine and Cosine are always going to be weighted at the edges, the opposite to a Bell Curve (which would be more friendly) although for the 0-1 range that can be cured with a 1-x but the math side will need revision and current methods are just a stop-gap solution to get some kind of automatic distribution.

Only tried with single cycles and breaks so far. Some good results with both. Single cycles often get that wavery depth (although forced to play them in the higher registers) and after multiple clickings you can get some good effects on breaks, where it sounds like the players are slipping out of time and slowing down, especially if mixed with some pitch commands ;)

Anybody is welcome to try and chuck in a hand. When I’m a bit more happy with it I’ll give it its own Tool thread (and publish to the site) but for now I’ll leave it in the Ideas one ;)

I don’t understand the second sentence. I have the scripting terminal open at all times, save changes and re-run the tool from the context menu. Did I misunderstand something?

But User Tools are not Installed Tools. If you have anything that uses renoise.tool() it will not run from the Terminal but instead throw up an error that its not defined.

Hence to test full tools, rather than small chunks, (especially when it comes to testing GUI stuff!) I’ve been having to create the Tool, drag and drop it onto Renoise to reinstall the edited version and give it a go again.

Surely there is an easier way, no?

Use the Bantai “Create Tool” to create a tool, load the main.lua in, edit it, save it, and run the script. All within Renoise.

I cannot stress how important it is to use [u][i][b]Bantai’s Create & Export Tool scripts[/b][/i][/u]!!!