//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);
)