I’m not entirely clear on your goal, but let me rephrase what I think you’re looking for, and then propose a relatively simply place to start. If I got it a little wrong, sorry - talk about this stuff is difficult!
- There are individual, separate sounds (Synths) played back-to-back
- There are short crossfades between these sounds
- There is a long envelope controlling the amplitude of the WHOLE THING.
Here’s a prototype:
- Here’s the back-to-back, which I think you’ve already figured out (this assumes you have some synth named
\sound
).
Task({
10.do {
~duration = rrand(1, 4);
Synth(\sound, [\bus, 0, \duration, ~duration, \freq, rrand(100, 400)]);
~duration.wait;
};
}).play
- Here’s a simple crossfading synth:
SynthDef(\sound, {
|bus, duration, freq|
var sig, fadeEnv;
// A short 0.1 fade in, and then hold at 1 for the duration. Use doneAction to free automatically
fadeEnv = Env([0, 1, 1], [0.1, duration]).kr(doneAction:Done.freeSelf);
// The sound
sig = SinOsc.ar(freq);
// Now, we want to use fadeEnv to fade in our new sound,
// replacing whatever was playing back before
XOut.ar(bus, fadeEnv, sig);
}).add;
We need to modify our Task slightly: we want new synths nodes to be AFTER the previous ones, because they’ll be overwriting the sound they made (for the crossfade) - so we to change to:
~synth = Synth(\sound,
[\bus, 0, \duration, ~duration, \freq, rrand(100, 400)],
target:~synth, addAction:\addAfter
);
- Here’s our overall envelope synth - we want to read in the sounds from our
\sounds
, apply the envelope, and write them back out.
SynthDef(\envelope, {
|bus|
var sig, env;
// Some random envelope...
env = Env([0, 1, 0.4, 0.6, 1, 0], [4, 1, 3, 8, 4]).kr.poll;
sig = In.ar(bus, 1);
sig = env * sig;
// We need to use ReplaceOut because we want to REPLACE the sound
// that was already with our version that is modulated by the envelope
ReplaceOut.ar(bus, sig);
}).add;
Finally, we need the envelope to come AFTER all of our sounds in the signal chain, because it’s modifying them - the easiest way to do this is to put our \sound
s in a Group
, and then put the envelope synth after that group. After this, our task looks like:
Task({
~bus = 0;
~synthGroup = Group();
~envelopeSynth = Synth(\envelope,
[\bus, ~bus],
target: ~synthGroup, addAction: \addAfter
);
10.do {
~duration = rrand(1, 4);
~synth = Synth(\sound,
[\bus, ~bus, \duration, ~duration, \freq, rrand(100, 400)],
target:~synthGroup, addAction:\addToTail // add new sounds to the end of our group instead....
);
~duration.wait;
};
~envelopeSynth.free;
}).play
I think you can probably image now how you could do the above, but use a different synth for each \sound
iteration - you’d still want to do the crossfade part for each of the Synths. It would also be straightforward to e.g. add some additional effects or modulation to your \envelope
synth, which would be applied to all your synths. You’ll need to do a little more work to e.g. make sure your envelope matches the overall duration of your pieces, and of course if you want to do anything more complex there will be problems to solve :).
In the code you originally posted, you’ve got Synth code mixed into Task code etc. - this is a common misunderstanding… It’s best to start from a basis of thinking of SynthDef
s as relatively fixed objects you define ahead of time, and then e.g. run via Synth(\foo, ...)
inside your task. You CAN generate SynthDefs more dynamically, and be a little more fluid with these distinctions - but that is definitely advanced territory… You don’t need it (yet) for what you’re trying to do, and you’ll almost definitely get stuck if you try to push that edge too soon.
Making sure nodes are in the right order is critical - you can’t modify (e.g. crossfade) a sound before you generated it! If you are careful about ordering, however, you can make the synth graph do a lot of work for you - the server node tree can help visualize if you’re not sure whats happening (check the Server menu).
Finally: if solving this problem is a deeper dive into SuperCollider than you are looking for, and you want to just be moving your piece forward, you might consider reading the help files for NodeProxy
and Ndef
- these objects wrap up some of the bookkeeping stuff a little easier, and they can do things like crossfading automatically.