Idea: Unison Generator

Hi. Now that you can have multiple samples overlapping, the obvious question (at least to me) is this:

Could a LUA Script be made which, for instance, takes the sinewave in your instrument-slot, and creates 7 additional overlapping KeyZones (i.e. Original sinewave was c1-c8, 7 additional sinewaves will be c1-c8) and then allow a slider per additional sample to define the amount of detune.

This would allow for some serious doubling up and “fattening” of sounds, by the same method as, for instance, Absynth4 allows for Unison usage.

All it would really need to ask is: How many additional overlapping copies of the same sample you want - and how much detune per additional overlapping copy.

+1 for ultimate trance saw.

Can you not detune individual samples alraeady? Although neither truely free running, as all start in sync, and no Osc Sync mode, surely this gives what you describe.

Or do you mean Osc Sync, where detuned waves are retriggered at each cycle of the Base Note (OSC1)?

Actually I think I get what you mean. Unison usually detunes the same, entire ensamble but unlike Chrous it isn’t modulated, although may possible use a different detune amount per note triggered. Is that more what you mean?

I was thinking of just having an easy tool that would, for instance, enhance the usage of “Custom Wave Synth”, or enhance the usage of onecycle chipsounds, so that instead of running a DSP-effect on it, one could for instance have 8 same copies of the same Sample and have all of them have different detune settings. Of course this can be done by hand, as can most things, but in this case, it would just be a useful tool to have.

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)