Skip to content

Commit 3837a12

Browse files
committed
Added zero-crossing counter pitch detector. TO BE TESTED: filer stages, TO BE ADDED: frequency to MIDI
1 parent f59210b commit 3837a12

2 files changed

Lines changed: 174 additions & 0 deletions

File tree

LibSource/PitchDetector.hpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#ifndef __PitchDetectors_hpp__
2+
#define __PitchDetectors_hpp__
3+
4+
#include "FloatArray.h"
5+
6+
class ZeroCrossingPitchDetector{
7+
private:
8+
BiquadFilter *filter;
9+
int numLowPassStages;
10+
int numHighPassStages;
11+
FloatArray counts;
12+
FloatArray filterOutput;
13+
float samplingRate;
14+
public:
15+
ZeroCrossingPitchDetector(): samplingRate(48000), numLowPassStages(1), numHighPassStages(0){
16+
init(0);
17+
};
18+
ZeroCrossingPitchDetector(float aSamplingRate, int aMaxBlocksize):numLowPassStages(1), numHighPassStages(0){
19+
samplingRate=aSamplingRate;
20+
init(aMaxBlocksize);
21+
};
22+
ZeroCrossingPitchDetector(float aSamplingRate, int aMaxBlocksize, int aNumLowPassStages, int aNumHighPassStages){
23+
samplingRate=aSamplingRate;
24+
init(aMaxBlocksize);
25+
}
26+
~ZeroCrossingPitchDetector(){
27+
FloatArray::destroy(counts);
28+
FloatArray::destroy(filterOutput);
29+
BiquadFilter::destroy(filter);
30+
}
31+
void init(int aMaxBlocksize){
32+
setMaxBlocksize(aMaxBlocksize);
33+
counts=FloatArray::create(10); //number of zcc to be averaged
34+
counts.setAll(0);
35+
filter=BiquadFilter::create(numLowPassStages+numHighPassStages);
36+
setLowPassCutoff(0.03);
37+
setHighPassCutoff(0.001);
38+
};
39+
void setSamplingRate(float aSamplingRate){
40+
samplingRate=aSamplingRate;
41+
}
42+
void setMaxBlocksize(int aMaxBlocksize){
43+
FloatArray::destroy(filterOutput);
44+
filterOutput=FloatArray::create(aMaxBlocksize);
45+
}
46+
void setLowPassCutoff(float fc){
47+
if(numLowPassStages<1)
48+
return;
49+
FilterStage stage0=filter->getFilterStage(0);
50+
stage0.setLowPass(fc/samplingRate, FilterStage::BUTTERWORTH_Q);
51+
for(int n=1; n<numLowPassStages; n++){
52+
FilterStage stage=filter->getFilterStage(n);
53+
stage.setCoefficients(FloatArray(stage0.getCoefficients(), 5));
54+
}
55+
};
56+
void setHighPassCutoff(float fc){
57+
if(numHighPassStages<1)
58+
return;
59+
FilterStage stage0=filter->getFilterStage(numLowPassStages);
60+
stage0.setHighPass(fc/samplingRate, FilterStage::BUTTERWORTH_Q);
61+
for(int n=numLowPassStages+1; n<numHighPassStages+numLowPassStages; n++){
62+
FilterStage stage=filter->getFilterStage(n);
63+
stage.setCoefficients(stage0.getCoefficients());
64+
}
65+
};
66+
void process(FloatArray input){
67+
ASSERT(input.getSize()<=filterOutput.getSize(), "wrong size");
68+
static float lastValue=0;
69+
static int countsPointer=0;
70+
static float period=0;
71+
static float count;
72+
filter->process(input, filterOutput);
73+
// filterOutput.copyTo(input);
74+
for(int n=0; n<input.getSize(); n++){
75+
float currentValue=filterOutput[n];
76+
if(currentValue>0 && lastValue<=0){
77+
/*
78+
counts[countsPointer]=count; //Could use nearest neighbour, but
79+
count=0;
80+
*/
81+
//linear interpolation gives a better estimate of the zero crossing time:
82+
float offset=(-lastValue)/(lastValue+currentValue);
83+
counts[countsPointer]=count+offset;
84+
count=-offset;
85+
countsPointer++;
86+
if(countsPointer==counts.getSize()) //use counts as a circular buffer
87+
countsPointer=0;
88+
}
89+
lastValue=currentValue;
90+
count++;
91+
}
92+
}
93+
94+
float getFrequency(){
95+
return samplingRate/counts.getMean();
96+
}
97+
BiquadFilter* getFilter(){
98+
return filter;
99+
}
100+
};
101+
102+
#endif /* __PitchDetectors_hpp__ */
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
////////////////////////////////////////////////////////////////////////////////////////////////////
2+
3+
/*
4+
5+
6+
LICENSE:
7+
This program is free software: you can redistribute it and/or modify
8+
it under the terms of the GNU General Public License as published by
9+
the Free Software Foundation, either version 3 of the License, or
10+
(at your option) any later version.
11+
12+
This program is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU General Public License for more details.
16+
17+
You should have received a copy of the GNU General Public License
18+
along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
20+
*/
21+
22+
23+
/* created by the OWL team 2013 */
24+
25+
26+
////////////////////////////////////////////////////////////////////////////////////////////////////
27+
28+
29+
#ifndef __PitchDetectorTestPatch_hpp__
30+
#define __PitchDetectorTestPatch_hpp__
31+
32+
#include "StompBox.h"
33+
34+
class PitchDetectorTestPatch : public Patch {
35+
public:
36+
BiquadFilter *filter;
37+
ZeroCrossingPitchDetector zcc;
38+
PitchDetectorTestPatch() : zcc(getSampleRate(), getBlockSize()){
39+
registerParameter(PARAMETER_A, "Mix");
40+
registerParameter(PARAMETER_B, "HP cutoff");
41+
registerParameter(PARAMETER_C, "LP cutoff");
42+
registerParameter(PARAMETER_D, "gain");
43+
}
44+
void processAudio(AudioBuffer &buffer){
45+
FloatArray fa=buffer.getSamples(0);
46+
float mix=getParameterValue(PARAMETER_A);
47+
zcc.setHighPassCutoff(getParameterValue(PARAMETER_B)*500+50);
48+
zcc.setLowPassCutoff(getParameterValue(PARAMETER_C)*1000+150);
49+
zcc.process(fa);
50+
float estimated=zcc.getFrequency();
51+
float envelope=fa.getRms();
52+
fa.multiply(1-mix);
53+
for(int n=0;n<fa.getSize(); n++){
54+
static float phase=0;
55+
static float pastEnvelope=0;
56+
phase += 2.0 * M_PI * estimated/getSampleRate();
57+
if(phase > 2.0 * M_PI)
58+
phase -= 2.0 * M_PI;
59+
if(phase > 4.0*M_PI)
60+
phase=0;
61+
envelope=0.001*envelope + pastEnvelope*0.999;
62+
pastEnvelope=envelope;
63+
fa[n]+=sin(phase)*mix*envelope;
64+
}
65+
fa.multiply(getParameterValue(PARAMETER_D));
66+
float *coeffs=zcc.getFilter()->getFilterStage(0).getCoefficients();
67+
debugMessage("estimated envelope: ", estimated, envelope);
68+
// debugMessage("coeffs: ", coeffs[3], coeffs[4], coeffs[2] );
69+
}
70+
};
71+
72+
#endif // __PitchDetectorTestPatch_hpp__

0 commit comments

Comments
 (0)