Fellow ChucKists,<br><br>There was a question on the forum about tracking the pitch of a Theremin.&nbsp; FFT-based techniques may be the most obvious choice for attempting to track a pitch in general but I felt that such a simple signal could be tracked by some modest UGens. Below you&#39;ll find a class that counts zero crossings over a given duration, tracking them in real time and outputing a value corresponding to the pitch (in Hz) of the input signal. I did in &quot;mock UGen&quot; form because of the tracking ability of the plain oscillators (see usage example below).<br>
<br>Might be useful in some cases or good for amusement value. Thanks to Frostburn for teaching me the OnPole+Delay trick this is based on; turns out it&#39;s good for more then just calculating the average over the last n samples.<br>
<br>Please note; when you see people with a flute next to a modular synth; there is a good reason why they are using a flute and not a more complex signal; this is a very primitive method.<br><br>Yours,<br>Kas.<br><br>//----------------8&lt;-------------------------------------------------<br>
<br><br>//zero-crossing based pitch tracking by Kas.
<br>
//permission granted to copy, redistribute, extend and/or remix.
<br>
//no warranties, no refunds.
<br>
//please mind your speakers and neighbours.
<br>

<br>
PitchTrack p;
<br>

<br>
adc =&gt; <a href="http://p.in">p.in</a>;
<br>
p.out =&gt; SawOsc s =&gt; dac;
<br>

<br>
//make the SawOsc track the PitchTrack&#39;s output.
<br>
0 =&gt; s.freq;
<br>
3 =&gt; s.sync;
<br>

<br>
.10::second =&gt; p.avgTime;
<br>
hour=&gt; now;
<br>

<br>
class PitchTrack
<br>
&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;//primitive pitch tracker based on zero crossings.<br>
&nbsp; &nbsp;//counts the zerocrossings over a certain duration (default is one second)
<br>
&nbsp; &nbsp;//suitable for demonstrating the power of UGens
<br>
&nbsp; &nbsp;//suitable for whistling saw-waves
<br>
&nbsp; &nbsp;//not suitable for complex input signals.
<br>
&nbsp; &nbsp;//however, this will continuously and smoothly (attempt to) track a signal, unlike fft
<br>
&nbsp; &nbsp;
<br>
&nbsp; &nbsp;Gain in =&gt; ZeroX x =&gt; FullRect f =&gt; OnePole avg =&gt; Gain out;
<br>
&nbsp; &nbsp;f =&gt; Delay d =&gt; avg;
<br>

<br>
&nbsp; &nbsp;//make the averaging filter simply stack up all input values
<br>
&nbsp; &nbsp;//here it is counting how many zero crossings it&#39;s seen
<br>
&nbsp; &nbsp;-1 =&gt; avg.a1; 
<br>
&nbsp; &nbsp;1 =&gt; avg.b0;
<br>

<br>
&nbsp; &nbsp;//remove incoming values after a second
<br>
&nbsp; &nbsp;//Thanks to Frostburn for this trick
<br>
&nbsp; &nbsp;5::second =&gt; d.max;
<br>
&nbsp; &nbsp;second =&gt; d.delay;
<br>
&nbsp; &nbsp;-1 =&gt; d.gain;
<br>
&nbsp; &nbsp;
<br>
&nbsp; &nbsp;//compensate for tracking two crossings per cycle
<br>
&nbsp; &nbsp;.5 =&gt; avg.gain;
<br>
&nbsp; &nbsp;
<br>
&nbsp; &nbsp;//========member functions=====================
<br>
&nbsp; &nbsp;
<br>
&nbsp; &nbsp;//sets time to average the input freq over.
<br>
&nbsp; &nbsp;//reaction speed v.s. accuracy
<br>
&nbsp; &nbsp;fun dur avgTime( dur length)
<br>
&nbsp; &nbsp;&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;&nbsp; &nbsp;if (length &lt; samp)
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;&lt;&lt;&quot;warning, duration too short, correcting&quot;&gt;&gt;&gt;;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ms =&gt; d.delay;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}
<br>
&nbsp; &nbsp;&nbsp; &nbsp;if (length &gt; d.max())
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&lt;&lt;&lt;&quot;warning, duration too long, correcting&quot;&gt;&gt;&gt;;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;d.max() =&gt; d.delay;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}
<br>
&nbsp; &nbsp;&nbsp; &nbsp;else length =&gt; d.delay;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;.5 * (second / d.delay() ) =&gt; avg.gain;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;
<br>
&nbsp; &nbsp;&nbsp; &nbsp;return d.delay();
<br>
&nbsp; &nbsp;&nbsp; &nbsp;}
<br>
&nbsp; &nbsp;//returns this time&nbsp; &nbsp;
<br>
&nbsp; &nbsp;fun dur avgTime( dur length)
<br>
&nbsp; &nbsp;&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;&nbsp; &nbsp;return d.delay();
<br>
&nbsp; &nbsp;&nbsp; &nbsp;}
<br>
&nbsp; &nbsp;
<br>
&nbsp; &nbsp;//pretend we are a UGen
<br>
&nbsp; &nbsp;//note this value will start at 0 and we still need a blackhole to operate
<br>
&nbsp; &nbsp;fun float last()
<br>
&nbsp; &nbsp;&nbsp; &nbsp;{
<br>
&nbsp; &nbsp;&nbsp; &nbsp;return avg.last();
<br>
&nbsp; &nbsp;&nbsp; &nbsp;}
<br>
&nbsp; &nbsp;}