Okay, I spent 45 minutes to try and do a refactor of this code. This represents a bunch of design patterns I use for things like this - this is really not to be taken as advice or direct guidance, but only for potentially sparking some ideas related to organizing this kind of code. Happy to answer questions on any of it.
Super happy to to answer question about any of it. A few of the things happening:
- Slightly more organized server boot and initialization details (this is always my biggest headache)
- Generation of SynthDefs for multiple different input/output configurations.
- A prototype pattern (
Pdef
) to handle the setup for “convolution reverb operating on a bus inside a group”, such that I can just feed it what I want to play and don’t have to worry about allocating buses and stuff. I usually create a prototype Pdef for any complex synth or complex multi-synth setup, so I can set reasonable default values, create required buses etc., so when I re-use them later I don’t have to remember all that setup stuff. If I’m going to re-use something, I try to keep it so that I can run it later on with one line / no parameters - always a nice place to start when revisiting code.
( // INIT
// All of the initialization things in one block
~fftSize = 4096;
~server = Server.default;
~server.options.numOutputBusChannels = 4; //quad out
~server.options.memSize = 2**15;
~server.reboot;
~server.doWhenBooted {
~impulses = (
4.collect{
|i|
Buffer.readChannel(s, "~/Downloads/TownHall-L.wav".standardizePath, channels: [i])
} ++ 4.collect{
|i|
Buffer.readChannel(s, "~/Downloads/TownHall-R.wav".standardizePath, channels: [i])
}
);
~server.sync;
~convBufferSize = PartConv.calcBufSize(~fftSize, ~impulses[0]);
~convBuffers = Buffer.allocConsecutive(8, ~server, ~convBufferSize);
~server.sync;
~convBuffers.do {
|convBuffer, i|
convBuffer.preparePartConv(~impulses[i], ~fftSize)
};
// Lets put these in separate lists of left and right
~convBuffers = ~convBuffers.clump(4);
};
)
( // SYNTHDEFS
SynthDef(\stereoTest, {
var pulse = Impulse.ar(4);
Out.ar(
\out.ir(0) + ToggleFF.ar(pulse).poll,
LFTri.ar(111) * Decay.ar(TDelay.ar(pulse, 0.01), 0.5, 0.1)
);
}).add;
// Roll up my different channel configurations rather than
// manually defining each one.
[
(ins: 1, outs: 1),
(ins: 1, outs: 4),
(ins: 1, outs: 2),
(ins: 2, outs: 2),
(ins: 2, outs: 4),
].do {
|settings|
var ins = settings[\ins];
var outs = settings[\outs];
var name = "convolution_in_%_out_%".format(ins, outs).asSymbol;
SynthDef(name, {
var input, sig;
input = In.ar(\in.kr, ins).asArray;
sig = outs.collect {
|index|
input.collect({
|in, channel|
PartConv.ar(
in,
~fftSize,
~convBuffers[channel][index]
)
}).sum
};
sig = \amp.kr(0.2) * sig;
Out.ar(\out.ir(0), sig);
}).add;
};
// Make a little prototype pattern for playing things back in my reverb.
// What do I need to play this back?
// 1. A bus
// 2. A group
// 3. A running convolution synth
// 4. A pattern to play though it.
//
// Values from Pdef:envir are passed in to the arguments of my Pdef function,
// which in this case is `innerFunction` and an ins / outs count. I can use this like
// a little factory to pass in a pattern from the outside, and assemble all the pieces I need
// to play it back correctly.
// A Ppar because I'm playing two things, a Pmono for my convolution since I want one synth
// playing continuously.
Pdef(\convPlayer, {
|innerPattern, ins, outs|
[innerPattern, ins, outs].postln;
Pproto(
{
~bus = (type: \audioBus, channels: ins).yield;
~group = (type: \group).yield;
},
Ppar([
// My inner pattern - set the right out and addAction.
// ~group is set automatically because of Pproto.
Pbind(
\out, { ~bus },
\addAction, \addToHead
) <> innerPattern,
// My reverb
Pmono(
"convolution_in_%_out_%".format(ins, outs).asSymbol,
\addAction, \addToTail,
\in, { ~bus },
\out, 0
)
])
)
});
);
(
// 2 in, 4 out
Pdef(\convPlayer).envir_((
ins: 2, outs: 4,
innerPattern: Pmono(
\stereoTest
)
));
Pdef(\convPlayer).play;
)
(
// 1 in, 4 out
Pdef(\convPlayer).envir_((
ins: 1, outs: 4,
innerPattern: Pmono(
\stereoTest
)
));
Pdef(\convPlayer).play;
)
(
// 1 in, 2 out
Pdef(\convPlayer).envir_((
ins: 1, outs: 2,
innerPattern: Pmono(
\stereoTest
)
));
Pdef(\convPlayer).play;
)