Greyhole (Global Effect)

Hey everyone —

I've just gotten started with TidalCycles, and felt an itch yesterday to create a synthdef for the amazing Greyhole reverb/echo effect from the sc3-plugins. I've configured it locally as a global effect, in a way I hope is appropriate/safe. I'm definitely open to feedback if you spot anything that seems off.

Here's a little demo for those who haven't experimented with the ugen in sc before:

SynthDef

(
var numChannels =  ~dirt.numChannels;
(1..SuperDirt.maxSampleNumChannels).do { |numChannels|
	SynthDef("grey_h" ++ numChannels, { |dryBus, effectBus, gdelay = 1, gdamp = 0.5, gsize = 0.5 gdiff = 0.5, gfdbk = 0.5, gdepth = 0, gfreq = 0, gverb = 0|
    var signal = In.ar(dryBus, ~dirt.numChannels);
	var verbd = Greyhole.ar(
			signal,
			gdelay,
			gdamp,
			gsize,
			gdiff,
			gfdbk,
			gdepth,
			gfreq,
    ) * gverb;
    Out.ar(effectBus, verbd);
  }, [\ir, \ir]).add;
}
)

I have this SynthDef saved in a file, stored in SuperCollider's synthdefs folders under Library/Application Support on my mac, which I then load in my startup file:

load("/Users/olivier/Library/Application Support/SuperCollider/synthdefs/grey_h.scd");

The global effect is declared and added to all orbits in my startup file also:

~dirt.orbits.do { |x|
	    var grh = GlobalDirtEffect(\grey_h, [\gdelay, \gdamp, \gsize, \gdiff, \gfdbk, \gdepth, \gfreq, \gverb]);
            x.globalEffects = x.globalEffects
                  .addFirst(grh);
            x.initNodeTree;
        };

Lastly, you'll have to evaluate the following lines in your tidal file (or add them to your BootTidal.hs file:

let gverb = pF "gverb" -- dry/wet (0-1)
    gdelay = pF "gdelay" -- approximate reverberation time in seconds (0.1 - 60 sec)
    gdamp = pF "gdamp" -- controls damping of high-frequencies as the reverb decays. 0 is no damping, 1 is very strong damping. Values should be between (0..1). argumentsize scales size of delay-lines within the diffusion unit, producing the impression of a larger or smaller space. Values below 1 can sound metallic. Values should be between (0.5..5).
    gsize = pF "gsize" -- 
    gdiff = pF "gdiff" -- controls pattern of echoes produced by the diffuser. At very low values, the diffuser acts like a delay-line whose length is controlled by the 'size' parameter. Medium values produce a slow build-up of echoes, giving the sound a reversed-like quality. Values of 0.707 or greater than produce smooth exponentially decaying echoes. Values should be in the range (0..1).
    gfdbk = pF "gfdbk" -- amount of feedback through the system. Sets the number of repeating echoes. A setting of 1.0 produces infinite sustain. Values should be in the range (0..1)
    gdepth = pF "gdepth" -- depth (0..1) of delay-line modulation. Use in combination with modFreq to produce chorus and pitch-variations in the echoes.
    gfreq = pF "gfreq" -- frequency (0..10 Hz) of delay-line modulation. Use in combination with modDepth to produce chorus and pitch-variations in the echoes.

Have fun.

9 Likes

That's super cool, nice one - nice variation on the defaut verb :slight_smile:

1 Like

This looks super cool, but it seems that I can't get it to work.
I have added the SynthDef code to a file, modified my startup.scd file so that it looks like this:

startup.scd
/*
This is an example startup file. You can load it from your startup file
(to be found in Platform.userAppSupportDir +/+ "startup.scd")
*/

(
s.reboot { // server options are only updated on reboot
    // configure the sound server: here you could add hardware specific options
    // see http://doc.sccode.org/Classes/ServerOptions.html
    s.options.numBuffers = 1024 * 256; // increase this if you need to load more samples
    s.options.memSize = 8192 * 32; // increase this if you get "alloc failed" messages
    s.options.numWireBufs = 64; // increase this if you get "exceeded number of interconnect buffers" messages 
    s.options.maxNodes = 1024 * 32; // increase this if you are getting drop outs and the message "too many nodes"
    s.options.numOutputBusChannels = 2; // set this to your hardware output channel size, if necessary
    s.options.numInputBusChannels = 2; // set this to your hardware output channel size, if necessary
    // boot the server and start SuperDirt
    s.waitForBoot {
        ~dirt = SuperDirt(2, s); // two output channels, increase if you want to pan across more channels
        ~dirt.loadSoundFiles;   // load samples (path containing a wildcard can be passed in)
        // for example: ~dirt.loadSoundFiles("/Users/myUserName/Dirt/samples/*");

        ~dirt.start(57120, 0 ! 12);   // start listening on port 57120, create two busses each sending audio to channel 0

	load("/home/th4/tidal/hacks/greyhole.scd");
	~dirt.orbits.do { |x|
	    var grh = GlobalDirtEffect(\grey_h, [\gdelay, \gdamp, \gsize, \gdiff, \gfdbk, \gdepth, \gfreq, \gverb]);
            x.globalEffects = x.globalEffects.addFirst(grh);
			// x.globalEffects.postln;
            x.initNodeTree;
        };

        // optional, needed for convenient access from sclang:
        (
            ~d1 = ~dirt.orbits[0]; ~d2 = ~dirt.orbits[1]; ~d3 = ~dirt.orbits[2];
            ~d4 = ~dirt.orbits[3]; ~d5 = ~dirt.orbits[4]; ~d6 = ~dirt.orbits[5];
            ~d7 = ~dirt.orbits[6]; ~d8 = ~dirt.orbits[7]; ~d9 = ~dirt.orbits[8];
            ~d10 = ~dirt.orbits[9]; ~d11 = ~dirt.orbits[10]; ~d12 = ~dirt.orbits[11];
        );
	
	    };

    s.latency = 0.3; // increase this if you get "late" messages
};
);

and added the definitions to my BootTidal.hs (I believe this part is not the issue)

I've tried putting the global effect code before and after the ~dirt.start line.
When putting it after (as in the file I've posted), I get this error:

*** ERROR: SynthDef grey_h2 not found
FAILURE IN SERVER /s_new SynthDef not found

which I don't get when putting in before.
However, in both cases testing it in Tidal doesn't seem to make any difference sound-wise, even though invoking SynthDescLib.global.browse; shows grey_h1 and grey_h2 under the SynthDefs listing.

Can someone who knows more about SuperDirt than me tell me what I'm doing wrong? Thanks in advance!

I had this issue too when I was setting it up @th4. The thing that worked for me was making sure the .scd file and the synthdef name (in the startup file) have the same name. Maybe try changing the name of your .scd file from greyhole.scd to grey_h.scd?

Still no luck. Would you mind posting a stripped-down version of your startup.scd?

3 Likes

small bump, this error is also going on for me and i'd love to know how you got it working @oli– my file is called grey_h.scd as well

2 Likes

Dang — sorry for the delay! I’ll try to share my setup in more detail this weekend.

@th4 @arethusa Here's my startup file, along with the synthdef file. Note that in my startup, I'm loading the .scd file before ~dirt.start; maybe that has something to do with it?

Startup File
(
s.reboot {
	s.options.inDevice = "M4";
	s.options.outDevice = "BlackHole 16ch";
	s.options.numBuffers = 2048 * 256;
	s.options.memSize = 8192 * 32;
	s.options.numWireBufs = 64;
	s.options.maxNodes = 1024 * 32;
	s.options.numOutputBusChannels = 16;
	s.options.numInputBusChannels = 4;
	s.waitForBoot {
		~dirt = SuperDirt(2, s);
		load("/Users/olivier/Library/Application Support/SuperCollider/synthdefs/mi-ugens.scd");
		load("/Users/olivier/Library/Application Support/SuperCollider/synthdefs/grey_h.scd");
		~dirt.loadSoundFiles;
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/samples-extra/*");
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/perc/*");
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/inst/*");
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/field/*");
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/superc/*");
		~dirt.loadSoundFiles("/Users/olivier/Sound/slib/tss/*");
		// s.sync; // optionally: wait for samples to be read
		~dirt.start(57120, [0, 2, 4, 6, 8, 10, 12, 14]);
		(
			~d1 = ~dirt.orbits[0]; ~d2 = ~dirt.orbits[1]; ~d3 = ~dirt.orbits[2];
			~d4 = ~dirt.orbits[3]; ~d5 = ~dirt.orbits[4]; ~d6 = ~dirt.orbits[5];
			~d7 = ~dirt.orbits[6]; ~d8 = ~dirt.orbits[7]; ~d9 = ~dirt.orbits[8];
			~d10 = ~dirt.orbits[9]; ~d11 = ~dirt.orbits[10]; ~d12 = ~dirt.orbits[11];
		);
		        // define global effects
        ~dirt.orbits.do { |x|
            var clouds = GlobalDirtEffect(\global_mi_clouds, [\cloudspitch, \cloudspos, \cloudssize, \cloudsdens, \cloudstex, \cloudswet, \cloudsgain, \cloudsspread, \cloudsrvb, \cloudsfb, \cloudsfreeze, \cloudsmode, \cloudslofi]);
            var verb = GlobalDirtEffect(\global_mi_verb, [\verbwet, \verbtime, \verbdamp, \verbhp, \verbfreeze, \verbdiff, \verbgain]);
			var grh = GlobalDirtEffect(\grey_h, [\gdelay, \gdamp, \gsize, \gdiff, \gfdbk, \gdepth, \gfreq, \gverb]);
            x.globalEffects = x.globalEffects
              .addFirst(clouds)
              .addFirst(verb)
			  .addFirst(grh);
			x.initNodeTree;
        };
        // end define global effects
		~looper = TidalLooper(~dirt);
        // You can adjust these parameter even in runtime
        /*~looper.rLevel = 1.5;
        ~looper.pLevel = 0.8;
        ~looper.linput = 0; // Set this to your main input port.
        ~looper.lname = "mybuffer";*/
	};
	s.latency = 2; // increase this if you get "late" messages
};
s.meter;
)
grey_h.scd
(
var numChannels =  ~dirt.numChannels;
(1..SuperDirt.maxSampleNumChannels).do { |numChannels|
	SynthDef("grey_h" ++ numChannels, { |dryBus, effectBus, gdelay = 1, gdamp = 0.5, gsize = 0.5 gdiff = 0.5, gfdbk = 0.5, gdepth = 0, gfreq = 0, gverb = 0|
    var signal = In.ar(dryBus, ~dirt.numChannels);
	var verbd = Greyhole.ar(
			signal,
			gdelay,
			gdamp,
			gsize,
			gdiff,
			gfdbk,
			gdepth,
			gfreq,
    ) * gverb;
    Out.ar(effectBus, verbd);
  }, [\ir, \ir]).add;
}
)

Hope this helps!

1 Like

So I've experimented with this again, and it seems that the issue is with the "load" part in startup.scd, since copy-pasting the contents of grey_h.scd directly in SuperCollider (followed by the code adding the effects to each of the orbit) fixes my issue.
It's still not a satisfying solution, but it is a workaround and it can be a first step to a more perennial fix.
I note that @oli seems to use Windows, while I use Linux (I don't know about @arethusa), this could be potentially linked?

1 Like

Weird! For the record, I’m actually on a new M1 MacBook Pro. I definitely struggled myself to get the effect running, and I’m sadly at a loss to explain how - or why - my current config works.

I'm on a pretty old 2015 MacBook pro, I made the change oli suggested and it appears it's working for me now!

1 Like

what would be the correct way to define this as a non-global effect?

i tried taking the example in Adding Effects | Tidal Cycles and replacing the parameter names with the ones defined in grey_h.scd like so:

(
~dirt.addModule('grey_h', { |dirtEvent|
	dirtEvent.sendSynth('grey_h' ++ ~dirt.numChannels,
        // OPTIONAL
        // passing this array of parameters could be left out,
        // but it makes it clear what happens
        [
            gdelay: ~gdelay,
            gdamp: ~gdamp,
            gsize: ~gsize,
            gdiff: ~gdiff,
			gfdbk: ~gfdbk,
			gdepth: ~gdepth,
			gfreq: ~gfreq,
			gverb: ~gverb,
		]
    )
}, { ~tsdelay.notNil or: { ~xsdelay.notNil } }); // play synth only if at least one of the two was given
)

but i get no sound when testing in tidal. i also get no error messages in either sc or tidal, so it seems the parameters are defined properly, my guess is they are just not calling the grey_h.scd file correctly.
this is my first time loading in custom effects (although i did try out the example on the page linked above and was able to get it working correctly) so i might be going about this totally the wrong way :slight_smile:

I didn't know about this delay effect. Sounds awesome! Specially patterned

I'm finally getting a chance to return to this - is anyone else getting excessive CPU usage when this is turned on?

My SuperCollider load goes to ~20% when it's enabled, and ~1% when it's not

Same for me, jump to >20% idle CPU usage when Grayhole is in my node tree. verb from mi-ugens is way cheaper.