1- #ifndef __PitchDetectors_hpp__
2- #define __PitchDetectors_hpp__
1+ #ifndef __PitchDetector_hpp__
2+ #define __PitchDetector_hpp__
33
44#include " FloatArray.h"
55
6+ class FourierPitchDetector {
7+ private:
8+ FastFourierTransform fft;
9+ float samplingRate;
10+ float minBin;
11+ float maxBin;
12+ float binSize;
13+ float frequency;
14+ ComplexFloatArray fd;
15+ FloatArray magnitudes;
16+ FloatArray timeDomain;
17+ int writePointer;
18+ Window window;
19+ public:
20+ FourierPitchDetector (){
21+
22+ };
23+ FourierPitchDetector (int fftSize, float aSamplingRate){
24+ init (fftSize, aSamplingRate);
25+ };
26+ ~FourierPitchDetector (){
27+ ComplexFloatArray::destroy (fd);
28+ FloatArray::destroy (magnitudes);
29+ FloatArray::destroy (timeDomain);
30+ Window::destroy (window);
31+ }
32+ void init (int fftSize, float aSamplingRate){
33+ samplingRate=aSamplingRate;
34+ frequency=0 ;
35+ writePointer=0 ;
36+ fft.init (fftSize);
37+ binSize=samplingRate/fftSize;
38+ fd=ComplexFloatArray::create (fftSize);
39+ magnitudes=FloatArray::create (fftSize);
40+ timeDomain=FloatArray::create (fftSize);
41+ window=Window::create (Window::HannWindow, fftSize);
42+ }
43+ int getSize (){
44+ return fft.getSize ();
45+ }
46+ void setsamplingRate (float asamplingRate){
47+ samplingRate=asamplingRate;
48+ }
49+ void setMinFrequency (float aMinFrequency){
50+ minBin=(int )(aMinFrequency/binSize);
51+ }
52+ void setMaxFrequency (float aMaxFrequency){
53+ maxBin=(int )(aMaxFrequency/binSize+1 );
54+ }
55+ int process (FloatArray input){ // return values: 0 no fft performed, 1 fft performed
56+ ASSERT (input.getSize ()<=fd.getSize (), " wrong size" );
57+ if (input.getSize ()==fft.getSize ()){ // if the two sizes match, no need to copy the input into the local timeDomain FloatArray
58+ input.multiply (window, timeDomain);
59+ fft.fft (timeDomain, fd);
60+ return 1 ;
61+ }
62+ // otherwise keep filling the input buffer TODO: could multiply by the window while copying
63+ int samplesToCopy=min (input.getSize (),timeDomain.getSize ()-writePointer);
64+ timeDomain.insert (input, 0 , writePointer, samplesToCopy);
65+ writePointer+=samplesToCopy;
66+ if (writePointer<fft.getSize ()){ // if it is not full, keep going
67+ return 0 ;
68+ }
69+ if (writePointer==fft.getSize ()){ // if it is full, reset pointer and do fft
70+ writePointer=0 ;
71+ timeDomain.multiply (window);
72+ fft.fft (timeDomain, fd);
73+ return 1 ;
74+ }
75+ }
76+ float computeFrequency (){// this could have been implemented into process().
77+ // The reason why it is in a separate method is to allow to distribute the computational load across different audio blocks
78+ ComplexFloatArray fdsub=fd.subArray ((int )minBin, (int )maxBin-(int )minBin);
79+ FloatArray magnitudesSub=magnitudes.subArray ((int )minBin, (int )maxBin-(int )minBin);
80+ fdsub.getMagnitudeSquaredValues (magnitudesSub);
81+ int maxIndex=magnitudesSub.getMaxIndex ();
82+ // do quadratic interpolation https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
83+ // this single iteration method gives the following accuracy using as input a sinewave in the range 100Hz-200Hz
84+ // fftSize=512; max(abs(error))=15Hz well, with this fftSize, the binsize is 93.75 Hz, with a 100Hz input
85+ // the peak is in the first quarter of the second bin and the estimate cannot be precise!
86+ // performance improve drastically above 140Hz (error <2Hz)
87+ // fftSize=1024; max(abs(error))=0.7495Hz
88+ // fftSize=2048; max(abs(error))=0.37531Hz
89+ // fftSize=4096; max(abs(error))=0.18746Hz
90+ if (maxIndex==0 ||maxIndex==magnitudesSub.getSize ()-1 ) { // value out of range
91+ frequency=0 ; // TODO: what to do if value is out of range?
92+ return getFrequency ();
93+ }
94+ // note that we are working on the values in Bel (10*dB).
95+ // Using the logarithmic scale gives better accuracy of the peak estimate
96+ // in this application
97+ float alpha=log10 (magnitudesSub[maxIndex-1 ]);
98+ float beta=log10 (magnitudesSub[maxIndex]);
99+ float gamma=log10 (magnitudesSub[maxIndex+1 ]);
100+ float p=0.5 *(alpha-gamma)/(alpha-2 *beta+gamma);
101+ // ASSERT(p>=-0.5 && p<=0.5, "Wrong range for p");
102+ int bin=maxIndex+minBin;
103+ frequency=(bin+p)*binSize;
104+ return getFrequency ();
105+ }
106+ float getFrequency (){
107+ return frequency;
108+ }
109+ };
110+
6111class ZeroCrossingPitchDetector {
7112private:
8113 BiquadFilter *filter;
@@ -99,4 +204,4 @@ class ZeroCrossingPitchDetector{
99204 }
100205};
101206
102- #endif /* __PitchDetectors_hpp__ */
207+ #endif /* __PitchDetector_hpp__ */
0 commit comments