Recording separate orbits inside SuperCollider

I think it would be very useful to be able to record all orbits separately directly inside SuperCollider, without having to do a routing to a DAW. Currently I am using soundflower and reaper and it works fine but I am seeing around that other people are having trouble finding substitutes on other OS and also I think it would be more efficient to do all the work in a single environment.
Talking about this with @torotumbo and @ritchse we ended up with this piece of code provided by @ritchse. I did a few corrections but despite those it still doesn’t work on my machine (I am on macOS High Sierra). Actually everything seems fine but when I evaluate the Stop recording block I get this:
WARNING: Not recording
for each orbit.

https://github.com/fracnesco/SeparateOrbitRecording/blob/master/WIP

(
// configure the sound server: here you could add hardware specific options
// see [http://doc.sccode.org/Classes/ServerOptions.html](http://doc.sccode.org/Classes/ServerOptions.html?fbclid=IwAR1RerxBYrBVCluqT9sXFP4rRIxQglsotz4brg641TNHplTsBfjeRsegcO8)
s.options.numBuffers = 1024 * 256; // increase this if you need to load more samples
s.options.memSize = 8192 * 32; // increase this if you get "alloc failed" messages
s.options.maxNodes = 1024 * 32; // increase this if you are getting drop outs and the message "too many nodes"
s.options.numOutputBusChannels = 2; // set this to your hardware output channel size, if necessary
s.options.numInputBusChannels = 2; // set this to your hardware output channel size, if necessary

// boot the server and start SuperDirt
s.waitForBoot {
~dirt = SuperDirt(2, s); // two output channels, increase if you want to pan across more channels
~dirt.loadSoundFiles;
s.sync;
~orbs = [Bus.new()] ++ ({Bus.audio(s,2)}!11);
~dirt.start(57120, ~orbs);
//~amountOfOrbitsToRecord = 12;

// start listening on port 57120, create two busses each sending audio to channel 0
};

(
~orbs.do({
arg item, i;
if(i != 0,{~orbs[i].play();});
});
); // This code sends all orbits to your speakers. More on this at the end of the file.
~amountOfOrbitsToRecord = 12;
~rec = {Recorder.new(s)}!12;

s.latency = 0.5; // increase this if you get "late" messages
)

( // Start recording
var amountOfOrbitsToRecord = 12;
~orbs.do({
arg item, i;
if(i < amountOfOrbitsToRecord)},{
~rec[i].record(bus: ~orbs[i], numsChannels: 2,
path: "Users/admin/Music/SuperCollider Recordings/Name"++i++".aiff");
});
)

( // Stop recording
~rec.do({
arg item, i;
~rec[i].stopRecording;
});
)

// I highly recommend doing a test tone when you start recording, so synchronization gets easier.Maybe try:

{SinOsc.ar(440!2, mul: 0.4)}.play //and Ctrl+.

/*
How this works: Every orbit gets send to a different Bus. In SC the first to buses are just L+R to the speakers, but if you make new ones, those are purely logical, they are not listenable unless you explicitly send them to the main buses. That's why there's some code to make all orbits sound off your speakers. So, basically: Use orbits from 1 to 11. Do not use the '0' orbit to avoid trouble
*/
1 Like

In the past I have done:

s.recChannels = 16;
s.prepareForRecord;
s.record;
...
s.stopRecording

and gave me 16 mono channels because I had 8 orbits configured.
The output in an .aiff with thouse 16 mono channels inside

Hope this helps.

Bests,
Rapo

Hi! Talking with @torotumbo I ended up writing this for recording SuperDirt + physical audio inputs… not sure about globalEffects though, but I think it’s flexible enough to meet different SuperDirt setups

(
/*
SoundIn recorder
*/
var numInputs = 2; //number of inputs
var numChannels = 1; //channels per input (mono)

~soundInBus = Array.fill(numInputs, { Bus.audio(s, numChannels); });

{Out.ar(~soundInBus[0], SoundIn.ar(0))}.play; //hardcoded routing SoundIn -> ~soundInBus
{Out.ar(~soundInBus[1], SoundIn.ar(1))}.play; //hardcoded routing SoundIn -> ~soundInBus

~recorderIn = Array.fill(numInputs, { Recorder(s); });

~recorderIn.do({ |recorder, i|
	recorder.prepareForRecord(
		path: PathName(thisProcess.nowExecutingPath).pathOnly ++ "recordings" ++ "/" ++ "in" ++ i,
		numChannels: numChannels
	)
});

/*
SuperDirt Orbits recorder
*/
~recorderDry = Array.fill(~dirt.orbits.size, { Recorder(s); });
~recorderFx = Array.fill(~dirt.orbits.size, { Recorder(s); });

~dirt.orbits.do({ |orbit, i|
	~recorderDry[i].prepareForRecord(
		path: PathName(thisProcess.nowExecutingPath).pathOnly ++ "recordings" ++ "/" ++ "dry" ++ orbit.orbitIndex,
		numChannels: ~dirt.numChannels
	);

	~recorderFx[i].prepareForRecord(
		path: PathName(thisProcess.nowExecutingPath).pathOnly ++ "recordings" ++ "/" ++ "fx" ++ orbit.orbitIndex,
		numChannels: ~dirt.numChannels
	)
});
)

(
// start Recording
~recorderDry.do{ |recorder, i|
	recorder.record(bus: ~dirt.orbits[i].dryBus)
};

~recorderFx.do{ |recorder, i|
	recorder.record(bus: ~dirt.orbits[i].globalEffectBus)
};

~recorderIn.do{ |recorder, i|
	recorder.record(bus: ~soundInBus[i])
};
)

(
// stop Recording
~recorderDry.do{ |recorder|
	recorder.stopRecording;
};

~recorderFx.do{ |recorder|
	recorder.stopRecording;
};

~recorderIn.do{ |recorder|
	recorder.stopRecording;
};
)

(
// pause Recording
~recorderDry.do{ |recorder|
	recorder.pauseRecording;
};

~recorderFx.do{ |recorder|
	recorder.pauseRecording;
};

~recorderIn.do{ |recorder|
	recorder.pauseRecording;
};
)

(
// resume Recording
~recorderDry.do{ |recorder|
	recorder.resumeRecording;
};

~recorderFx.do{ |recorder|
	recorder.resumeRecording;
};

~recorderIn.do{ |recorder|
	recorder.resumeRecording;
};
)
1 Like