Trouble adding new Synthdefs

Hello there.

I’m trying to add new synthdefs to my sonic pi environment since the built-ins aren’t suited to some of what i want to make (Industrial and EBM. They’re wonderful for dark ambient and dance-y music). I’ve found some synthdefs online which seem to adhere to the guidelines here. (or can easily be modified to fit).

They load without complaining, but when I try to play them with synth "name", note: :c1 there’s no sound. They also never stop. I managed to get sound out of this one, but it kept playing until I alt-s’d. The example synth didn’t make any sound at all.

I just cannot for the life of me make it work. I’ve even tried to super-basic example in the guidelines and I get the same result. I’m using Sonic Pi 3.1.0. on Windows 10, local install. Am i missing something obvious?

1 Like

Hi @chris.krakou,

I am not really an expert on the synth def subject but have you checked the box Allow external synth defs in the preferences (don’t know exactly the English labels because my version is in German)? Might be worth a try:

Aah yes, I should have mentioned that I’ve enabled external synths. That’s what i meant by “loads without problems”. The problem I think I’m facing is that the synth is loaded, but not playing properly.

1 Like

Hi Chris,

there are definitely a few gotchas with using your own synthdefs. SuperCollider’s notion of a ‘synth’ is extremely broad and covers any combination of it’s so-called ugens (the building blocks). This gives incredible control and flexibility but also makes it a little more difficult to share and build infrastructure around them.

Sonic Pi therefore places some constraints on external synthdefs so that they can work nicely with the environment. The following are a list of these constraints:

  • They must handle all their own arguments (for example, Sonic Pi will not convert MIDI notes like :e1 to numbers in MIDI or Hz as it has no way of knowing which units your args take).
  • They must self-terminate. This is typically achieved with an envelope which ‘frees’ the containing synth. Sonic Pi will only trigger your synths and will not manage its lifecycle beyond that.
  • They must have an argument named out_bus which is where it will feed a stereo signal.
  • They must not use any SuperCollider features that aren’t available in the audio server (the language runtime isn’t available in Sonic Pi). This means you need to compile your synthdefs and be able to use them outside of the supercollider language.

One of the main reasons Sonic Pi doesn’t (yet) have any functionality to design your own sounds internally is that this stuff is hard and needs a lot of specific knowledge. It will likely be hard to just cut and paste a synth design and get it to work seamlessly. You’ll likely need to redesign some parts to get it to fit into Sonic Pi’s architecture.

However, if you do wind up designing some new sounds - please do consider sharing them back with us here and we can even look into including them in a new release!

1 Like

Thanks a ton. I’ll look into making the base example work first, and I’ll make sure to share the boilerplate I come up with.
As for the midi notes like :e1, I thought those were aliases for midi numbers that Sonic Pi fed into the synth. Are those strings or enums or something else?. As for self-termination, is there more to that than adding doneAction = 2 to the envelope?

1 Like

@chris.krakou - re self-termination, that should be enough.

Re note names etc - What Sam said. Sonic Pi does not have any method of automatically knowing the format of SynthDef parameters etc - it needs to be told. Currently, Sonic Pi is informed about the ‘built in’ synths by including a bunch of extra metadata along side them. With ‘External’ synths on the other hand, there is no accompanying metadata that Sonic Pi is able to read, and as such we do not try to handle their parameters in any special way. In order to allow your new synths to handle note names or numbers, they would currently need to have some information added to this internal metadata.

This question was also raised recently on Github - there are discussions going around about a proposed improvement which would allow synth parameters to be specified in a more flexible manner. (I’m looking at working on that some time soon hopefully).

1 Like

Given @samaaron’s response above, I think my question about how to manage sustaining synthdefs is answered. I was wondering how sustaining envelopes worked, and wondered if the sustain was supplied (if an argument) at synth creation time, or whether there was communication through a gate argument. If Sonic Pi only communicates with the synthdef at creation time, and when arguments are updated, then that implies no ‘gate’ communication.

How is it possible to read the built-in synthdefs of Sonic Pi? I can find the definitions in Clojure, in (for example) /Applications/Sonic Pi.app/etc/synthdefs/designs/sonic_pi/synths/basic.clj, but I’d like to be able to read the synthdef versions, if possible. Is there a translator for the compiled version. it seems not? If not, is there somewhere where I can find more example synthdefs suitable for use in Sonic Pi?

Also, how would samples be sent to a synthdef synth? I guess that the bottom line is that the full path of the sample can be sent as a string, but is there a way for samples sent in variables, or by built-in short names to be sent. I guess if I could read the source for sample.scsynthdef then that would tell me the answer.

Re decompiling to the text definition - I also don’t know of any way to do this.
Re finding more SynthDefs, I personally don’t know of any specific places off the top of my head - I know there are some, but I’d need to do some googling to find them…
Re sending samples to SynthDefs, what is actually sent in the long run is a buffer Id number. (The Sonic Pi server does some jiggery pokery behind the scenes to tell Supercollider to allocate memory for a buffer and load the sample into it - this returns a buffer Id if successful).

1 Like

Thanks. Continuing to work on this.

BTW: This may not be the most serious sound I’ve ever created in my life :smiley:

(
SynthDef.new( \whoosh,
	
	
	{   arg amp=10.0, time=3, q=0.01, lfo_depth=0.2, lfo_freq=10,
		    freq_from = 12000, freq_to = 20, out_bus=0, pan=0;
		
		var snd, env, left, right;
		
		snd = WhiteNoise.ar( );
		env = Line.kr( 1, 0, time, doneAction: 2 );
		
		snd = Resonz.ar( snd, Line.kr( freq_from, freq_to, time ) * 
			  SinOsc.kr( lfo_freq, 0, lfo_depth, 1 ), q, amp * env );
		
		pan = pan/2 + 0.5;
		
		left = snd * cos( pan * pi / 2 );
		right = snd * sin( pan * pi / 2 );
		
		Out.ar( out_bus, [left, right ]  );
		
} ).writeDefFile( "/Users/pieater/Sonic Pi/synthdefs" );
//} ).add;
)
use_bpm 120

load_synthdefs "/Users/pieater/Sonic Pi/synthdefs"

live_loop :whoosher do
  with_fx :reverb, mix: 0.5, room: 0.7 do
    
    synth :whoosh, freq_from: rrand( 20, 15000 ),
      freq_to: rrand( 20, 15000 ),
      time: rrand( 2, 8 ),
      lfo_depth: rrand( 0, 0.4 ), lfo_rate: rrand( 0.3, 15 ),
      pan: rrand( -1, 1 )
  end
  sleep 1
end
1 Like

I’m noticing the same thing @PiEaterAndPlayer pointed out about the SuperCollider \gate convention.

In SuperCollider, it appears the convention for building synths with sustained envelopes (i.e. envelopes that sustain for as long as you’re pressing a key on the keyboard) is that you must define a \gate arg (usually defaulted to 1), and pass that gate arg to an envelope that’s configured to tear the synth down when it reaches zero (doneAction: 2). If you honor this convention, then your synthdef will work with common SuperCollider conventions like playing melodies with Pbind, and also SC lets you store synth nodes that you can eventually .release, which will cause the envelope to start releasing and eventually free up the synth resources.

I might be misunderstanding something but it seems like there’s an impedance mismatch between this convention and the Sonic Pi approach wherein all args (including duration/sustain) must be specified up front for a single call to synth. I think I sorta understand the reasons why (I imagine if Sonic Pi let you play a synth indefinitely, you’d be required to somehow track all the unreleased synth nodes yourself and do a lot more manual cleanup?). But as someone who dabbles in both SuperCollider and Sonic Pi, it seems weird that a synth I’m building up in SC would need to have its interface changed to fit into the Sonic Pi world.

Would it be possible to unify these synthdef conventions such that an SC-style synth with \gate param Just Works™ in Sonic Pi? i.e. synth "myScSynth", sustain: 1 would kick off the SC synth and then set the \gate to 0 after 1 second to let the SC synth start to fade out on its own.

And then I think it’d made sense to make it possible opt into a manually sustained synth call, i.e. one that sustains infinitely until you manually call synth_node.release. Then you could have long live loops that simply change the controls on a never-ending synth node (perhaps one that has really interesting/unusual custom options / parameters). And if you ever just wanted to stop all sounds, Sonic Pi would just keep track of all manual/infinite synth nodes and set their \gates to zero to fade them out gracefully before shutting down the SC engine.

I have no idea the work involved to do this, but I might be interested to work on this myself if there’s not a conceptual flaw to this idea.

1 Like