Hello everyone,
I would like to ask for support for a synth on which I am having a little trouble.
My goal is to design a synthesiser that can play consecutive monophonic percussive sounds, randomly selected from a collection of pre-loaded buffers provided as an argument.
Collaterally I would like each of these samples to have independent amplitude, rate and envelope.
To achieve this, I am using PulseDivider
as a starting point (taking inspiration from what I learned here).
In addition, this case study also involves a topic that I’ve always struggled a bit to keep in mind, perhaps because I don’t use it that often, namely the passing of lists as arguments to a synth (which can be solved with a NamedControl
anyway, see also this discussion on the subject).
In the end, I managed to write the following code for the synth
(
SynthDef(\test, {
|out=0,level=1.0, pan=0.0, pulseFreq=4,
atk=0.0, rel=0.1|
var number_of_copies = 10;
var bufs = \buf.kr(999!6);
var trig = Impulse.ar( pulseFreq );
var trigs = PulseDivider.ar(trig, number_of_copies, (0..(number_of_copies-1)));
var sig = DC.ar(0.0)!number_of_copies;
sig = sig.collect({
|channel, i|
var buf = Demand.ar(trigs[i], 1, Drand(bufs, inf) );
var rate = Demand.ar(trigs[i], 1, Dwhite(1.0, 2.0, inf) );
var amp = Demand.ar(trigs[i], 1, Dwhite(0.15, 0.35, inf) );
var phase = Phasor.ar(trigs[i], BufRateScale.kr(buf)*rate, start:0.0, end:BufFrames.kr(buf)*100);
var env = EnvGen.ar(Env.perc(atk, rel), trigs[i] );
BufRd.ar(1, buf, phase, loop:0) * env * amp ;
});
sig = sig * level;
sig = Splay.ar(sig);
Out.ar(out, sig);
}).add;
);
What I don’t understand is why, upon instantiation of the synth,
~my_samples_list; // <-- load some monophonic sample inside this list.
(
x = Synth(\test, [
\buf, ~my_samples_list,
\amp, 1.0,
\pulseFreq, 6,
\atk, 0.0,
\rel, 1
]);
)
I get these messages in the post window, although afterwards, the synth operation seems correct.
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
Buffer UGen channel mismatch: expected 1, yet buffer has 3 channels
How can I correct this behaviour?
what is wrong with my implementation that could be better rewritten/refactored?
Instead, here I would like to go into the details of the implementation a bit below:
-
I’m using the namedControl
\buf
to allow the synth to accept a list of values as input (in our case the bufnumbers of the buffers we want to use). I don’t know if what I’m about to say makes sense or not, but what I think I’ve implemented here is the initialisation of this list to6
copies of the number999
(which I would assume to be a non-existent bufnum and which will then be immediately replaced when the synth is actually instantiated). -
does this number of copies used in the namedControl have to exactly correspond to the amount of elements inside the list I will pass as an argument?
Let me explain: if I use!6
in the namedControl, can I then pass a list with a different size? -
the
number_of_copies
is a variable I use internally in the synth to change the structure of its UGen graph, in particular to make more or less “divisions” of the impulse on different “channels”.
This value is intended to be fine tuned according to the sound you want to obtain. This mainly depends on the length of the samples you want the synth to playback. If those samples are relatively short, you can use a relatively small number here. -
my intention is not to rely too much on the inherent multichannel expansion that a certain type of code syntax would entail. Rather, I prefer to write things more clearly (at least for me) and make explicit how each audio channel the synth is generating works.
That is why I use this function:
sig = sig.collect({
|channel, i|
// code using index 'i' for its main logic
});
What I am wondering here is whether there is a more stringent syntax to achieve the same kind of result, especially with regard to the line just before
var sig = DC.ar(0.0)!number_of_copies;
where, in my mind, a prior creation of a multi-channel dummy signal with a corresponding number of channels was necessary.
Perhaps there is a more compact way to rewrite this section?
- in the line
var phase = Phasor.ar(trigs[i], BufRateScale.kr(buf)*rate, start:0.0, end:BufFrames.kr(buf)*100);
I’m using an (exaggerated) *100
multiplication for the end
parameter of the Phasor
in order to allow the ramp to increase its value well beyond the duration in frames of the sample I want to play, so I won’t have the sample to loop.
It will start playing again when the new trigger on its own channel occurs.
Please let me know if anything is unclear.
Thank you very much for your support