Using Pitch.kr to update a Pattern

Hi everyone,

n00bish question. Some background:
I am working on a piece that plays back shuffled pentatonic scales and transposes them around. I have the pattern stuff all worked out, but I’d love to take it to the next level and hook up an instrument: specifically, play my saxophone, read the pitch from that, and use it as a root note of a scale, updated as I play new notes.

Pitch.kr is simple enough; I can write a SynthDef that spits out the nearest midinote to what I’m playing into the microphone, but I don’t know how to get that number into my pattern in real time, or with a short delay.
This Synth shows a constant flow of the current midi note in the post window:

(        
SynthDef(\pitchFollow, { |out|
	var in, amp, freq, midinote, hasFreq;
	in = Mix.new(SoundIn.ar([0, 1]));
	amp = Amplitude.kr(in, 0.05, 0.05);
	# freq, hasFreq = Pitch.kr(in, ampThreshold: 0.1);
	midinote = freq.cpsmidi.round(1);
	midinote.poll;
	};
).play;
)

Can I get the output of that Synth to control a version of the following pattern, replacing ~root? (with a PatternProxy or something like that…?):

~root = 60;

(
Pdef(\test,
	Pbind(		
		\midinote, ~root + Prand([0, 2, 4, 7, 9], inf), 
		\dur, 0.5,
	)
).quant=1;
)

I know I can’t use a UGen inside a pattern. Can I turn the output of the Synth into some kind of stream? Can I control how often the Synth or the Pattern checks the current note? i.e. every 0.1 seconds, check the midinote from the mic, then feed that into the pattern (using Pbind so as not to interrupt rhythms).
Pfunc? Pfuncn? Prout?

I’m sure this is a piece of cake for all the wizards out there - thanks in advance.

Hello and welcome,

Yes, play the value from the Synth to a control bus, then in the Pbind do

~bus = Bus.control(s, 1);

Pbind(
	...
	\midinote, Pfunc { ~bus.getSynchronous } + Prand( ... ),
	...
)

That would check in the moment of event evaluation and is – up to the limit hardware buffer size – synchronous, which should serve for most needs.
BTW I’d take Tartini from SC plugins instead of Pitch which is more accurate.

Thank you!

I’ll check out Tartini too. Thanks for the tip.

Glad to discover there’s an easy way to do this - really appreciate the help.

Just comes to my mind: by default Pbind is played with a latency of 0.2 sec (s.latency = … or within Pbind). You might want to set this to 0.05, which is pretty secure. You could also test and maybe reduce to 0.03 or so.

Ah good call didn’t know Pbind had a built in default latency. Cheers

Wanted to follow up with a simple Trigger question. (Tartini works great btw.)

In the SynthDef I’m using to feed midinotes into a Pattern, I want to filter out some noise. Specifically, I don’t want to register new midinotes when the ‘hasFreq’ (i.e. confidence of pitch follower) is less than 0.9, so that only “real notes” change the output. I have this right now, which works but has a problem:

(
SynthDef(\pitchFollow2, { 
    arg out = ~bus;
	var in, amp, freq, sig, midinote=60, hasFreq=0;
	in = Mix.new(SoundIn.ar([0, 1]));
	amp = Amplitude.kr(in, 0.05, 0.05);
	# freq, hasFreq = Tartini.kr(in);

	//hasFreq.poll;

	sig = (hasFreq > 0.9)*freq;
	midinote = sig.cpsmidi.round(1);
	//midinote.poll;

	Out.kr(out, midinote);
	};
).play;
)

The problem is that when I stop playing/singing into the mic, the midinotes stop flowing into the pattern. I thought this would be simple enough to fix with a trigger, but I see that Trig.kr constantly outputs 0 when signal isn’t coming in, which defeats the purpose. Then I thought I could use Select.kr to mimic an if statement in the SynthDef, but that didn’t work either:

midinote = Select.kr(hasFreq > 0.9, [midinote, freq.cpsmidi.round(1)])

I thought that the above would preserve the last note played until I play a new note, at which point it would update. But I had a similar bad result: when I stop playing, midinote just defaults to 60.

Is there something I can use besides Trig that will keep track of whatever the last note I play, continuously output that even when the note ends, but update to a new note as soon as I play it? I suppose I could try to do this in the pattern downstream from the SynthDef, but it seems like the SythDef should be able to do this with a smarter approach. (I guess my problem really is that you can’t do if statements in a SynthDef and I haven’t figured out how to get around that in a SuperCollider-kosher way.)

Thanks

you could use Gate with the Amplitude already in your code:

midinote = Gate.kr (in: freq.cpsmidi.round(1), 
    trig: (amp > 0.05) * (hasFreq > 0.9));

I think it’s a combined issue: first the bus assignment as arg (plays to kr bus 0). Then you can use Latch for sample and hold.

I got this working with headphones and Pitch, though not with Tartini. No idea why ATM and no time to look into it, maybe someone can chime in.

(
~bus = Bus.control(s, 1);

SynthDef(\pitchFollow2, { arg out;
	var in, amp, freq, sig, midinote=60, hasFreq=0, trig=1;
	in = Mix.new(SoundIn.ar(0));
	amp = Amplitude.kr(in, 0.05, 0.05);
	#freq, hasFreq = Pitch.kr(in);

	trig = hasFreq > 0.9;
	sig = Latch.kr(freq, trig).poll;

	midinote = sig.cpsmidi.round(1).clip(20, 90);
	midinote.poll;

	Out.kr(out, midinote);
}).add
)



x = Synth(\pitchFollow2, [out: ~bus]);


(
p = Pbind(
	\dur, 0.3,
	\do, Pfunc { ~bus.getSynchronous.postln },
	\midinote, Pkey(\do)
).trace.play
)


p.stop

x.free

Ah yes of course, Gate does the trick!

Thanks for this. Hadn’t seen Latch before - useful tool.