//Copyright (C) 2006  Nick Collins distributed under the terms of the GNU General Public License


Qitch constant Q transform pitch follower


#freq, hasFreq = Qitch.kr(in, databufnum,ampThreshold, algoflag, ampbufnum, minfreq, maxfreq)

This alternative pitch follower works in the frequency domain rather than Pitch's time domain correlation. Tradeoffs are made between latency and frequency resolution. It is meant to provide a relatively stable resultant tracked pitch based on a harmonic template.  


You must load one of the auxilliary data files into a Buffer and pass the bufnum to the routine. These files take the form:


 QspeckernN1024SR44100.wav

 QspeckernN2048SR44100.wav

 QspeckernN4096SR44100.wav

 QspeckernN8192SR44100.wav

 QspeckernN2048SR48000.wav

 QspeckernN4096SR48000.wav


Only use the one for your output sampling rate. Bigger N means more stability and resolution but longer delay and higher average CPU cost. N= 2048 is a good tradeoff if you don't want to track over 1000Hz fundamentals. The 48000 SR files are untested and provided without any promises. 


in- the audio rate input signal

databufnum- you must provide the initialisation data required by Qitch. This in the form of a .wav file that must be loaded to a buffer (see examples below).  The data files should have come with this UGen. 

ampThreshold- Qitch outputs a 0 in hasFreq if the input amplitude is below this threshold. Same as original Pitch. 

algoflag- 0 means use just the constant Q template pattern matcher. 1 flags the refinement based on the phase estimate.

ampbufnum- you may provide an 11 component buffer with template amplitudes- see example below. 

minfreq- minimum output frequency in Hz

maxfreq- maximum output frequency in Hz


In technical terms, this UGen calculates an FFT, applying Brown and Puckette's efficient constant Q transform on a quartertone scale, base note F3= 174.6Hz. Cross correlation search leads to the best match for a harmonic spectrum grid with falling amplitude components. A further fine tuning takes place based on instantaneous frequency estimation (rate of change of phase) for the winning FFT bin. 


The algorithms are based on the following papers:

 

Judith C. Brown and Miller S. Puckette, 1992, An efficient algorithm for the calculation of a constant Q transform. Journal of the Acoustical Society of America. 92(5); 2698-701.

   

Judith C. Brown, 1992, Musical Fundamental Frequency Tracking Using a Pattern Recognition Method. Journal of the Acoustical Society of America. 92(3); 1394-402.

   

Judith C. Brown and Miller S. Puckette, 1993, A High-Resolution Fundamental Frequency Determination Based on Phase Changes of the Fourier Transform. Journal of the Acoustical Society of America. 94(2); 662-7.

         


Examples: (use headphones!)


s = Server.local;


//assumes data file is in SC home application directory; else provide full path

b = Buffer.read(s, "QspeckernN4096SR44100.wav"); 

//this line is absolutely essential! You must load the data required by the UGen! 


b.numFrames //it's not that much data



(

a= SynthDef("testqitch",{arg infreq=440;

var in, freq, hasFreq, out;

in=SinOsc.ar(infreq);

# freq, hasFreq = Qitch.kr(in, b.bufnum,0.01,1);

Out.ar(0,[SinOsc.ar(freq,0.1),in]);

}).play(s);

)


a.set(\infreq,237);




c=Buffer.read(s,"/Volumes/data/audio/nikkisitar/warmup.wav"); //sitar test file, try anything you have on your disk


c.numFrames


( //sample tracking

SynthDef("pitchFollow1",{

var in, amp, freq, hasFreq, out;

in = PlayBuf.ar(1,c.bufnum, loop:1);

amp = Amplitude.kr(in, 0.05, 0.05);

# freq, hasFreq = Qitch.kr(in, b.bufnum, 0.1,1, -1,261, 800 );

out = Mix.new(VarSaw.ar(freq * [0.5,1,2], 0, LFNoise1.kr(0.3,0.1,0.1), amp));

Out.ar(0,[out,in])

}).play(s);

)



b = Buffer.read(s, "QspeckernN2048SR44100.wav"); //quicker response with this data set


d = Buffer.alloc(s,11); //make an amp template

d.setn(0, [1,0.98,0.92,0.88,0.84,0.8,0.76,0.72,1.68,1.64,1.6]); //must have 11 components


//analogous to example in the Pitch helpfile 

(

SynthDef("pitchFollow1",{

var in, amp, freq, hasFreq, out;

in = Mix.new(AudioIn.ar([1,2]));

amp = Amplitude.kr(in, 0.05, 0.05);

# freq, hasFreq = Qitch.kr(in, b.bufnum, 0.02, 1, d.bufnum, 160, 880);

//freq = Lag.kr(freq.cpsmidi.round(1).midicps, 0.05);

out = Mix.new(VarSaw.ar(freq * [0.5,1,2], 0, LFNoise1.kr(0.3,0.1,0.1), amp));

6.do({

out = AllpassN.ar(out, 0.040, [0.040.rand,0.040.rand], 2)

});

Out.ar(0,out)

}).play(s);

)