177 lines
5.9 KiB
Text
177 lines
5.9 KiB
Text
title:: Non-Realtime Synthesis (NRT)
|
|
summary:: Non-realtime synthesis with binary files of OSC commands
|
|
categories:: Server>NRT, External Control>OSC
|
|
related:: Classes/Score
|
|
|
|
section:: Non-Realtime Synthesis
|
|
|
|
SuperCollider 3 supports non-realtime synthesis through the use of binary files of OSC commands.
|
|
|
|
First create an OSC command file (i.e. a score)
|
|
code::
|
|
f = File("Cmds.osc","w");
|
|
|
|
// start a sine oscillator at 0.2 seconds.
|
|
c = [ 0.2, [\s_new, \NRTsine, 1001, 0, 0]].asRawOSC;
|
|
f.write(c.size); // each bundle is preceded by a 32 bit size.
|
|
f.write(c); // write the bundle data.
|
|
|
|
// stop sine oscillator at 3.0 seconds.
|
|
c = [ 3.0, [\n_free, 1001]].asRawOSC;
|
|
f.write(c.size);
|
|
f.write(c);
|
|
|
|
// scsynth stops processing immediately after the last command, so here is
|
|
// a do-nothing command to mark the end of the command stream.
|
|
c = [ 3.2, [0]].asRawOSC;
|
|
f.write(c.size);
|
|
f.write(c);
|
|
|
|
f.close;
|
|
|
|
// the 'NRTsine' SynthDef
|
|
(
|
|
SynthDef("NRTsine",{ arg freq = 440;
|
|
Out.ar(0,
|
|
SinOsc.ar(freq, 0, 0.2)
|
|
)
|
|
}).writeDefFile;
|
|
)
|
|
::
|
|
then on the command line (i.e. in Terminal):
|
|
code::
|
|
./scsynth -N Cmds.osc _ NRTout.aiff 44100 AIFF int16
|
|
::
|
|
The command line arguments are:
|
|
code::
|
|
-N <cmd-filename> <input-filename> <output-filename> <sample-rate> <header-format> <sample-format> <...other scsynth arguments>
|
|
::
|
|
If you do not need an input sound file, then put "_" for the file name as in the example above.
|
|
|
|
For details on other valid arguments to the scsynth app see Server-Architecture.
|
|
|
|
This could be executed in SC as:
|
|
code::
|
|
"./scsynth -N Cmds.osc _ NRTout.aiff 44100 AIFF int16 -o 1".unixCmd; // -o 1 is mono output
|
|
::
|
|
A more powerful option is to use the link::Classes/Score:: object, which has convenience methods to create OSC command files and do nrt synthesis.
|
|
code::
|
|
(
|
|
x = [
|
|
|
|
[0.0, [ \s_new, \NRTsine, 1000, 0, 0, \freq, 1413 ]],
|
|
[0.1, [ \s_new, \NRTsine, 1001, 0, 0, \freq, 712 ]],
|
|
[0.2, [ \s_new, \NRTsine, 1002, 0, 0, \freq, 417 ]],
|
|
[0.3, [ \s_new, \NRTsine, 1003, 0, 0, \freq, 1238 ]],
|
|
[0.4, [ \s_new, \NRTsine, 1004, 0, 0, \freq, 996 ]],
|
|
[0.5, [ \s_new, \NRTsine, 1005, 0, 0, \freq, 1320 ]],
|
|
[0.6, [ \s_new, \NRTsine, 1006, 0, 0, \freq, 864 ]],
|
|
[0.7, [ \s_new, \NRTsine, 1007, 0, 0, \freq, 1033 ]],
|
|
[0.8, [ \s_new, \NRTsine, 1008, 0, 0, \freq, 1693 ]],
|
|
[0.9, [ \s_new, \NRTsine, 1009, 0, 0, \freq, 410 ]],
|
|
[1.0, [ \s_new, \NRTsine, 1010, 0, 0, \freq, 1349 ]],
|
|
[1.1, [ \s_new, \NRTsine, 1011, 0, 0, \freq, 1449 ]],
|
|
[1.2, [ \s_new, \NRTsine, 1012, 0, 0, \freq, 1603 ]],
|
|
[1.3, [ \s_new, \NRTsine, 1013, 0, 0, \freq, 333 ]],
|
|
[1.4, [ \s_new, \NRTsine, 1014, 0, 0, \freq, 678 ]],
|
|
[1.5, [ \s_new, \NRTsine, 1015, 0, 0, \freq, 503 ]],
|
|
[1.6, [ \s_new, \NRTsine, 1016, 0, 0, \freq, 820 ]],
|
|
[1.7, [ \s_new, \NRTsine, 1017, 0, 0, \freq, 1599 ]],
|
|
[1.8, [ \s_new, \NRTsine, 1018, 0, 0, \freq, 968 ]],
|
|
[1.9, [ \s_new, \NRTsine, 1019, 0, 0, \freq, 1347 ]],
|
|
|
|
[3.0, [\c_set, 0, 0]]
|
|
];
|
|
)
|
|
::
|
|
You can then use code::Score.write:: to convert the above to the OSC command file as follows:
|
|
code::
|
|
Score.write(x, "score-test.osc");
|
|
"./scsynth -N score-test.osc _ score-test.aiff 44100 AIFF int16 -o 1".unixCmd;
|
|
::
|
|
Score also provides methods to do nrt synthesis directly:
|
|
code::
|
|
(
|
|
var f, o;
|
|
g = [
|
|
[0.1, [\s_new, \NRTsine, 1000, 0, 0, \freq, 440]],
|
|
[0.2, [\s_new, \NRTsine, 1001, 0, 0, \freq, 660]],
|
|
[0.3, [\s_new, \NRTsine, 1002, 0, 0, \freq, 220]],
|
|
[1, [\c_set, 0, 0]]
|
|
];
|
|
o = ServerOptions.new.numOutputBusChannels = 1; // mono output
|
|
Score.recordNRT(g, "help-oscFile.osc", "helpNRT.aiff", options: o); // synthesize
|
|
)
|
|
::
|
|
|
|
section:: Analysis using a Non-Realtime server
|
|
|
|
An NRT server may also be used to extract analytical data from a sound file. The main issues are:
|
|
|
|
DEFINITIONlIST::
|
|
## Suppressing audio file output
|
|
|| In macOS and Linux environments, use teletype::/dev/null:: for the output file path. In Windows, use teletype::NUL::.
|
|
## Retrieving analytical data.
|
|
|| The easiest way is to allocate a buffer at the beginning of the NRT score, and use BufWr to fill the buffer. At the end of the score, write the buffer into a temporary file. Then you can use SoundFile on the language side to access the data. See the example.
|
|
::
|
|
|
|
code::
|
|
// Example: Extract onsets into a buffer.
|
|
|
|
(
|
|
fork {
|
|
var resultbuf, resultpath, oscpath, score, dur, sf, cond, size, data;
|
|
|
|
// get duration
|
|
sf = SoundFile.openRead(Platform.resourceDir +/+ "sounds/a11wlk01.wav");
|
|
dur = sf.duration;
|
|
sf.close;
|
|
|
|
resultpath = PathName.tmp +/+ UniqueID.next ++ ".aiff";
|
|
oscpath = PathName.tmp +/+ UniqueID.next ++ ".osc";
|
|
|
|
score = Score([
|
|
[0, (resultbuf = Buffer.new(s, 1000, 1, 0)).allocMsg],
|
|
[0, [\d_recv, SynthDef(\onsets, {
|
|
var sig = SoundIn.ar(0), // will come from NRT input file
|
|
fft = FFT(LocalBuf(512, 1), sig),
|
|
trig = Onsets.kr(fft),
|
|
// count the triggers: this is the index to save the data into resultbuf
|
|
i = PulseCount.kr(trig),
|
|
// count time in seconds
|
|
timer = Sweep.ar(1);
|
|
// 'i' must be audio-rate for BufWr.ar
|
|
BufWr.ar(timer, resultbuf, K2A.ar(i), loop: 0);
|
|
BufWr.kr(i, resultbuf, DC.kr(0), 0); // # of points in index 0
|
|
}).asBytes]],
|
|
[0, Synth.basicNew(\onsets, s, 1000).newMsg],
|
|
[dur, resultbuf.writeMsg(resultpath, headerFormat: "AIFF", sampleFormat: "float")]
|
|
]);
|
|
|
|
cond = Condition.new;
|
|
|
|
// osc file path, output path, input path - input is soundfile to analyze
|
|
score.recordNRT(oscpath, "/dev/null", sf.path, sampleRate: sf.sampleRate,
|
|
options: ServerOptions.new
|
|
.verbosity_(-1)
|
|
.numInputBusChannels_(sf.numChannels)
|
|
.numOutputBusChannels_(sf.numChannels)
|
|
.sampleRate_(sf.sampleRate),
|
|
action: { cond.unhang } // this re-awakens the process after NRT is finished
|
|
);
|
|
cond.hang; // wait for completion
|
|
|
|
sf = SoundFile.openRead(resultpath);
|
|
// get the size: one frame at the start
|
|
sf.readData(size = FloatArray.newClear(1));
|
|
size = size[0];
|
|
// now the rest of the data
|
|
sf.readData(data = FloatArray.newClear(size));
|
|
sf.close;
|
|
|
|
File.delete(oscpath); File.delete(resultpath);
|
|
|
|
data.postln; // these are your onsets!
|
|
};
|
|
)
|
|
::
|