Mi-UGens for Norns

the big reason to not bundle stuff into a script subfolder, is to avoid the dreaded duplicate class error when that script is copied. unfortunately we could not come up with any reasonable direct fix for this.

maybe we should just have ~/dust/lib or something for things that must never be duplicated.

5 Likes

I agree that being able to support more than just scripts could provide tangible benefit. My primary concern is that we collectively work to identify a solution which provides predictable and reliable results for people as well as a solution which is maintainable.

Unlike lua supercollider code there are potentially glibc and other compiler runtime issues to be considered when delivering binaries (this seems to have already come up during @okyeron’s initial experimentation).

This was one of my initial concerns regarding placement of ugens under the dust/code structure. We know that SC doesn’t like duplicate classes. What does SC do when it finds multiple copies of a ugen?

Maybe more critical than were to place stuff which shouldn’t be copied is what the means of distribution would be. Today we are using git under the hood which isn’t great for binaries (due to the space consumed by commit history). It might be possible to only do shallow checkouts… An open question would be how to manage the dependencies (between script/projects and the contents of the global lib directory)

4 Likes

Speech synthesis with Plaits!

Also added midi note in for Rings

and then added Elements!

19 Likes

@toneburst could you run this plaits version MiPlaits.scx (259.0 KB), and report back if it works on catalina?

20 characters of Will do!

wrks like a dream plaits is awsome. :grinning:

Unfortunately not.

It’s not just a certificate issue, I think. Apple need to check the software before they allow it to run in Catalina, I think (though I’m not a developer, so don’t know the details).

ok, thanks. Catalina has to wait then… :slightly_frowning_face:

It’s a PITA. Apple have gone overboard with security, and the systems they’ve implemented in many cases make no sense, from what I’ve been reading.

yes you can open that on Catalina - BUT you have to go through the dance

So when you get the dialog above ^^^^ you then go to system preferences/ privacy/general and click ‘open anyway’. THEN close supercollider and open it again , boot server and then it will give you a new dialog box with ‘open anyway’ as an option - which you can click. And YES I have done this for all my bloody extensions on both my computers and YES Apple deserve a kick in the £(&$^&^&*£

that said I’m not finding MiPlaits when it is running which is odd because it clearly opened it and no errors to be seen

5 Likes

MiClouds implemented as a pedal unit in Pedalboard. Beats by Beets.

22 Likes

I’ve just been attempting to build my own 3-channel SC engine, using MiPlaits, based on yours, @okyeron.

It seems to be working so far, but one thing I can’t work out how to do: MiPlaits UGen output is 2-channel, with main and aux models on the L and R channels. I’m sure this is really simple, but I can’t work out how to have just one of the channels copied to both sides of the engine audio output.

This is the Synthdef (copied from @okyeron’s engine)

    SynthDef(\MiPlaits, {
      arg out, pitch=60.0, eng=0, harm=0.1, timbre=0.5, morph=0.5, trigger=0.0, level=0, fm_mod=0.0, timb_mod=0.0, morph_mod=0.0, decay=0.5, lpg_colour=0.5, mul=1.0;
      var sound = {
        MiPlaits.ar(pitch,eng,harm,timbre,morph,trigger,level,fm_mod,timb_mod,morph_mod,lpg_colour,mul);
      };
      Out.ar(out, sound);
    }).add;

I imagine it’s something on this line

Out.ar(out, sound);

… but I don’t know what.

Can anyone enlighten me?

1 Like
    SynthDef(\MiPlaits, {
      arg out, pitch=60.0, eng=0, harm=0.1, timbre=0.5, morph=0.5, trigger=0.0, level=0, fm_mod=0.0, timb_mod=0.0, morph_mod=0.0, decay=0.5, lpg_colour=0.5, mul=1.0;
      var primarySound, sound;
      sound = MiPlaits.ar(pitch,eng,harm,timbre,morph,trigger,level,fm_mod,timb_mod,morph_mod,lpg_colour,mul);
      primarySound = sound[0];
      Out.ar(out, [primarySound, primarySound]);
    }).add;

In SuperCollider, stereo sounds are just arrays of mono sounds.

1 Like

Great, thank you @21echoes !

That’s kinda what I thought, actually.

I tried

Out.ar(out, [sound[0], sound[0]]);

though, and the script I was using the engine in hung on load.

This seems to work, anyway:

	SynthDef(\MiPlaits, {
		arg out, pitch=60.0, eng=0, harm=0.1, timbre=0.5, morph=0.5, trigger=0.0, level=0, fm_mod=0.0, timb_mod=0.0, morph_mod=0.0, decay=0.5, lpg_colour=0.5, mul=1.0;
		var sound, primarySound, auxSound;
		sound = MiPlaits.ar(pitch,eng,harm,timbre,morph,trigger,level,fm_mod,timb_mod,morph_mod,lpg_colour,mul);
		primarySound = sound[0];
		auxSound = sound[1];
	  	Out.ar(out, [primarySound, primarySound]);
	}).add;

Now to add a command to switch between the two versions.

I’ve not had much chance to work on this beyond the initial test engine.

It’s be great if we can come up with a nice synth engine lib (or set of libs) with these ugens.

Let me know how we can collaborate on this and not duplicate effort.

I’m definitely interested in adding Clouds and Rings to the Pedalboard engine, but yeah there should probably also be a dedicated MI engine that has all the UGens including the VCO-style modules as well (Pedalboard doesn’t really make sense with the oscillators). Pedalboard may have some inspiration for y’all on how to make it possible to patch the separate modules into eachother in a dynamic order, but I’m between laptops right now and won’t be able to do much actual development for a little while. Happy to help in other ways though! I’d also recommend jah’s R engine which is a full-on modular synthesis environment

4 Likes

I suppose I can PR the pedalboard modules for rings/clouds - or make a branch (?) - for testing and see if you or anyone else has implementation suggestions?

@21echoes OK - I forked pedalboard and added a branch for mi-ugens

2 Likes

Elements interface work/experiment and discovering the UI library.

13 Likes

My use-case is quite specific at the moment.

I have 3x MiPlaits setup as Kick, Snare and Hat, with commands for a sub-set of the parameters for each channel.

I’m happy to share what I’ve made, but it may not be especially useful for anyone else.

Has anyone considered making MI modules (or whatever the term is) for R?

1 Like

Here’s an Engine script for a 3-channel (Kick/Snare/Hat) MiPlaits drum kit.


Engine_BEPlaits : CroneEngine {

  var <kick;
  var <snare;
  var <hat;

  *new { arg context, doneCallback;
    ^super.new(context, doneCallback);
  }

  alloc {

	//////////////////
    // DEFINE SYNTH //
    //////////////////

	SynthDef(\MiPlaits, {
		arg out, pitch=60.0, eng=0, harm=0.1, timbre=0.5, morph=0.5, trigger=0.0, level=0, fm_mod=0.0, timb_mod=0.0, morph_mod=0.0, decay=0.5, lpg_colour=0.5, model=0.0, mul=1.0;
		var sound, primarySound, auxSound, mixed;
		sound = MiPlaits.ar(pitch,eng,harm,timbre,morph,trigger,level,fm_mod,timb_mod,morph_mod,lpg_colour,1.0);
		primarySound = sound[0] * mul;
		auxSound = sound[1] * mul;
        mixed = XFade2.ar(primarySound,auxSound,model);
		Out.ar(out, [mixed, mixed]);
	}).add;

    context.server.sync;

	////////////////
	// Kick Synth //
	////////////////

    kick = Synth.new(\MiPlaits, [
		\out, context.out_b.index,
		\pitch, 60.0,
		\eng, 13,
		\harm, 0.1,
		\timbre, 0.5,
		\morph, 0.5,
		\trigger, 0,
		\level, 1,
		\fm_mod, 0.0,
		\timb_mod, 0.0,
		\morph_mod, 0.0,
		\decay, 0.5,
		\lpg_colour, 0.5,
		\mul, 0.6
      ],
    context.xg);

	// Kick Commands
    this.addCommand("trig_kick", "f", {|msg|
      kick.set(\trigger, 1);
      kick.set(\level, msg[1]);
      SystemClock.sched(0.01, {
        kick.set(\trigger, 0);
      });
    });
	this.addCommand("pitch_kick", "i", {|msg|
	  kick.set(\pitch, msg[1]);
	});
	this.addCommand("harm_kick", "f", {|msg|
	  kick.set(\harm, msg[1]);
	});
	this.addCommand("timbre_kick", "f", {|msg|
	  kick.set(\timbre, msg[1]);
	});
	this.addCommand("morph_kick", "f", {|msg|
	  kick.set(\morph, msg[1]);
	});
	this.addCommand("model_kick", "f", {|msg|
	  kick.set(\model, (msg[1]*2)-1);
	});
    this.addCommand("level_kick", "f", {|msg|
	  kick.set(\mul, msg[1]);
	});

	/////////////////
	// Snare Synth //
	/////////////////

    snare = Synth.new(\MiPlaits, [
		\out, context.out_b.index,
		\pitch, 60.0,
		\eng, 14,
		\harm, 0.1,
		\timbre, 0.5,
		\morph, 0.5,
		\trigger, 0,
		\level, 0,
		\fm_mod, 0.0,
		\timb_mod, 0.0,
		\morph_mod, 0.0,
		\decay, 0.5,
		\lpg_colour, 0.5,
		\mul, 1.0
      ],
    context.xg);

	// Snare Commands
    this.addCommand("trig_snare", "f", {|msg|
      snare.set(\trigger, 1);
      snare.set(\level, msg[1]);
      SystemClock.sched(0.01, {
        snare.set(\trigger, 0);
      });
    });
	this.addCommand("pitch_snare", "i", {|msg|
	  snare.set(\pitch, msg[1]);
	});
	this.addCommand("harm_snare", "f", {|msg|
	  snare.set(\harm, msg[1]);
	});
	this.addCommand("timbre_snare", "f", {|msg|
	  snare.set(\timbre, msg[1]);
	});
	this.addCommand("morph_snare", "f", {|msg|
	  snare.set(\morph, msg[1]);
	});
	this.addCommand("model_snare", "f", {|msg|
	  snare.set(\model, (msg[1]*2)-1);
	});
    this.addCommand("level_snare", "f", {|msg|
	  snare.set(\mul, msg[1]);
	});

	///////////////
	// Hat Synth //
	///////////////

    hat = Synth.new(\MiPlaits, [
		\out, context.out_b.index,
		\pitch, 60.0,
		\eng, 15,
		\harm, 0.1,
		\timbre, 0.5,
		\morph, 0.5,
		\trigger, 0,
		\level, 0,
		\fm_mod, 0.0,
		\timb_mod, 0.0,
		\morph_mod, 0.0,
		\decay, 0.5,
		\lpg_colour, 0.5,
		\mul, 0.6
      ],
    context.xg);

	// Hat Commands
    this.addCommand("trig_hat", "f", {|msg|
      hat.set(\trigger, 1);
      hat.set(\level, msg[1]);
      SystemClock.sched(0.01, {
        hat.set(\trigger, 0);
      });
    });
	this.addCommand("pitch_hat", "i", {|msg|
	  hat.set(\pitch, msg[1]);
	});
	this.addCommand("harm_hat", "f", {|msg|
	  hat.set(\harm, msg[1]);
	});
	this.addCommand("timbre_hat", "f", {|msg|
	  hat.set(\timbre, msg[1]);
	});
	this.addCommand("morph_hat", "f", {|msg|
	  hat.set(\morph, msg[1]);
	});
	this.addCommand("model_hat", "f", {|msg|
	  hat.set(\model, (msg[1]*2)-1);
	});
    this.addCommand("level_hat", "f", {|msg|
	  hat.set(\mul, msg[1]);
	});
  }
  
  free {
    kick.free;
    snare.free;
    hat.free;
  }
}

And the params definitions to control it.

Note “model” parameter crossfades between the two MiPlaits outputs. If you want, you could just pass the engine 0 or 1 to switch between them.

I might experiment with ControlSpec presets for the Pitch param (does that show the MIDI note # in the params list?).

----------------------
  -- Drum Edit Params --
  ----------------------
  
  params:add_separator("drum edit")
  params:add_control("bdp1","bassdrum pitch",ControlSpec.new(0,127,'lin',1,32))
  params:set_action( "bdp1", function(x) engine.pitch_kick(x) end)
  params:add_control("bdp2","bassdrum harmonics",ControlSpec.new(0,1,'lin',0,0.1))
  params:set_action( "bdp2", function(x) engine.harm_kick(x) end)
  params:add_control("bdp3","bassdrum timbre",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "bdp3", function(x) engine.timbre_kick(x) end)
  params:add_control("bdp4","bassdrum morph",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "bdp4", function(x) engine.morph_kick(x) end)
  params:add_control("bdp5","bassdrum model",ControlSpec.new(0,1,'lin',0,0))
  params:set_action( "bdp5", function(x) engine.model_kick(x) end)
  params:add_control("bdlvl","bassdrum level",ControlSpec.new(0,1,'lin',0,0.6))
  params:set_action( "bdlvl", function(x) engine.level_kick(x) end)
  
  params:add_separator()
  params:add_control("sdp1","snaredrum pitch",ControlSpec.new(0,127,'lin',1,32))
  params:set_action( "sdp1", function(x) engine.pitch_snare(x) end)
  params:add_control("sdp2","snaredrum harmonics",ControlSpec.new(0,1,'lin',0,0.1))
  params:set_action( "sdp2", function(x) engine.harm_snare(x) end)
  params:add_control("sdp3","snaredrum timbre",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "sdp3", function(x) engine.timbre_snare(x) end)
  params:add_control("sdp4","snaredrum morph",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "sdp4", function(x) engine.morph_snare(x) end)
  params:add_control("sdp5","snaredrum model",ControlSpec.new(0,1,'lin',0,0))
  params:set_action( "sdp5", function(x) engine.model_snare(x) end)
  params:add_control("sdlvl","snaredrum level",ControlSpec.new(0,1,'lin',0,1))
  params:set_action( "sdlvl", function(x) engine.level_snare(x) end)
  
  params:add_separator()
  params:add_control("hhp1","hat pitch",ControlSpec.new(0,127,'lin',1,32))
  params:set_action( "hhp1", function(x) engine.pitch_hat(x) end)
  params:add_control("hhp2","hat harmonics",ControlSpec.new(0,1,'lin',0,0.1))
  params:set_action( "hhp2", function(x) engine.harm_hat(x) end)
  params:add_control("hhp3","hat timbre",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "hhp3", function(x) engine.timbre_hat(x) end)
  params:add_control("hhp4","hat morph",ControlSpec.new(0,1,'lin',0,0.5))
  params:set_action( "hhp4", function(x) engine.morph_hat(x) end)
  params:add_control("hhp5","hat model",ControlSpec.new(0,1,'lin',0,0))
  params:set_action( "hhp5", function(x) engine.model_hat(x) end)
  params:add_control("hhlvl","hat level",ControlSpec.new(0,1,'lin',0,0.75))
  params:set_action( "hhlvl", function(x) engine.level_hat(x) end)

Might be useful to someone.

1 Like