PbindFx MIDI Help

hey, i would like to control a delay/echo effect inside of a pattern and was trying to use PbindFx from the miSCellaneous_lib for this instance and also was investigating in Pfxb.

its working fine with the SynthDef \source from the helpfile:
(
PbindFx([
\instrument, \source,
\dur, 1/2,
\amp, 0.01,
\midinote, Pseq([76, 83, 84, 79]-12, inf).trace,

		\fxOrder, 1,
        \decayTime, Pwhite(0.2, 2),
        \cleanupDelay, Pkey(\decayTime)
	],[
        \fx, \echo,
        \echoDelta, 0.2,
        \decayTime, 2,
		\cleanupDelay, Pkey(\decayTime)
]).play(t, quant:1);
)

but i would like to insert a Pattern which controls midi like this:

(
x = Synth.new(\dpo_1, [\amp, 0.2, \out, ~bus[\reverb]], ~mainGrp);

PbindFx([
		\type, \midi,
		\dur, 1/2,
		\midicmd, \noteOn,
		\midiout, m,
		\chan, 0,
		\midinote, Pseq([76, 83, 84, 79], inf),
				
		\fxOrder, 1,
        \decayTime, Pwhite(0.2, 2),
        \cleanupDelay, Pkey(\decayTime)
	],[
        \fx, \echo,
        \echoDelta, 0.2,
        \decayTime, 2,
		\cleanupDelay, Pkey(\decayTime)
]).play(t, quant:1);
)

when i do this i get the following error:
*** ERROR: SynthDef default not found
FAILURE IN SERVER /s_new SynthDef not found
FAILURE IN SERVER /n_set Node 11627 not found

i think i dont understand how i sequence MIDI, get audio back via SoundIn and put fx on the soundIn audio but with the ability to control the fx arguments with a pattern connected to the sequencing pattern. can someone help?

there seems to be a problem with soundin
when im using fx Synthdefs with:

var sig;
sig = In(sig, 2);

INPUT ERROR in building an instance of nil:`
in the Synthdef from the Help file you find

var inSig = In.ar(in, 2);

where is the difference?

Hi,

sorry to say that I’m not too optimistic that this will work, at least not out of the box. PbindFx was definitely not designed to work with midi, it was hard to make it work in the context of SC itself with delay times, cleanup etc., midi brings timing inaccuracy in addition, that would probably inhibit reliable working.
Besides of that PbindFx uses a dedicated event type, so it cannot be used with an arbitrary one (there were special cases in the past where this could be circumvented, but I don’t have them at hand).
What might be possible though is what you’re mentioning: routing audio back to fx or src, for this PbindFx has a special convention (‘otherBusArgs’), it is described in the (admitted: annoyingly long) help file, Exs. 6a and 6b. See also point (3) of the Conventions list, it explains the reason for the error message.
With the rerouted signal any fx processing could be done though, but syncing might be demanding. I won’t have time to look into that during the next days, but keep the thing in mind.

Best

Daniel

Hey,
thanks for your reply.
I was looking at “otherBusArgs” in the annoying long helpfile :wink:
the external audio is set to hardware inputbus in=2 in the corresponding SynthDef.
I tried to set \otherBusArgs, 2 in the PbindFx (maybe a bit too optimistic).
So im really looking forward to some further insights on PbindFx.

See the example, you’d have to set ‘otherBusArgs’ to an array with names of the arguments, like

    \otherBusArgs, [\modIn],

I’ll try a midi example when I have time a few days from now.

hey, thanks so much. Im trying my best with the helpfile in the meantime.

BTW what device are you going to control with midi ? If it would be a VST instrument it would probably work nicely with Christof’s VSTPlugin class – unfortunately its use with patterns seems to be broken in the recent version ( VSTPlugin Event Pattern examples broken in v0.3.1? ), but I’m hoping for an easy fix. Otherwise Jack – or do you have a hardware device ?

its a modular synthesizer

Ok, I’ll think about it. I suppose the first thing would be to measure / estimate the maximum latency / release deviation and derive suited cleanup times. Just a vague idea yet, late here.

I have verified that this works as expected with re-routing, though there are some limitations and possible pitfalls. This example is difficult, it needs some pattern tricks like data sharing, lagging according to MIDI latency is also necessary, fxs with delay demand further care …

It is related to some examples with VSTPlugin and PbindFx that I’m intending to post in the next days.

I have tested this one on a setup with a MIDI piano. If the device has only one audio out you are limited to non-overlapping events, otherwise the fx processing cannot be separated (however fx-processed events, like with long echo tails, can overlap, this is done by PbindFx automatically). Hence I have taken a low legato param in the midi pattern and not too short durations.

I measured a MIDI latency of ca 0.24 sec, so 40 ms above standard OSC latency. This value is needed for lagging the re-routing synths.

// MIDI init
(
MIDIClient.init;
// insert your MIDI device data here
~midiout = MIDIOut.newByName("MT4", "Port 1");
CmdPeriod.add( { (0..15).do { |i| ~midiOut.allNotesOff(i) } });
)


// routing and fx SynthDefs
(
SynthDef(\routeBackExternal, { |out = 0, routeBack, totalDur = 1, att = 0.01, rel = 0.01|
	var src = In.ar(routeBack, 2);
	var env = EnvGen.ar(Env.linen(att, totalDur - att - rel, rel), doneAction: 2);
	OffsetOut.ar(out, src * env);
}).add;

// echo fx, always unified delay maxEchoDelta

SynthDef(\echo, { |out, in, maxEchoDelta = 0.2, echoDelta = 0.1,
    decayTime = 1, amp = 1, mix = 1|
    var sig, inSig = In.ar(in, 2);
    sig = DelayL.ar(
        CombL.ar(inSig, maxEchoDelta, echoDelta, decayTime, amp),
        maxEchoDelta,
        maxEchoDelta - echoDelta
    );
    Out.ar(out, (1 - mix) * inSig + (sig * mix));
}).add;


// wah-wah fx

SynthDef(\wah, { |out, in, resLo = 200, resHi = 5000,
    cutOffMoveFreq = 0.5, rq = 0.1, amp = 1, mix = 1|
    var sig, inSig = In.ar(in, 2);
    sig = RLPF.ar(
        inSig,
        LinExp.kr(LFDNoise3.kr(cutOffMoveFreq), -1, 1, resLo, resHi),
        rq,
        amp
    ).softclip;
    Out.ar(out, (1 - mix) * inSig + (sig * mix));
}).add;
)


(
// groups needed for proper order of synths
~srcGroup = Group.new;
~fxGroup = Group.after(~srcGroup);
)

// ATTENTION: your setup might require other SoundIn args !
// you might get feedback if SoundIn refers to your mic !

(
// route audio from external device to dedicated bus
~bus = Bus.audio(s, 2);
~toBus = { Out.ar(~bus, SoundIn.ar([0, 1])) }.play(~srcGroup);
)



// timing has to be done in the midi pattern
// but also fxOrder needs to be determined here for lagging
(
p = Pbind(
	\type, \midi,
	\midinote, Pwhite(25, 80) + Prand([0, [0, 7], [0, 12]], inf),
	\amp, 1,
	// partial application: short for .collect { |x| ~dur = x }
	\dur, Prand([0.6, 0.3, 0.3], inf).collect(~dur = _),
	\fxOrder, Pseq([0, 1, 2, [2, 1]], inf).collect(~fxOrder = _),
	// lag if echo involved !
	\lag, (Pfunc { |e| e.fxOrder.asArray.includes(2).if { 0 }{ 0.1 } }).collect(~lag = _),

	\midiout, ~midiout,
	\chan, 0,
	\legato, 0.05
);

q = PbindFx([
		\instrument, \routeBackExternal,
		\routeBack, ~bus,
		// exceptional bus reading
		\otherBusArgs, [\routeBack],

		// take over data from midi master
		\dur, Pfunc { ~dur },

		// need to subtract because of echo as we only have one channel !
		\totalDur, Pkey(\dur) - 0.1,

		// additional lag because of MIDI latency
		\lag, Pfunc { ~lag } + 0.04,
		\group, ~fxGroup,

		// envelope for re-routed audio for safety
		\att, 0.01,
		\rel, 0.01,

		\fxOrder, Pfunc { ~fxOrder },
		\cleanupDelay, Pkey(\dur) + 0.1
    ],[
        // variation by sequencing of params
        \fx, \wah,
        \mix, Pseq([0.2, 0.5, 0.7], inf),
        \cutOffMoveFreq, Pseq([1, 2, 5, 10], inf),
        \cleanupDelay, 0.01
    ],[
        \fx, \echo,
        \echoDelta, 0.075,
        \maxEchoDelta, 0.1,
        \decayTime, Pwhite(1.0, 2),
        \cleanupDelay, Pkey(\decayTime)
    ]
);

// delay for data sharing
x = Ptpar([0.0, p, 0.0001, q]).play
)

// stop and cleanup
x.stop

~toBus.free

Hope this helps, best

Daniel

thank you so much. i will try to setup everything and keep you posted about my results :slight_smile:

Here’s a improved version regarding echo - which is a bit more complicated also …
In the previous version the echo delay time was subtracted from the source event time (forcing faded shortening of events). This can be circumvented by doing another routing of the delayed re-routed signal to alternating buses. You can define more buses for allowing more overlaps with longer delay times. This idea will be also used in the VSTPlugin + PbindFx examples that I’ll post later on. However the limitation which remains is the possible overlapping of incoming events, you’d have to either avoid or accept it or read different incoming events from different audio channels (if this is possible at all in your setup).

(
MIDIClient.init;
// insert your MIDI device data here
~midiout = MIDIOut.newByName("MT4", "Port 1");
CmdPeriod.add( { (0..15).do { |i| ~midiout.allNotesOff(i) } });
)


// routing and fx SynthDefs
(
SynthDef(\routeBack_1, { |out = 0, inBus, delay = 0, totalDur = 1, att = 0.01, rel = 0.01|
	var src = In.ar(inBus, 2);
	var env = EnvGen.ar(Env([0, 0, 1, 1, 0], [delay, att, totalDur - att - rel, rel]), doneAction: 2);
	OffsetOut.ar(out, DelayL.ar(src, delay, delay) * env);
}).add;

SynthDef(\routeBack_2, { |out = 0, inBus, totalDur = 1, att = 0.01, rel = 0.01|
	var src = In.ar(inBus, 2);
	var env = EnvGen.ar(Env([0, 1, 1, 0], [att, totalDur - att - rel, rel]), doneAction: 2);
	OffsetOut.ar(out, src * env);
}).add;

// echo fx, always unified delay maxEchoDelta

SynthDef(\echo, { |out, in, maxEchoDelta = 0.2, echoDelta = 0.1,
    decayTime = 1, amp = 1, mix = 1|
    var sig, inSig = In.ar(in, 2);
    sig = DelayL.ar(
        CombL.ar(inSig, maxEchoDelta, echoDelta, decayTime, amp),
        maxEchoDelta,
        maxEchoDelta - echoDelta
    );
    Out.ar(out, (1 - mix) * inSig + (sig * mix));
}).add;


// wah-wah fx

SynthDef(\wah, { |out, in, resLo = 200, resHi = 5000,
    cutOffMoveFreq = 0.5, rq = 0.1, amp = 1, mix = 1|
    var sig, inSig = In.ar(in, 2);
    sig = RLPF.ar(
        inSig,
        LinExp.kr(LFDNoise3.kr(cutOffMoveFreq), -1, 1, resLo, resHi),
        rq,
        amp
    ).softclip;
    Out.ar(out, (1 - mix) * inSig + (sig * mix));
}).add;
)


(
// groups needed for proper order of synths
~routeBackGroup_1 = Group.new;
~routeBackGroup_2 = Group.after(~routeBackGroup_1);
~fxGroup = Group.after(~routeBackGroup_2);
)

// ATTENTION: your setup might require other SoundIn args !
// you might get feedback if SoundIn refers to your mic !

(
// route audio from external device to dedicated bus
~routeBackBus_1 = Bus.audio(s, 2);
~toRouteBackBus_1 = { Out.ar(~routeBackBus_1, SoundIn.ar([0, 1])) }.play(~routeBackGroup_1);

// buses to dispatch re-routed signal
~overlapNum = 3;
~routeBackBuses_2 = { Bus.audio(s, 2) } ! ~overlapNum;
)



// timing has to be done in the midi pattern

(
p = Pbind(
	\type, \midi,
	\midinote, Pwhite(25, 80) + Prand([0, [0, 7], [0, 12]], inf),
	\amp, 1,
	// partial application: short for .collect { |x| ~dur = x }
	\dur, Prand([0.6, 0.3, 0.3], inf).collect(~dur = _),

	\midiout, ~midiout,
	\chan, 0,
	\legato, 0.05
);

// fxOrder needs to be determined here for delaying
q = Pbind(
	\instrument, \routeBack_1,
	\dur, Pfunc { ~dur },
	\inBus, ~routeBackBus_1,
	\count, Pseq((0..~overlapNum-1), inf),
	\out, (~routeBackBuses_2[0].index + (Pkey(\count) * 2)).collect(~out = _),
	\fxOrder, Prand([0, 2, 1, [2, 1]], inf).collect(~fxOrder = _),

	\delay, (Pfunc { |e| e.fxOrder.asArray.includes(2).if { 0 }{ 0.2 } }).collect(~delay = _),

	// envelope for re-routed audio for safety
	\att, 0.01,
	\rel, 0.01,

	// lag because of MIDI latency
	\lag, 0.04,

	\totalDur, Pkey(\delay) + Pkey(\dur),
	\group, ~routeBackGroup_2
);

r = PbindFx([
		\instrument, \routeBack_2,
		// read frfom alternating buses
		\inBus, Pfunc { ~out },

		// exceptional bus reading
		\otherBusArgs, [\inBus],

		// take over data from midi master
		\dur, Pfunc { ~dur },

		\totalDur, Pkey(\dur) + Pfunc { ~delay },

		// envelope for re-routed audio for safety
		\att, 0.01,
		\rel, 0.01,

		// lag because of MIDI latency
		\lag, 0.04,

		\group, ~fxGroup,

		\fxOrder, Pfunc { ~fxOrder },
		\cleanupDelay, Pkey(\totalDur)
    ],[
        // variation by sequencing of params
        \fx, \wah,
        \mix, Pseq([0.2, 0.5, 0.7], inf),
        \cutOffMoveFreq, Pseq([1, 2, 5, 10], inf),
        \cleanupDelay, 0.01
    ],[
        \fx, \echo,
        \echoDelta, 0.075,
        \maxEchoDelta, 0.2,
        \decayTime, Pwhite(1.0, 2),
        \cleanupDelay, Pkey(\decayTime)
    ]
);

// delay for data sharing
x = Ptpar([0.0, p, 0.0001, q, 0.0002, r]).play
)

// stop and cleanup
x.stop

~toRouteBackBus_1.free