Plotting a UGen without waiting

Say I want to plot a UGen over a second:

{ Line.kr }.plot(1)

I have to wait a second before the plot appears, which makes sense since it’s waiting for real-time values to be emitted from the UGen, but I’m wondering whether it’s possible to generate more immediate plots using some kind of “virtual clock”, where it just plots the values that would be emitted if it were running in real time? In other words, it seems like it should be possible to generate a plot of a UGen’s behavior over 10s without having to actually wait 10 seconds. Does such a thing exist?

Have you tried .scope? Does that do what you want?

Sam

You can do NRT synthesis and write a SoundFile, then get the data into language and plot. I’m not aware if there’s a out-of-the box solution for this, but writing a little helper program for this should be rather straight.

There’s no way to make a real time server instance run faster than real time, even temporarily.

Btw on my machine, plotting one second means squishing 44100 audio samples into about 1300 horizontal pixels, i.e. 33 samples per pixel, so I generally prefer to plot shorter durations that can render accurately. (AFAIK plots don’t currently have zoom and scroll features, unless I missed something.)

True. There’s some overhead to launch the NRT server process – while rendering one second of a single UGen would be instantaneous, launching the process wouldn’t be, so I think this wouldn’t meet the requirement for rapid rendering – unless you’re plotting 20/30 seconds of audio, which is not really useful unless you can zoom and scroll through the plot.

TL;DR as I understand it, plot is currently designed for about a thousand samples of audio or about 22 ms. To make it useful for more data requires interaction features that don’t currently exist.

hjh

Oh dear, how easily said, how wrong it can be …

Just finished a basic version, but many cliffs around: processes with multiple asynchronicity - but most annoying: Score’s help file contains an error: the SynthDef has to be stored, not added - it was in the depth of my memory that something was critical here. This error is there for eternity but I’ve forgotten meanwhile …

// helper synthDef to indicate storage of temporary SynthDef of passed function

(
SynthDef(\finishedStore, {
	SendReply.ar(Impulse.ar(0), '/finishedStore');
	Line.ar(dur: 0.001, doneAction: 2);
	Silent.ar
}).store
)


(
~plotNRT = { |function, duration = 1, numChannels = 1|
	var soundFile, array;
	var synthDef = function.asSynthDef;
	var dateString = Date.getDate.stamp;
	var renderString = "render_" ++ dateString;
	var soundFileName = Platform.recordingsDir +/+ renderString ++ ".aiff";
	var options = ServerOptions().numOutputBusChannels = numChannels;
	var oscFunc = OSCFunc({ |msg|
		// produces score and renders aiff
		Score.recordNRT(
			[
				[0.0, [\s_new, synthDef.name, 1000, 0, 0]],
				[duration, [\c_set, 0, 0]]
			],
			renderString ++ ".osc",
			soundFileName,
			options: options,
			duration: duration,
			// after rendering is finished we can plot
			action: {
				soundFile = SoundFile.new;
				soundFile.openRead(soundFileName);
				{
					array = FloatArray.newClear(soundFile.numFrames);
					soundFile.readData(array);
					array.plot;
					soundFile.close;
				}.defer

			}
		);
	}, '/finishedStore').oneShot;
	// triggers synth finishedStore which triggers OSCFunc
	synthDef.store(completionMsg: [\s_new, \finishedStore, -1]);
}
)


// plot 10 seconds
// audio file stored in SC recordings directory

~plotNRT.({ LFDNoise3.ar(1) }, 10)

Analogously this can be done for Pattern (well, I shouldn’t again state that it’s straight …)

1 Like

The acceleration factor here on my machine is around 40, plot isn’t really well suited for files of some minutes, true. NRT rendering on the other hand is really a nice option for several reasons – e.g. because of sample accuracy with Patterns, but also for usage of audio files, e.g. viewing in a smart litlle editor like ocenaudio.

Not strictly true that store is required.

Non-Realtime Synthesis (NRT) | SuperCollider 3.12.2 Help is an example of including the SynthDef in the score file (without using store). It would be nice if that were easier, but it’s possible (and not too hard in a helper function).

(This helpfile is linked in from Score and easy to find by searching “NRT” – I spent rather a lot of time writing it and I think it answers a lot of questions. Really should start with this document rather than jumping right to Score.)

hjh

Right, I remembered that writeDefFile is also fine, it came to my mind then, but however Score help is erroneous - if something doesn’t work as expected you don’t think at first that help is wrong and wouldn’t immediately look for another help file. I think we should finally file this on GitHub (after 15 years or so).

It is still not strictly necessary to create a scsyndef file on disk! You can add a d_recv message into the Score. Really, seriously, it totally works.

Score help uses .store throughout.

hjh

No, it uses ‘add’ twice:

http://doc.sccode.org/Classes/Score.html#NRT%20Examples
http://doc.sccode.org/Classes/Score.html#Real-time%20Examples

… and a second time “oh dear” …

File writing conventions have changed … on my old mac above example works but not on the other machine with OS 10.13.6, we have to specify the path to the osc file now, e.g.:

// helper synthDef to indicate storage of temporary SynthDef of passed function

(
SynthDef(\finishedStore, {
	SendReply.ar(Impulse.ar(0), '/finishedStore');
	Line.ar(dur: 0.001, doneAction: 2);
	Silent.ar
}).store
)


(
~plotNRT = { |function, duration = 1, numChannels = 1|
	var soundFile, array;
	var synthDef = function.asSynthDef;
	var dateString = Date.getDate.stamp;
	var renderString = "render_" ++ dateString;
	var soundFileName = Platform.recordingsDir +/+ renderString ++ ".aiff";
	var options = ServerOptions().numOutputBusChannels = numChannels;
	var oscFunc = OSCFunc({ |msg|
		// produces score and renders aiff
		Score.recordNRT(
			[
				[0.0, [\s_new, synthDef.name, 1000, 0, 0]],
				[duration, [\c_set, 0, 0]]
			],
			("~/" ++ renderString ++ ".osc").standardizePath,
			soundFileName,
			options: options,
			duration: duration,
			// after rendering is finished we can plot
			action: {
				soundFile = SoundFile.new;
				soundFile.openRead(soundFileName);
				{
					array = FloatArray.newClear(soundFile.numFrames);
					soundFile.readData(array);
					array.plot;
					soundFile.close;
				}.defer

			}
		);
	}, '/finishedStore').oneShot;
	// triggers synth finishedStore which triggers OSCFunc
	synthDef.store(completionMsg: [\s_new, \finishedStore, -1]);
}
)


// plot 10 seconds
// audio file stored in SC recordings directory

~plotNRT.({ LFDNoise3.ar(1) }, 10)

This issue is worse than the other, I’m afraid it is affecting a lot of help files and users’ code in general.

3 Likes

This at least has been fixed in the development branch.

Oh, and the same commit fixes the Mac temp directory and recommends its use for Score NRT rendering.

Unfortunately this was never backported into 3.10 (which would explain why sccode.org is using the older version). So for the next couple months until 3.11, right, the help is broken. (I’m using a build based on develop, so I saw the newer help, so I did see .store in my local SC help browser.)

hjh

1 Like

Ah ok, this explains some confusion and the commit is at least good news. Thanks for your efforts on the NRT help, James. Lacking and misleading documentation in this area might have prevented people from diving into NRT, which is a pity as it’s really a powerful option.

This is also an issue on MS Windows if using the exe installer, or generally installation to paths that only the admin can write to like “Program Files”, instead of extracting the zip distro to some other non-canonical path. Your first example tries to write to the SC install folder, which will fail on Windows in protected locations like “Program Files”. I know because the .osc files were left over in my (unprotected) SC install folder after running your example. Best way is probalby for that line to read

(Platform.defaultTempDir +/+ renderString ++ ".osc").standardizePath,

as in geoffroymontel commit linked by jamshark70 later in this thread.

Thanks for showing how to do this, by the way. There are few SC NRT examples to be found.

Thanks @dkmayer !

There seems to be another issue when using windows 11: On fresh install, if you haven’t executed any recording before, SuperCollider recording folder (C:\Users\yourUserName\OneDrive\Documents\SuperCollider\Recordings) will not be available , so the code above will trow an error.

The temporary solution is to execute any recording before using NRT mode…

Or, add into the function:

var dir = Platform.recordingsDir;

...

if(File.exists(dir).not) {
    File.mkdir(dir);
};

hjh

1 Like

If someone could add this plotNRT as pull request it would be great to have it available!