Skip to content

capital-G/DynGen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DynGen

DynamicUGen evaluates EEL2 code on the server, which allows to dynamically write DSP code of UGens which can be updated while its running.

Installation

Builds are occasionally uploaded at https://github.com/capital-G/DynGen/releases. Download and extract the content of the archive into the Platform.userExtensionDir folder.

macOS De-Quarantine

Since the plugin is not notarized it needs to be de-quarantined. Run the following command within SuperCollider, assuming you have installed DynGen like specified above

"xattr -rd com.apple.quarantine \"%/DynGen\"".format(Platform.userExtensionDir).unixCmd;

Build

On x86-64 machines it is necessary to have nasm installed.

git clone --recursive https://github.com/capital-G/DynGen.git
cd DynGen

# replace DSC_SRC_PATH w/ your local source code copy of SuperCollider
# and adjust the CMAKE_INSTALL_PREFIX if necessary
cmake \
    -S . \
    -B build \
    -DSC_SRC_PATH=/Users/scheiba/github/supercollider \
    -DCMAKE_INSTALL_PREFIX=./install
cmake --build build --config Release
cmake --install build --config Release

Symlink or copy the content of the install folder to Platform.userExtensionDir.

Demo

Scripts are registered on the server via DynGen and behaves like SynthDef.

All inputs are available via the variables in0, in1, ... and the outputs need to be written to out0, out1, ...

In = Output * 0.5

s.boot;

// registers the script on the server with identifier \simple
// like on SynthDef, remember to call .send
~simple = DynGenDef(\simple, "out0 = in0 * 0.5;").send;

// spawn a synth which evaluates our script
(
Ndef(\x, {DynGen.ar(
	1, // numOutputs
	~simple, // script to use - can also be DynGenDef(\simple) or \simple
	SinOsc.ar(200.0), // ... the inputs to the script
);
}).scope;
)

Modulate parameters

~modulate = DynGenDef(\modulate, "out0 = in0 * in1;").send;

Ndef(\x, {DynGen.ar(1, ~modulate, SinOscFB.ar(200.0, 1.3), LFPulse.ar(5.2, width: 0.2)) * 0.2}).play;

Single sample feedback

One pole filter

(
~onePoleFilter = DynGenDef(\onePoleFilter, "
y1 += 0; // make the variable persistent across runs
alpha = 0.95;
// the one pole filter formula
out0 = alpha * y1 + (1.0 - alpha) * in0;
y1 = out0; // write value to history
").send;
)

Ndef(\x, {DynGen.ar(1, ~onePoleFilter, Saw.ar(400.0)) * 0.2}).play;

Feedback SinOsc

A phase modulatable SinOscFB

(
~sinOscFB = DynGenDef(\sinOscFB, "
phase += 0;
y1 += 0;
twoPi = 2*$pi;
inc += 0;

inc = twoPi * in0 / srate;

x = phase + (in1 * y1) + in2;

phase += inc;
// wrap phase
phase -= (phase >= twoPi) * twoPi;

out0 = sin(x);

y1 = out0;
").send;
)

(
Ndef(\x, {
	var sig = DynGen.ar(1, ~sinOscFB,
		\freq.ar(100.0), // in0 = freq
		\fb.ar(0.6, spec: [0.0, pi]), // in1 = fb
		SinOsc.ar(\phaseModFreq.ar(1000.0 * pi)) * \modAmt.ar(0.0, spec:[0.0, 1000.0]),  // in2 = phaseMod
	);
	sig * 0.1;
}).play.gui;
)

Delay line

Write sample accurate into a modulatable delay line.

(
~delayLine = DynGenDef(\delayLine, "
buf[in1] = in0;
out0 = buf[in2];
").send;
)

(
Ndef(\x, {
	var bufSize = SinOsc.ar(4.2).range(1000, 2000);
	var writePos = LFSaw.ar(2.0, 0.02).range(1, bufSize);
	var readPos = LFSaw.ar(pi, 0.0).range(1, bufSize);
	var sig = DynGen.ar(1, ~delayLine,
		SinOsc.ar(100.0),
		writePos.floor,
		readPos.floor,
	);
	sig.dup * 0.1;
}).play;
)

Complex oscillator

Two cross phase-modulated sine oscillators, 64 times oversampled.

(
~complex = DynGenDef(\complex, "
twopi = 2*$pi;

phaseA += 0;
phaseB += 0;

freqA = in0;
freqB = in1;
modIndexA = in2;
modIndexB = in3;

oversample = 64;

osSrate = srate * oversample;
incA = freqA / osSrate;
incB = freqB / osSrate;

sumA = 0;
sumB = 0;

// calculate subsaples
loop(oversample,
    phaseA += incA;
    phaseB += incB;
    // wrap phases between [0, 1)
    phaseA -= floor(phaseA);
    phaseB -= floor(phaseB);

    // apply cross-phase modulation
    phaseA = phaseA + modIndexA * sin(twopi * phaseB);
    phaseB = phaseB + modIndexB * sin(twopi * phaseA);

    // accumulate (for downsampling)
    sumA += sin(twopi * phaseA);
    sumB += sin(twopi * phaseB);
);

// scale down b/c of os
out0 = sumA / oversample;
out1 = sumB / oversample;
").send;
)

(
Ndef(\y, {
	var sig = DynGen.ar(2, ~complex, 
		\freqA.ar(200.0),
		\freqB.ar(pi*100),
		\modA.ar(0.02, spec: [-0.1, 0.1]) * 0.05 * Env.perc(releaseTime: \releaseTime.kr(0.2)).ar(gate: Impulse.ar(\offsetKick.kr(4.0))),
		\modB.ar(0.0, spec: [-0.1, 0.1]) * 0.05,
	);
	sig * 0.1;
}).play.gui;
)

Multi-channel

~multi = DynGenDef(\multi, "out0 = in0 * in1; out1 = in0 * in2").send;

(
Ndef(\y, {DynGen.ar(2, ~multi, 
	SinOscFB.ar(200.0, 1.3), // in0
	LFPulse.ar(5.2, width: 0.2), // in1
	LFPulse.ar(3.2, width: 0.3) // in2
) * 0.2}).play;
)

License

EEL2 and WDL by Cockos are BSD licensed.

This project is GPL-3.0 licensed.