MIDI and audio out from Pbind?

I apologize as always if this question has been asked already. I searched and didn’t find anything.

I’m trying to send MIDI data and audio out from a proxy node. Specifically the Pbind lives on the first slot of the proxy:

(
~lead = { | out = 0, freq = 48, relTime = 2 |
	var sig = 0, temp, env, curv;
	out.postln;
	// curv = [\step, \sin, \wel].scramble;
	env = EnvGen.kr(Env.perc(0.5, releaseTime:relTime, curve: \step), doneAction: 2);
	8.do{ | i |
		temp = LFPulse.ar(freq + Rand(0, i), LFPulse.kr(Rand(0, i).round(rrand(0.125, i))).midicps)!2 / 8;
		sig = sig + temp * env * 0.9;
		Out.ar(out, sig * 1/16);
	};
};

~lead[1] = \xset -> Pbind(
	/*	\type, \midi,
		\midiout, m,
		\midicmd, \noteOn,
		\chan, 0,*/
	\dur, Pseq([0.125, 0.5, 1, 2, 0.25, 0.125, 0.125, 0.5, Rest(4), Rest(2), Rest(1)].scramble, inf),
	\degree, Pseq(Scale.hijaz.degrees.mirror.scramble -5, inf),
	\octave, Pwhite(2, 4, inf).round(1),
	\relTime, Pseq([1, 2, 3, 0.5], inf),
);

)

This provides me with a modifiable live-code proxy. I’d like to record both the audio out from the Synth proxy at slot [0] and the have the midi out to Reaper. I’m working on a custom Class that will set up Reaper by destroying and creating all necessary tracks for proxies that I feed it.

But my main question is having two Pbinds work in tandem - meaning everything from the audio generated event stream gets also sent to MIDI. Maybe Ppar, but that doesn’t seem correct either.

Eventually for my Reaper setup class, I’ll need also to get the \type of any proxy and if is type \midi use that to set a MIDI track for recording inside of Reaper. I think I can manage that part but first need to just get audio and MIDI out. The idea is of course to have audio stems using SC for synths, but also having the midi data available for other uses in a DAW.

Oh, and I tried Pchain, but that didn’t seem to work with a proxy slot. Once again needing environment variables - I was trying for less setup and am thinking of going back to Ndefs.

Be Well Everyone!

1 Like

For instance, this did not work in ProxySpace:

Using patterns for sending MIDI events

MIDIClient.init;

m = MIDIOut(0);  // Linux users: MIDIOut(0, MIDIClient.destinations[0].uid)

a = Pbind(\degree, Prand([1, 2, 3, [0, 5]], inf), \bend, Pwhite(0, 76, inf));

// chain a midi event into the pattern and play it (see Pchain)

(a <> (type: \midi, midiout: m)).play;

But also does not explicitly say that it is both Audio and MIDI. However, I assume there is way to have a MIDI Pbind for output mirror/follow the main one for Audio/Synth output.

Yes, this has come up in several variants. Maybe easiest to define an Event type that handles the doubling. Syncing might be necessary to do by a rough estimation. But there are different possibilities. Late here, so I can only give links.

1 Like

It’s covered partly in some other threads, but they are rather long and winding :slight_smile:

Here’s the design pattern I use for splitting a single event stream in two.

  1. Use one upstream Pbind to supply the “base” events.
  2. Use Pchain or the <> operator to compose this into a Ppar containing each of the “split” event streams you want to produce - in your case, one for MIDI and one for synths. These patterns will take the base events as an input, but can override values in them to e.g. change the \type to \midi, or change or add other arguments.
  3. Use PtimeClutch to ensure I only pull one “base” event at any given time.
(
Pdef(\midi, Pbind(
	\type, 		\midi,
	\midiout, 	MIDIOut.newByName("Digital Piano", "Digital Piano"),
	\midicmd, 	\noteOn,
	\chan, 		0,
));
Pdef(\synth, Pbind(
	\instrument, \default
));
Pdef(\baseEvents, Pbind(
	\dur, 1/4,
	\degree, Pseq([0, -3, -1, 2, -1], inf),
	\legato, Pseg([0.5, 2, 0.5], [4, 4]).repeat
));

Pdef(\pattern,
	Ppar([
		Pdef(\midi),
		Pdef(\synth)
	])
	<> Pdef(\baseEvents)
).play;
)

There’s a catch here: EACH pattern in your Ppar will pull one event from the upstream pattern, and they will be played at the same time. So, for example, in the above your \midi pattern will get \degree, 0 and your \synth pattern will get \degree, -3. This is probably not the intended result: if the two patterns in your Ppar are playing at the same time, you probably want them to use the SAME event.

To fix this: you can use PtimeClutch to ensure that when two items are requested at the same clock time from a pattern, the same value will be provided, and only one value from the pattern will be consumed. The final working pattern would then look like this:

Pdef(\pattern,
	Ppar([
		Pdef(\midi),
		Pdef(\synth)
	])
	<> PtimeClutch(Pdef(\baseEvents))
).play;

I don’t think this will work if you intend to plug your pattern Pdef(\pattern) directly into an Ndef (Ndef messes with your events before it plays them, and I think it would disrupt MIDI events). I usually solve this by just creating an “empty” Ndef, defining a Pdef separately, and pointing it to the Ndef. For example:

Ndef(\synth, { DC.ar([0, 0]) }).play; // two channels, silent
Pdef(\synth, Pbind(
	\instrument, \default,
	\out, Ndef(\synth).bus, \group, Ndef(\synth).group,
));
3 Likes

Thanks @scztt! This is exactly what I believe I’m looking for. I’ll give this a shot later, but thank you much for the detailed explanation and examples, as well as your PtimeClutch source file. Very much appreciated. I love this forum so much :slight_smile:

And sorry again if this is redundant to other questions, but this is very concise.

1 Like