I am trying to create a harpsichord synth in which the strings for each note would be hit at small onset different times (50-90ms) also with a small detuning between each string freq * {rrand(1-percentDetune, 1+percentDetune)}.dup(4) .
I manage to have a global detune for each harpsichord key as well as a global difference in the attack onset. How would it be possible to have a unique detuning for each harpsichord key (freq) as well as unique onset difference for each key?
Any other considerations about how to improve the synthesis model of a harpsichord?
Hi John,
you can do everything you have described directly inside a pattern!
(
s.waitForBoot {
SynthDef(\harpsi, {
var env = Env.perc(\atk.kr(0.01), \rel.kr(1.0)) .ar(2, \gate.kr(1));
var sig = Pulse.ar(\freq.kr(220));
var out = sig * env * \amp.kr(0.2);
OffsetOut.ar(\out.kr(0), out!2)
}).add;
s.sync;
Pbind(
\instrument, \harpsi,
\amp, 0.25,
\degree, Pn(Pseries(0, 1, 8), inf),
\dur, 1,
\mtranspose, [0, 0.013, -0.011, 0.023, -0.032], // detune in cents
\strum, 0.005 // time between each detuned freq - make it big to see the effect
).play;
}
)
If you want to change the \mtranspose or \strum for each note, you could use a Pfunc or Pwhite, like this:
This, isnāt a āa global detune for each harpsichord keyā, but a global detune for all harpsichords as the randomness is only evaluated when the synthdef is defined.
If the values in question are randomized, a fun way to do this entirely within a SynthDef is to use Hasher. Hasher.kr(freq) will give you a value between -1 and 1 that is ārandomā but produces the same result for the same frequency. I do this a lot to add consistent imperfections to mallet and keyboard instruments.
From my little knowledge on patterns, I think that you canāt pass an array for the key \strum because it wonāt multichannel expand, right? Or is there another built-in key to treat onsets like initial phase and not as a global different time between strummed notes?
Regarding the Hasher, is it possible to multichannel expand twice? For instance, here are four different detuned versions of a Pulse.
Hasher will always produce the same result for the same input. To turn it into a full-on seeded random number generator, I iterate Hasher by defining a little function like this:
var state, random;
state = \seed.kr(0);
random = { state = Hasher.kr(state); state };
Now every call to random.() will produce a different random value from -1 to 1, and the sequence will be reproducible but re-randomized for each distinct seed (to which you may supply freq or anything else).
When Iām lazy I do stuff like Hasher.kr(freq + [0, 1, 2, 3]) to get four channels of random values, but be aware that freq and freq + 1 will have values in common, so itās not completely decorrelated from the input.
Thereās also RandSeed but Iāve never gotten it to work. I think all UGens that use randomness draw from a common RGen and RandSeed affects the state of all random ugens, so it will stop reproducing the same results in polyphony or something like that. If I were rewriting the random UGens I would give each one an isolated prng with an optional pseudorandom seed.