MM – Stochastic Bell Clouds

Here we load a single triangle hit sample then we play with Pbinds to create clouds.

It shows how you can manipulate and randomize various parameters.

You can find the triangle sample here.

https://drive.google.com/open?id=14F-Kz5JNoYCel_N0Xl7BmuFtTC4y6hr-

EDIT: Implemented MIDI keyboard. Pretty cool stuff!!

The MIDI implementation plays a Pbind.
– Notes control clouds overall heights. Naturally you can play multiple clouds (poly)
– cc1 controls density
– Bend controls cloud height too (can’t avoid) and negative values plays the bell instances backwards.

// "STOCHASTIC BELL CLOUDS" (Fun with Pbinds) - 2020 Matias monteagudo

//Here you can set your interface options.

(
Server.local.options.device = "ASIO : ASIO PreSonus FireStudio";
Server.local.options.sampleRate=48000;
Server.local.options.hardwareBufferSize=128;
Server.local.options.maxNodes=2048; //Lower this to 1024 in case you're overloading your core.
)

//Add reverb to start with. Makes everything more pleasant ;-)

((
SynthDef(\FreeVerb2x2, { |out, mix = 0.25, room = 0.15, damp = 0.5, amp = 1.0|
    var signal;

    signal = In.ar(out, 2);

    ReplaceOut.ar(out,
        FreeVerb2.ar( // FreeVerb2 - true stereo UGen
            signal[0], // Left channel
            signal[1], // Right Channel
            mix, room, damp, amp
        )
    );

}).add;
);

z = Synth(\FreeVerb2x2, [\outbus, 0,\room,1], addAction:\addToTail);
)

//First we need to laod the sample in a buffer. We need to give the buffer a name ("c" in this case) to be able to reference it in our SynthDef.
c=Buffer.read(s,"C:/Audio/VCV/Mat VCV/MM Realtime Chariots 1.45/Triangle.wav")

c.query
//The sample has a total number of 67618 frames.

//Here we can test our sample.
c.play

//Our single bell SynthDef.
(
SynthDef(\bell,{
	| trig=1,amp=1,fq=1,pos=0,out |
	var sig;
	sig=PlayBuf.ar(2,c,fq,trig,pos,doneAction:2); //Here "doneAction: 2" is important to free the node after is finished. Will avoid having "too many nodes" later on.
	Out.ar(0, sig*amp)
}).add;
)

//Here we can test our SynthDef.
Synth(\bell)

// PBinds. Use the "x.stop" lines to stop them without killing the reverb.

//Here we vary the height on each node. Note the +0.1 offset in Prand just to transpose everything (optional)
x=Pbind(\instrument, \bell,\trig,1,\amp,0.6,\fq, Prand([0.94,0.96,0.98,1,1.02,1.04,1.06]+0.1, inf), \dur, 0.1).play;
x.stop

// This one has more density, has also a different "startpos" (\pos) on each node to avoid having the same attack.
x=Pbind(\instrument, \bell,\trig,1,\amp,0.6,\fq, Prand([0.94,0.96,0.98,1,1.02,1.04,1.06]+0.1, inf), \pos, Prand([2000, 10000, 20000,30000,40000], inf),\dur, 0.02,).play;
x.stop

//More density. This one also varies the amplitudes. Note that until here the cloud stays "static" within a pitch range.
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Prand([0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]-0.2,inf), //Note the -0.2 to offset the volume at will. We could also use Pwhite instead of Prand.
	\fq, Prand([0.94,0.96,0.98,1,1.02,1.04,1.06]+0.1, inf),
	\pos, Prand([2000, 10000, 20000,30000,40000], inf),
	\dur, 0.003,).play;
)

x.stop

//Here we use Pwhite for \amp  and \fq (just to show how is done)
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Pwhite(0.2,1,inf),
	\fq, Pwhite(0.94,1.06,inf)+0.1,
	\pos, Prand([2000, 10000, 20000,30000,40000], inf),
	\dur, 0.003,).play;
)

x.stop

//Moving "x" (\fq) using a sequence containing two Pseries. This to move the cloud's pitch as a whole.
(
~fq=Pwhite(0.94,1.06,inf); //Here we define our initial Pwhite for \fq as a variable outside Pbind. Note the variable with tilde "~"
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Pwhite(0.2,1,inf),
	\fq, ~fq+Pseq([Pseries(0.05,0.0004,500), Pseries(0.2,-0.0004,500)],inf),//Here, note how two Pseries are in sequence inside Pseq to produce an upwards/downwards movement.
	\pos, Prand([2000, 10000, 20000,30000,40000,50000], inf),
	\dur, 0.003,).play;
))

x.stop


//Second approach: This examples uses PSinOsc (external) to modulate "x"
(
~fq=Pwhite(0.94,1.06,inf);
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Pwhite(0.2,1,inf),
	\fq, ~fq+PSinOsc(1000,0,0.2,0,inf),//Here.
	\pos, Prand([2000, 10000, 20000,30000,40000,50000], inf),
	\dur, 0.003,).play;
))

x.stop

//Third Approach: This one concatenates two Pseq to modulate "x" (Reddit rocks!)
(
~fq=Pwhite(0.94,1.06,inf);
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Pwhite(0.2,1,inf),
	\fq, ~fq+Pseries(0.01, Pseq(0.001!500++(-0.001!500), inf), inf),//Here, note how two Pseq are concatenated with "++"
	\pos, Prand([2000, 10000, 20000,30000,40000,50000], inf),
	\dur, 0.003,).play;
))

x.stop

//Yet another way. This time using ".." to set a range which is pretty convenient.(Reddit rocks!)
(
~fq=Pwhite(0.94,1.06,inf);
(
x=Pbind(\instrument, \bell,\trig,1,
	\amp, Pwhite(0.2,1,inf),
	\fq, ~fq+Pseq((0.2,0.201..0.8).mirror,inf),//Here, starting at 0.2, increments of 0.001 and an upper limit of 0.8 then mirror.
	\pos, Prand([2000, 10000, 20000,30000,40000,50000], inf),
	\dur, 0.003,).play;
))

x.stop

Pseq((0.2,0.25..0.8).mirror.postln); //Here a visual example of what's going on. This time with increments of 0.05 for easy reading.

//Connect MIDI devices. (0,6) is my keyboard under a virtual MIDI cable. You probably need to evaluate only "MIDIIn.connectAll"
MIDIClient.init;
MIDIIn.connect(0,6);
MIDIIn.disconnect(0,6);
MIDIIn.connectAll;

//MIDI Implementation (all in one). Here we can play a Pbind using a MIDI keyboard and define the overall height and volume of the cloud. Pretty cool stuff!
//cc1 controls the density (\dur) and the pitch bend wheel controls height, set the pitch bend to negavite values and you'll get bells playing backwards!!!
(
//Our buffer
c=Buffer.read(s,"C:/Audio/VCV/Mat VCV/MM Realtime Chariots 1.45/Triangle.wav");
//The Synth, note "bend" is implemented.
(
SynthDef(\bell,{
	| trig=1,amp=1,fq=1,pos=0, pan=0.5, bend, out |
	var sig,span;
	sig=PlayBuf.ar(2,c,fq+bend,trig,pos,doneAction:2);
	span=Pan2.ar(sig.sum,pan);
	Out.ar(0, span*amp)
}).add;
);

~notes = Array.newClear(128);
~liftednotes = Array.newClear(128);
~pedaldown = 0;
~cc1= 0.05;
~bend = 8192;

MIDIdef.noteOn(\noteOn, {
	arg vel, nn, chan, src;
	if(~notes[nn] != nil){
		~notes[nn].set(\trig, 0); ~notes[nn] = nil
	};

	~notes[nn] = Pdef(\x, Pbind(\instrument, \bell,\trig,1, //Here we need to wrap Pbind into a Pdef to be able to modify its pattern values.
	\amp, Prand([0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]*vel/1000,inf),
	\fq, Prand([0.94,0.96,0.98,1,1.02,1.04,1.06]+~bend.linlin(0, 16383, -0.5, 0.5)*nn.midicps/400, inf),
	\pan, Pwhite(-1,1,inf),
	\pos, Prand([2000, 10000, 20000,30000,40000], inf),
	\dur,~cc1.linlin(0, 127, 0.05, 0.003))).play;

});

MIDIdef.noteOff(\noteOff, {
	arg vel, nn;
	if(~pedaldown == 127) {
		~liftednotes[nn] = ~notes[nn];
	}
	{//else if pedal is up:
		~notes[nn].stop;
		~notes[nn] = nil;
	}
} );

MIDIdef.cc(\cc1, {
	arg val, chan, src;
	['ModWheel', val].postln;
	~cc1=val;
	~notes.do(Pbindef(\x, \dur,~cc1.linlin(0, 127, 0.05, 0.003)));//Note the use of Pdef on NoteOn BUT Pbindef here to modify \dur
},ccNum:1, chan: 0);

MIDIdef.bend(\bend, {
	arg val, chan, src;
	['bend', val, chan, src].postln;
	~bend = val;
	~notes.do{arg synth; synth.set(\bend, val.linlin(0, 16383, -0.5, 0.5))};
}, chan: 0);


MIDIdef.cc(\pedal,{
	arg val, key;
	['pedal',val].postln;
	if(key ==64) {
	~pedaldown = val;
	if(val == 0) {
		~liftednotes.do{arg pbind;pbind.stop; pbind = nil;
			}
	};
	}
},ccNum:64);
)
Author
554 PM
  • Platform:
  • Category: Sound Synthesizer
  • Revision: 1.1
  • License: GNU General Public License family
  • Views: 922
  • Modified: 2 years ago
Chat about this patch on Discord!
Appreciate 1

Leave a Reply