I think you can ignore this message.
The message shouldn’t stop the loop, for one thing. Each iteration of the loop is starting a subprocess (server process), and then going immediately on to the next iteration. The loop will have finished all n iterations before any of the server processes have even booted up. There is no way for a future failure in a subprocess to retroactively stop a loop that has already completed.
Second, the message is based on the idea that you asked for a node to be freed, but that node already disappeared and doesn’t exist at the moment of the request. But what’s important here is not the condition of that request, but rather the result.
| Action |
Node existed |
Node didn’t exist |
| Issue /n_free |
End state: Node doesn’t exist |
End state: Node doesn’t exist |
So, whether the error is printed or not, in either case you end up with the node having been removed.
There would be a couple of ways to prevent the message from being posted:
- One way is to remove the
doneAction from the SynthDef. If you know you’re going to n_free it, then there is no need for the synth to free itself.
- Or, let doneAction do it and delete the
x.freeMsg.
One or the other, but not both.
It’s also possible to restructure the code to write all of the files using one NRT server. My thinking here is that booting a NRT server is slower than the actual audio processing. Booting 100 NRT servers for 100 files is possible but inefficient.
Instead, the Score can allocate a buffer for the length of file that you want, and then the score consists of a series of 1/ run synth, 2/ write buffer messages.
[0.0, ["/g_new", 1, 0, 0]]
[0.0, ['/d_recv', Int8Array[83, 67, 103, 102, ... synthdef stuff...]]]
[0.0, ["/b_alloc", 0, 44100, 2, nil, 44100.0]]
[0.0, [9, 'fm', 1000, 0, 1, 'carHz', 24.473357229763, 'modHz', 1595.4977392932, 'modAmp', 9897, 'amp', 0.43937270652929, 'atk', 0.038553115502246, 'rel', 0.2032622298744, 'pan', -0.17469465732574, 'buf', 0]]
[1.5, ["/b_write", 0, "/home/xxx/tmp/perc0.wav", "WAV", "int16", -1, 0, 0, nil]]
[2.0, [9, 'fm', 1000, 0, 1, 'carHz', 4493.9346917845, 'modHz', 76.299006596099, 'modAmp', 8621, 'amp', 0.20440363673376, 'atk', 0.036271699843923, 'rel', 0.14351514096175, 'pan', 0.0030591487884521, 'buf', 0]]
[3.5, ["/b_write", 0, "/home/xxx/tmp/perc1.wav", "WAV", "int16", -1, 0, 0, nil]]
// etc. etc.
I tested with 10 and it’s very fast. It probably could write a couple hundred files in less than a second.
(
// change this for your file location -- use % where the index number should go
var pathTemplate = "~/tmp/perc%.wav".standardizePath;
var numFiles = 20;
var server = Server(\nrt,
options: ServerOptions.new
.numOutputBusChannels_(2)
.numInputBusChannels_(2)
.sampleRate_(44100)
);
var buf = Buffer(server,
server.options.sampleRate, // * 1 = 1 second, * 2 = 2 seconds etc.
2,
sampleRate: server.options.sampleRate
);
var generateOne = { |score, path, time, args, dur|
var synth;
score.add([time, (x = Synth.basicNew(\fm, server, 1000))
.newMsg(args: args)
])
.add([time + dur + 0.5, buf.writeMsg(path, "WAV", "int16")]);
};
var genMsgs = { |score, num = 10|
// if it's 10, then 1 digit because 0-9 :wink:
var numDigits = num.log10.ceil;
num.do { |i|
var iStr = i.asString;
generateOne.(score,
pathTemplate.format(
String.fill(numDigits - iStr.size, $0) ++ iStr
),
i * (buf.duration + 1),
[
\carHz, exprand(20, 10000),
\modHz, exprand(20, 10000),
\modAmp, rrand(20, 10000),
\amp, exprand(0.1, 0.5),
\atk, exprand(0.001, 0.05),
\rel, exprand(0.05, 0.94),
\pan, rrand(-0.5, 0.5),
\buf, buf
],
buf.duration
)
};
};
a = Score([
[0.0, ['/d_recv',
SynthDef(\fm,
{ arg buf, carHz=400, modHz=1, modAmp=300,amp=0.2, pan=0, atk=0.01, rel=1;
var car, mod, env;
env = EnvGen.kr(Env.perc(atk, rel));
mod = SinOsc.ar(modHz, mul:modAmp);
car = SinOsc.ar(carHz + mod) * env * amp;
car = Pan2.ar(car, pan);
RecordBuf.ar(car, buf, loop: 0, doneAction: 2)
}
).asBytes;
]],
[0.0, buf.allocMsg]
]);
genMsgs.(a, numFiles);
a.recordNRT(
// this is a trick that's noted in the NRT guide
// since the output files are being written from buffers,
// there's no need for main audio output
// so we can shunt the main output to a "don't write" location
outputFilePath: "/dev/null", // "NUL" in Windows
headerFormat: "wav",
sampleFormat: "int16",
options: server.options,
duration: nil,
action: { "done".postln }
);
server.remove;
)
hjh