Hello, if I have a SynthDef with a 30 randomized params, how can I decide to control the first 15 params by a specific RandSeed and the 15 other by another one ? So far, I am just able to use one RandSeed for all the synth.
Thanks a lot
Anyone please ? Impossible de find any examples on the documentation or on the forums. So like:
(
SynthDef(\Rand, {
/// one RandSeed for thoses :
var aaa = rrand(1, 10);
var aaa2 = rrand(10, 20);
/// another RandSeed for thoses :
var bbb = rrand(100, 1000);
var bbb2 = rrand(1000, 2000);
/// but both staying in the same SynthDef
}).add;
)
Why would you want to do this? Randomness is supposed to be uniform, so it makes no difference to the distribution if you change the seed, or get the next value.
The reason why you would want to set a random seed in the first place is so that many synths can have the same random value for each subsequent call to the random number generator. If many synths are sharing the same parameters, then it would be better to generate all the values at once, store them, then initialise the synths with the same values.
Youâre slightly mistaken about how this is working: rrand
is called when you BUILD the SynthDef - this is controlled by thisThread.randSeed
- setting this seed will control the seed value for future calls to rrand
, so you can do something like this:
thisThread.randSeed = 10;
var aaa = rrand(1, 10);
var aaa2 = rrand(10, 20);
thisThread.randSeed = 11;
var bbb = rrand(100, 1000);
var bbb2 = rrand(1000, 2000);
These random values are now hard-coded into your SynthDef
structure - not sure if this is desired or not.
RandSeed
resets the random seed used for UGenâs for a Synth while itâs running. This would apply to things like TRand
and WhiteNoise
. You canât straightforwardly use two RandSeed
UGens in your SynthDef, since UGens arenât necessarily run in the order that you specify them in code, so placing a RandSeed in between your two sets of parameters may not mean much - and in any case, random values are random values, changing the seed in between will just result in more random values :). If you want reproducible ârandomâ values for a group of parameters, Iâve used a pattern like this:
SynthDef(\random, {
var baseSeed, makeRand, seed1, seed1, a, b, c,d;
baseSeed = rrand(0, 1000); // this can be deterministic also....
makeRand = {
|min, max, seed|
baseSeed = baseSeed + 1;
Hasher.ar(baseSeed + (seed * 1000)).linlin(-1, 1, min, max)
};
seed1 = \seed1.kr(10);
seed2 = \seed2.kr(11);
a = makeRand.(0, 100, seed1);
b = makeRand.(0, 400, seed1);
c = makeRand.(0, 10, seed2);
d = makeRand.(-0.3, 0.3, seed2);
});
Hasher produces a (deterministic) random number based on an input value. Your input value is a combination of (1) a synth-global value you increment each time you run the function (so that each makeRand call produces a different value), and (2) a seed value you pass in. With this setup, you can bump a seed to re-randomize ALL the values that use that seed. Your values will be random, but depend on baseSeed
- this can be hard-coded (so the ârandomâ values are the same ever time), or also randomized in whatever controlled way you want.
Thatâs clear, thank you taking your time to clarify this.
Sorry to reopen this, thanks a lot for the explanation.
How would it be possible to run inside a SynthDef two LFNoise0.ar(1)
and LFNoise0.ar(2)
which have different frequencies but outputs the same list of values?
With two SynthDefs I can make this, but I have no idea how to make it within a single SynthDef:
(
SynthDef(\test, {arg seed, freq;
RandSeed.ir(1, seed);
Out.ar(0, SinOsc.ar(LFNoise0.kr(freq).range(440, 880), 0, 0.4));
}).add;
)
a = Synth(\test, [\freq, 3, \seed, 1000]);
b = Synth(\test, [\freq, 2, \seed, 1000]);
But setting the RandSeed only makes each instance static, but not all of them equal:
{RandSeed.ir(1, 500);[LFNoise0.ar(500),LFNoise0.ar(1000), LFNoise0.ar(2000), LFNoise0.ar(4000)]}.plot
Moreover, how would it be possible to run inside a SynthDef
two LFNoise0.ar(1)
and LFNoise0.ar(2)
which have different frequencies but outputs the same list of values alongside with other two LFNoise0.ar(3)
and LFNoise0.ar(4)
which have different frequencies and also different list of values (different RandSeed
for each group of oscillators)?
as far as I can see, this is currently not possible:
(
{
var a, b;
RandID.ir(0);
RandSeed.ir(0,1789);
a = LFNoise1.ar(2000);
RandID.ir(1);
RandSeed.ir(0,1789);
b = LFNoise1.ar(2000);
[a, b]
}.plot
)
This doesnât produce the same form twice, I think because the rand id is set only once and not at each cycle. This was one of my first projects in supercollider, and I didnât think of that (if I remember correctly).
Would it be interesting to have a variant of LFNoise0 that has a seed (maybe as a parameter) that cold be reused/shared?
I also came across other pseudo-random algorithms, that claim to be more efficient and more ârandomâ, focused on the opposite, splitting generators. (Not sure any of this would be useful for sc)
@hobbes Lazy as i am. I would use something like CuspN or for this.
edit: or pick one of these ChaosGen.subclasses
and try to find some initial values that sounds interresting.
Perlin noise can be a good candidate for very controllable and deterministic noise.
https://doc.sccode.org/Classes/Perlin3.html
Though iirc there are some settings when generating perlin noise that are not exposed here, so it may have limited flexibility?
I second that.
But it also reminded me of colleagues who recorded pink noise from a beautiful analog generator, so they could work with âtruly random noise.â Iâm not sure it did make any difference, but, as soon as you recorded it, is it still random or just a sequence of numbers in your computer?
It would be cool to have this as a new feature, if possible!
Cunning solution, I will definitively try this out!
This implementation of Perlin noise have some bugs, right? Iâve tried to explore it but it shows some really strange behavior like triangular waveforms and impulse trainsâŚ
Please elaborate further! If you can post some code example of this it would be nice as well!
Iâve checked the sources, and in principle, it would work if we call the RandID
for every block:
(
{
var a, b;
RandID.kr(0);
RandSeed.ir(0,1789);
a = LFNoise1.kr(200);
RandID.kr(1);
RandSeed.ir(0,1789);
b = LFNoise1.kr(200);
[a, b] // should be the same
}.plot(0.5)
)
The problem seems to be in the setting of the RandID:
But this would have to be checked. I canât do this myself at the moment.
Thanks for the help!! Unfortunately, even when the RandSeed
is the same, the results are not equalâŚ
(
{
var a, b;
RandID.kr(0);
RandSeed.ir(0,1789);
a = LFNoise1.kr(200);
RandID.kr(1);
RandSeed.ir(0,1789);
b = LFNoise1.kr(200);
[a, b] // a!=b
}.plot(0.5)
)
(
{
var a, b;
RandID.kr(0);
RandSeed.ir(0,1789);
a = LFNoise1.kr(200);
RandID.kr(0);
RandSeed.ir(0,1789);
b = LFNoise1.kr(200);
[a, b] // a!=b
}.plot(0.5)
)
All makes sense to me, I agree. In the case of Gendys, a good splittable generator, providing several parallel deterministic high-quality generators would be be optimal option. We canât do without it, nathan, someone has to fix it.
Well, you avoid threatening seeds. Thatâs exactly the point, statistically itâs as good as independent generators (which is not the case with other algorithms)
Yes, exactly this was my point â it doesnât work, but should.
I think Iâve found the problem.
void RandID_next(RandID* unit, int inNumSamples) {
float id = ZIN0(0);
if (id != unit->m_id) {
unit->m_id = id;
uint32 iid = (uint32)id;
if (iid < unit->mWorld->mNumRGens) {
unit->mParent->mRGen = unit->mWorld->mRGen + iid;
}
}
ZOUT0(0) = 0.f;
}
should be
void RandID_next(RandID* unit, int inNumSamples) {
float id = ZIN0(0);
if (id != unit->m_id) {
unit->m_id = id;
}
if (iid < unit->mWorld->mNumRGens) {
uint32 iid = (uint32)id;
unit->mParent->mRGen = unit->mWorld->mRGen + iid;
}
ZOUT0(0) = 0.f;
}
I just canât test it right now.
It would be nice to have methods for Hasher
that impement these different behaviours.
Indeed, itâs necessary to move the uint32 iid
part outside of the if (id != unit->m_id)
â although youâd have to reverse the order of if (iid < unit->mWorld->mNumRGens)
and uint32 iid = (uint32)id;
because the posted version performs a <
check on an uninitialized variable.
void RandID_next(RandID* unit, int inNumSamples) {
float id = ZIN0(0);
if (id != unit->m_id) {
unit->m_id = id;
};
uint32 iid = (uint32)id;
if (iid < unit->mWorld->mNumRGens) {
unit->mParent->mRGen = unit->mWorld->mRGen + iid;
}
ZOUT0(0) = 0.f;
}
This would raise CPU usage for every synth using RandID.kr â currently, unit->mParent->mRGen
is set only when the ID changes, but after a change such as this, it will be set in every control block, whether changing or not. So it might be nice to add a second input, a flag, determining whether to fire every time or not, such as bool fire = ZIN0(1) > 0
and, in the id !=
block, set fire = true
. Then if(fire && (iid < unit->mWorld->mNumRGens))
. A positive input would be true in every block; a nonpositive input would initially be false, but a change in ID would override that. Default 0 for compatibility with current behavior.
Itâs also necessary to send a nonzero trigger to RandSeed. The examples are using trig = 0, so RandSeed never fires, and the seeds are ignored.
With some debugging output, then I get:
init rand id 0 with seed 1789
got 0.758146 from rand id 0
init rand id 1 with seed 1789
got 0.758146 from rand id 1 -- same
got 0.524698 from rand id 0
got 0.524698 from rand id 1 -- same
Diffs:
diff --git a/server/plugins/NoiseUGens.cpp b/server/plugins/NoiseUGens.cpp
index 7bbe84643..50c581d44 100644
--- a/server/plugins/NoiseUGens.cpp
+++ b/server/plugins/NoiseUGens.cpp
@@ -790,13 +790,15 @@ void RandID_Ctor(RandID* unit) {
void RandID_next(RandID* unit, int inNumSamples) {
float id = ZIN0(0);
+ bool fire = ZIN0(1) > 0;
if (id != unit->m_id) {
unit->m_id = id;
- uint32 iid = (uint32)id;
- if (iid < unit->mWorld->mNumRGens) {
- unit->mParent->mRGen = unit->mWorld->mRGen + iid;
- }
+ fire = true;
+ };
+ uint32 iid = (uint32)id;
+ if (fire && (iid < unit->mWorld->mNumRGens)) {
+ unit->mParent->mRGen = unit->mWorld->mRGen + iid;
}
ZOUT0(0) = 0.f;
}
diff --git a/SCClassLibrary/Common/Audio/Noise.sc b/SCClassLibrary/Common/Audio/Noise.sc
index ca001d258..ab37e62bf 100644
--- a/SCClassLibrary/Common/Audio/Noise.sc
+++ b/SCClassLibrary/Common/Audio/Noise.sc
@@ -33,12 +33,12 @@ RandSeed : WidthFirstUGen {
RandID : WidthFirstUGen {
// choose which random number generator to use for this synth .
- *kr { arg id=0;
- this.multiNew('control', id)
+ *kr { arg id = 0, alwaysUpdate = 0;
+ this.multiNew('control', id, alwaysUpdate)
^0.0 // RandID has no output
}
- *ir { arg id=0;
- this.multiNew('scalar', id)
+ *ir { arg id = 0, alwaysUpdate = 0;
+ this.multiNew('scalar', id, alwaysUpdate)
^0.0 // RandID has no output
}
}
hjh
Very good! Can you make a pull request for it?