I think the first thing is to think through, and be clear about, the process you want.
Your current example records the tracks concurrently. So you canât assume that the recordings will finish in order. In that case, hereâs an outline (a counter, used in a Condition function, and wait
/ signal
):
(
s.waitForBoot {
var samples;
var completed = 0;
var cond = Condition { completed >= samples.size };
// for demo purposes, this is just a 'time to finish'
samples = /*... get stuff...*/ Array.fill(5, { 10.0.rand });
samples.do { |time, i|
// simulate some process that will finish later
thisThread.clock.sched(time, {
"% finished\n".postf(i);
completed = completed + 1;
cond.signal;
});
};
cond.wait; // this waits for *all* of them
"All done".postln;
};
)
That leaves the action function.
In my example, I watched for a signal from the EventStreamPlayer that the pattern was finished.
Because youâre using record
for patterns, you donât have access to the EventStreamPlayer. Thatâs probably why you couldnât figure out where to unhang/signal. This is admittedly not easy to grasp at first â there are many kinds of processes in SC which finish in different ways.
What I would do here is to attach a cleanup to the pattern that you are recording. (Also beg pardon while I add spaces after commas â not required but IMO this is good for readability.)
player = Pbindf(m.p, \instrument, "play_mono", \buf, item, \legato, 1.0);
player = Pfset(nil, player, { cond.unhang /* or signal, for concurrent recording */ });
player.record(outputfilename, "WAV", "int16", 2, nil, 0.2, TempoClock.default, Event.default, Server.default, 0, 2);
Side note: You might be able to simplify a little by removing rt
. A waitForBoot
code block is already running in a routine. (There would be one reason to launch another routine. The waitForBoot
routine is running on AppClock, where timing may lag. When you need musical timing, that would call for a thread on a TempoClock â but, patterns run on TempoClock by default â so thereâs not a strict need to run the pattern-controller on TempoClock.)
Recording requires some initialization, and this is asynchronous. It allocates some memory from the OS. There is no way to predict how long that will take. So, it has to be outside of the audio thread, and this means that a record
convenience method cannot guarantee that recording will start precisely in sync with audio.
The other thing: Patterns play with s.latency
delay by default. (See the Server Timing help file.) But it takes some time to initialize, so I guess youâre seeing server latency minus recording setup time.
It is possible to start recording in sync, but you have to take the process apart. Iâll wrap it up into a function (much of it copied from Pattern:record) that you can call in place of the record
lines.
(
~patternRec = { |pattern, path, headerFormat, sampleFormat, numChannels = 2, dur = nil, fadeTime = 0.2, clock(TempoClock.default), protoEvent(Event.default), server(Server.default), out = 0, outNumChannels|
var buffer, nodeID, defname;
pattern = if(dur.notNil) { Pfindur(dur, pattern) } { pattern };
// recorder.recHeaderFormat = headerFormat;
// recorder.recSampleFormat = sampleFormat;
server.waitForBoot {
var group, bus, free, monitor;
// prepareForRecord
buffer = Buffer.alloc(server, 32768, numChannels);
buffer.write(path, headerFormat, sampleFormat, 0, leaveOpen: true);
defname = SystemSynthDefs.generateTempName;
SynthDef(defname, { |bufnum, bus|
var sig = In.ar(bus, numChannels);
DiskOut.ar(bufnum, sig);
}).add;
fadeTime = (fadeTime ? 0).roundUp(buffer.numFrames / server.sampleRate);
bus = Bus.audio(server, numChannels);
group = Group(server);
Monitor.new.play(bus.index, bus.numChannels, out, outNumChannels ? numChannels, group);
// important!
server.sync;
free = {
(type: \off, id: nodeID, server: server, hasGate: false).play;
{
bus.free; group.free;
buffer.free;
server.sendMsg(\d_free, defname);
}.defer(server.latency);
};
Pprotect(
Pfset(nil,
Pseq([
// start recording synth exactly in time with pattern
(
type: \on, id: nodeID, instrument: defname,
bus: bus, group: group, addAction: \addAfter,
delta: 0,
callback: { nodeID = ~id }
),
pattern <> (out: bus),
(type: \rest, delta: fadeTime)
], 1),
free
),
free // on error
).play(clock, protoEvent, quant: 0);
}
};
)
Is that hard? ⌠yeah⌠youâve accidentally stumbled into a use case where you need some fairly advanced types of control. Thereâs no way a beginner would figure this out alone.
hjh