Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
fftanalyzer.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // FFT Analyzer
22 //=============================================================================
23 
24 #include "fftanalyzer.h"
25 
26 #include <iostream>
27 #include <algorithm>
28 #include <utility>
29 
30 #include "../config.h"
31 #include "../system/log.h"
32 #include "../system/eptexception.h"
33 #include "../piano/piano.h"
34 #include "../math/mathtools.h"
35 
36 
37 //-----------------------------------------------------------------------------
38 // Constructor
39 //-----------------------------------------------------------------------------
40 
42  mOptimalSuperposition(), // Array for peak superposition
43  mCurrentKernelKey(nullptr) // Initially no kernel
44 {}
45 
46 //-----------------------------------------------------------------------------
47 // Main worker function running in an independent thread
48 //-----------------------------------------------------------------------------
49 
61 
62 std::pair<FFTAnalyzerErrorTypes, std::shared_ptr<Key> > FFTAnalyzer::analyse (
63  const Piano *piano,
64  FFTDataPointer finalFFT,
65  int finalKey)
66 {
67  // consisty check
68  EptAssert(piano, "Piano has to be set");
69  EptAssert(finalFFT, "FFT has to exist");
70  EptAssert(finalFFT->isValid(), "The FFT data is not valid");
71  EptAssert(finalKey >= 0, "The final key has to be set.");
72 
73 
74  std::shared_ptr<Key> key; // the key output
75 
76 
77  LogV("FFTAnalyzer started");
78 
79  // Map the final FFT to a logarithmically binned spectrum:
80  static SpectrumType spectrum(NumberOfBins);
81  constructLogBinnedSpectrum(finalFFT, spectrum);
82  Write("4-final-logspec.dat", spectrum);
83 
84  // In the first two octaves the ground frequency f1 is very weak
85  // or even non-existing. Here we use instead the 2nd or 4th partial
86  // for the frequency estimation. This function computes how many
87  // octaves we intend to shift:
88 
89  auto octaves = [this] (int keynumber, int keyNumberOfA)
90  {
91  int distance = keyNumberOfA - keynumber;
92  if (distance > 36) return 2;
93  else if (distance > 24) return 1;
94  else return 0;
95  };
96 
97  // First frequency estimate without inharmonicity
98  double factor = pow(2.0,octaves(finalKey, piano->getKeyboard().getKeyNumberOfA4()));
99  double f1 = factor * estimateFrequency(finalKey, piano->getConcertPitch(), piano->getKeyboard().getKeyNumberOfA4());
100  int m = Key::FrequencyToIndex(f1);
101  double f = Key::IndexToFrequency(locatePeak(spectrum,m,40)) / factor;
102 
103  LogV("FFTAnalyzer: Estimated frequency f = %f, key = %d", f, finalKey);
104 
105 
106  if (f>20 and f<6000)
107  {
108  // Inharmonicity estimation.
109  double B = estimateInharmonicity(finalFFT, spectrum,f);
110  LogV("FFTAnalyzer: Inharmonicity B = %f", B);
111 
112  // Correct frequency, taking inharmonicty into account
113  f = findAccuratePeakFrequency(finalFFT, factor*f)/factor/sqrt((1+B*factor*factor)/(1+B));
114  LogV("FFTAnalyzer: Accurate frequency f = %f", f);
115 
116  // Quality check by collapsing higher partials (except of highest ocatave)
117  double Q=0;
118  if (f<2200)
119  {
120  Q = estimateQuality();
121  LogV("FFTAnalyzer: Quality measure (cents) Q = %f", Q);
122 
123  double cents = estimateFrequencyShift();
124  LogV("FFTAnalyzer: frequency correction cents C = %f", cents);
125  }
126 
127  // Create a new key:
128  // This is done with a shared pointer, meaning that the instance
129  // will be deleted automatically if it is no longer used
130  key.reset(new Key());
131 
132  key->setRecordedFrequency(f);
133  key->setMeasuredInharmonicity(B);
134  key->setRecognitionQuality(Q);
135  key->setSpectrum(spectrum);
136  key->setRecorded(true);
137 
138  auto peaks = identifyPeaks(finalFFT, spectrum,f,B);
139  // %zu is c++ standard, but windows doenst support this... workaround use int
140  LogV("FFTAnalyzer: found %i peaks.", static_cast<int>(peaks.size()));
141  key->setPeaks(peaks);
142  }
143  else
144  {
145  LogW("Frequence %f is out of bounds", f);
146  return std::make_pair(FFTAnalyzerErrorTypes::ERR_FREQUENCY_OUT_OF_BOUNDS, key);
147  }
148 
149  LogV("leaving FFTAnalyzer thread.");
150 
151  return std::make_pair(FFTAnalyzerErrorTypes::ERR_NONE, key);
152 }
153 
154 //-----------------------------------------------------------------------------
155 // Determine the frequency of a known key
156 //-----------------------------------------------------------------------------
157 
166 
168  FFTDataPointer finalFFT,
169  const Piano *piano,
170  const Key &key,
171  int keyIndex)
172 {
173  // consisty check
174  EptAssert(piano, "Piano has to be set");
175  EptAssert(finalFFT, "FFT has to exist");
176  EptAssert(finalFFT->isValid(), "The FFT data is not valid");
177  EptAssert(keyIndex >= 0, "The final key has to be set.");
178 
179  // Create a shared pointer in which the result will be stored:
180  FrequencyDetectionResult result = std::make_shared<FrequencyDetectionResultStruct>();
181 
182  // This is the frequency to which we would like to tune the string
183  double targetFrequency = piano->getConcertPitch()/440.0 *
184  key.getComputedFrequency();
185 
186  if (targetFrequency <= 20 or targetFrequency > 10000)
187  {
189  return result;
190  }
191 
192  // Define the frequency which corresponds to the middle of the array
193  double centerFrequency = key.getRecordedFrequency();
194  if (centerFrequency<=10) centerFrequency = targetFrequency;
195 
196  // Map the final FFT to a logarithmically binned spectrum:
197  SpectrumType spectrum(NumberOfBins);
198  constructLogBinnedSpectrum(finalFFT, spectrum);
199 
200  // Define the search size around the center frequency in cents
201  const int searchSize = 200;
202  TuningDeviationCurveType out(searchSize), zoomedPeak (searchSize);
203 
204 
205  // First method: Simply magnify the lowest peak
206  if (not key.isRecorded())
207  {
208  double maximum = 0;
209  double middle = centerFrequency;
210  if (piano->getKeyboard().getKeyNumberOfA4()-keyIndex > 24)
211  {
212  double B = piano->getExpectedInharmonicity(centerFrequency);
213  middle *= 2 * sqrt((1+4*B)/(1+B));
214  }
215 
216  for (int i=0; i<searchSize; ++i)
217  {
218  int m = Key::FrequencyToRealIndex(middle) - searchSize/2 + i;
219  if (m>0 and m<static_cast<int>(spectrum.size()))
220  {
221  double value = spectrum[m] * spectrum[m];
222  if (value > maximum) maximum = value;
223  zoomedPeak[i] = value;
224  }
225  }
226  if (maximum < 1E-15)
227  {
229  return result;
230  }
231  for (auto &element : zoomedPeak) element /= maximum;
232  out = std::move(zoomedPeak);
233  }
234 
235  // else if the key has been recorded explicitely we use the method developed
236  // by Christoph. It uses an inverse kernel convolution
237  else
238  {
239 
240 
241  // create the kernel of the key, if the key changed
242  if (&key != mCurrentKernelKey)
243  {
244  mCurrentKernel = std::move(constructKernel(key.getSpectrum()));
245  mCurrentKernelKey = &key;
246  }
247 
248  // compute the deviation
249  out = std::move(computeTuningDeviation(mCurrentKernel, spectrum, searchSize));
250  }
251 
252  int maxIndex = MathTools::findMaximum(out);
253  double index = MathTools::weightedArithmetricMean(out, std::max(maxIndex - 10, 0), maxIndex + 10);
254  index -= searchSize / 2;
255 
256  double detectedFrequency = centerFrequency * std::pow(2.0, (index) / 1200.0);
257  EptAssert(detectedFrequency > 1 && detectedFrequency < 20000, "Unallowed frequency range");
258 
259  int computedIndex = MathTools::roundToInteger(log(targetFrequency / centerFrequency) * 1200 / MathTools::LOG2);
260 
261 
262  result->deviationInCents = static_cast<int>(index - computedIndex);
263  result->detectedFrequency = detectedFrequency;
264  result->positionOfMaximum = index;
265  result->tuningDeviationCurve = std::move(out);
266 
267  LogV("Deviation %d, comp index %d", result->deviationInCents, computedIndex);
268 
269  return result;
270 }
271 
272 //-----------------------------------------------------------------------------
273 // Construct logarithmically binned spectrum
274 //-----------------------------------------------------------------------------
275 
290 
292 {
293  const double b = 2.0 * fftData->fft.size() / fftData->samplingRate;
294  std::function<double(double)> mtoq = [this,b] (double m)
295  { return b * Key::IndexToFrequency(m); };
296  MathTools::coarseGrainSpectrum (fftData->fft,spectrum,mtoq,0.25);
297  MathTools::normalize(spectrum);
298 }
299 
301 {
302  SpectrumType kernel(NumberOfBins);
303  FFTComplexVector fftOfOriginal;
304  mFFT.calculateFFT(originalSpectrum, fftOfOriginal);
305  for (FFTComplexType &c : fftOfOriginal) {
306  c = c / (c.real() * c.real() + c.imag() * c.imag());
307  }
308  mFFT.calculateFFT(fftOfOriginal, kernel);
309  return kernel;
310 }
311 
313  const SpectrumType &kernel, const SpectrumType &signal, int searchSize)
314 {
315  const int searchOffset = searchSize / 2;
316  TuningDeviationCurveType out(searchSize);
317 
318  // in a range of 50 ct find the maximum when folding the kernel with the spectrum
319  for (int j = -searchOffset; j < searchSize - searchOffset; j++) {
320  out[j + searchOffset] = 0;
321  for (int i = 0; i < NumberOfBins; i++) {
322  out[j + searchOffset] += kernel[(i - j + NumberOfBins) % NumberOfBins] * signal[i];
323  }
324  }
325 
326  return out;
327 }
328 
329 //-----------------------------------------------------------------------------
330 // Locate a single peak in the logarithmically binned spectrum
331 //-----------------------------------------------------------------------------
332 
340 
341 int FFTAnalyzer::locatePeak (const SpectrumType &spectrum, int m, int width)
342 {
343  EptAssert(spectrum.size()==static_cast<size_t>(NumberOfBins)
344  and spectrum.size()>0, "Inconsistent arguments");
345  if (m<width or m>NumberOfBins-width) return 0;
346  return MathTools::findMaximum (spectrum,m-width,m+width);
347 }
348 
349 
350 //-----------------------------------------------------------------------------
351 // Interpolate the position of the maximum
352 //-----------------------------------------------------------------------------
353 
362 
363 double FFTAnalyzer::interpolatePeakPosition (const SpectrumType &spectrum, int m, int width)
364 {
365  EptAssert(spectrum.size()==static_cast<size_t>(NumberOfBins)
366  and spectrum.size()>0, "Inconsistent arguments");
367  if (m<width+1 or m>NumberOfBins-width-1) return 0;
368  int mmax = MathTools::findMaximum (spectrum,m-width,m+width);
369  double y1=spectrum[mmax-1], y2=spectrum[mmax], y3=spectrum[mmax+1];
370  double N = y1 - 2*y2 + y3;
371  if (N == 0) return mmax;
372  else
373  {
374  double correction = (y1-y3)/2/N;
375  if (fabs(correction)<1) return mmax+correction;
376  else return mmax;
377  }
378 }
379 
380 
381 //-----------------------------------------------------------------------------
382 // Find the nearest key to a given frequency, assuming average stretch
383 //-----------------------------------------------------------------------------
384 
393 
394 int FFTAnalyzer::findNearestKey (double f, double conertPitch, int numberOfKeys, int keyNumberOfA)
395 {
396  EptAssert(conertPitch>390 or conertPitch<500,"Concert pitch unreasonable.");
397  // Approximate distance in keys from A-440:
398  double d = 17.3123*log(f / conertPitch);
399  // Average stretch polynomial, giving the expected deviation in cents
400  double c =0.000019394+0.079694594*d-0.003718646*d*d+0.000450934*d*d*d + 0.000003724*d*d*d*d;
401  int k=-1; k = static_cast<int>(keyNumberOfA + d-c/100+0.5);
402  return (k>=0 and k<numberOfKeys ? k : -1);
403 }
404 
405 
406 //-----------------------------------------------------------------------------
407 // Roughly frequency estimate for a given key
408 //-----------------------------------------------------------------------------
409 
419 
420 double FFTAnalyzer::estimateFrequency (int keynumber, double concertPitch, int keyNumberOfA)
421 {
422  EptAssert(concertPitch>390 or concertPitch<500,"Concert pitch unreasonable.");
423  // Distance in keys from A-440:
424  double d = keynumber - keyNumberOfA;
425  // Average stretch polynomial, giving the expected deviation in cents
426  double c =0.000019394+0.079694594*d-0.003718646*d*d+
427  0.000450934*d*d*d + 0.000003724*d*d*d*d;
428  return pow(2.0, d/12+c/1200) * concertPitch;
429 }
430 
431 
432 //-----------------------------------------------------------------------------
433 // Find accurate frequency of the peak in original spectrum
434 //-----------------------------------------------------------------------------
435 
448 
449 double FFTAnalyzer::findAccuratePeakFrequency(FFTDataPointer fftData, double f, int cents)
450 {
451  // Define the bounds in which the peak will be searched
452  const double factor = 1.0 + 0.000577623 * cents;
453  const double b = 2.0 * fftData->fft.size() / fftData->samplingRate;
454  int q1 = MathTools::roundToInteger(b*f/factor);
455  int q2 = MathTools::roundToInteger(b*f*factor);
456 
457  // search for the maximum
458  if (q1 > 0 and q2 < static_cast<int>(fftData->fft.size()))
459  {
460  double fftmax=0;
461  int qmax=q1;
462  for (int q=q1; q<q2; ++q) if (fftData->fft[q]>fftmax)
463  { fftmax = fftData->fft[q]; qmax=q; }
464  return qmax/b;
465  }
466  else return f;
467 }
468 
469 
470 //-----------------------------------------------------------------------------
471 // Compute rough estimate for the expected inharmonicity
472 //-----------------------------------------------------------------------------
473 
483 
485 {
486  return (f > 100 ? exp(-15.45 + 1.354*log(f)) : 0.000099575);
487 }
488 
489 
490 //-----------------------------------------------------------------------------
491 // Estimate inharmonicity
492 //-----------------------------------------------------------------------------
493 
509 
511 {
512  // do not evaluate inharmonicity if parameters are invalid
513  if (spectrum.size()==0 or f<20) return 0;
514 
515  // do not evaluate inharmonicity in the very high treble (top octave)
516  // because this is usually very unreliable
517  if (f > 2250) return 0;
518 
519  // for frequencies above 1kHz only f1 and f2 are taken into account
520  // and the inharmonicity B is calculated directly from their ratio.
521  if (f>1000)
522  {
523  double f2 = findAccuratePeakFrequency (fftData, 2.0174*f, 15);
524  double z = f2*f2/f/f;
525  if (z>4.4 or z<4) return 0;
526  double B = (4-z)/(z-16);
527  LogV("FFTAnalyzer: treble: B = %f", B);
528  return B;
529  }
530 
531  // In the frequency range from 26 Hz to 1kHz the inharmonicity is computed
532  // by superposition of N higher partials and searching for the lowest
533  // Renyi entropy of the superposition.
534 
535  // Define the number of partials taken into accout.
536  int N=MathTools::roundToInteger(4*(8-log(f)));
537 
538  double B = 0; // Inharmonicity to be determined
539  double Hmin = 1E100; // Initial Renyi entropy very high
540  const int R = 80; // width of the observation window in cents
541 
542  // Calling this function gives a first rough estimate of the inharmonicty
543  double expected_B = getExpectedInharmonicity(f);
544  LogV("FFTAnalyzer: expected B = %f", expected_B);
545 
546  mOptimalSuperposition.clear();
547  for (double scan_B = expected_B/5; scan_B <= expected_B*5; scan_B*=1.03)
548  {
549  SpectrumType superposition(R,0);
550  for (int n=1; n<=N; ++n)
551  {
552  double fn = n*f*sqrt((1+scan_B*n*n)/(1+scan_B));
553  double mn = Key::FrequencyToRealIndex(fn);
554  SpectrumType partialspectrum(R,0);
555  if (mn-R/2>0 and mn+R/2<NumberOfBins)
556  {
557  for (int r=0; r<R; r++)
558  {
559  int m = static_cast<int>(mn+r-R/2);
560  EptAssert (m>=0 and m<NumberOfBins,"m invalid");
561  partialspectrum[r]=pow(spectrum[m],2);
562  }
563  MathTools::normalize(partialspectrum);
564  for (int r=0; r<R; r++) superposition[r]+=partialspectrum[r];
565  }
566  }
567 
568  MathTools::normalize(superposition);
569  double H = MathTools::computeRenyiEntropy(superposition,0.1);
570  if (fabs(H)<Hmin)
571  {
572  Hmin=H;
573  B=scan_B;
574  mOptimalSuperposition = superposition;
575  Write("7-find-inharmonicity.dat",superposition);
576  }
577  }
578  LogV("FFTAnalyzer: finished estimating inharmonicity: B = %f", B);
579 
580 #if CONFIG_ENABLE_XMGRACE
581  //system("killall -9 xmgrace; xmgrace -maxpath 200000 -fixed 570 440 -geometry 800x660 7-find-inharmonicity.dat &");
582 #endif // CONFIG_ENABLE_XMGRACE
583 
584  return B;
585 }
586 
587 
588 //-----------------------------------------------------------------------------
589 // estimate quality of the recorded sound
590 //-----------------------------------------------------------------------------
591 
593 {
594  if (mOptimalSuperposition.size()==0) return 0;
595 
596  // cut the superposition at the edges
597  int cut = mOptimalSuperposition.size()/2-10;
598 
600  mOptimalSuperposition.end()-cut);
601  double M0 = MathTools::computeNorm(vec);
602  if (M0==0) return 0;
604  double M1 = MathTools::computeMoment(vec,1);
605  double M2 = MathTools::computeMoment(vec,2);
606  double variance = M2-M1*M1;
607  // translate heuristicaly in a measure of quality to be displayed
608  return M0/(1+0.1*pow(variance,1.5));
609 }
610 
611 
612 //-----------------------------------------------------------------------------
613 // estimate frequency shift of the recorded sound
614 //-----------------------------------------------------------------------------
615 
617 {
618  // cut out a section of 20 cents
619  size_t start = mOptimalSuperposition.size()/2-10;
620  size_t stop = mOptimalSuperposition.size()/2+10;
621  if (start>=stop or stop>=mOptimalSuperposition.size()) return 0;
622  SpectrumType vec = SpectrumType(mOptimalSuperposition.begin()+start,
623  mOptimalSuperposition.begin()+stop);
624  return MathTools::computeMoment(vec,1)-10;
625 }
626 
627 
628 //-----------------------------------------------------------------------------
629 // Identify peaks of the spectrum for given inharmonicity
630 //-----------------------------------------------------------------------------
631 
633  const SpectrumType &spectrum,
634  const double f, const double B)
635 {
636  const int MaxNumberOfPeaks = 50;
637  auto InharmonicPartial = [] (double f, int n, double B) { return f*n*sqrt((1+B*n*n)/(1+B)); };
638  int N=std::min(MaxNumberOfPeaks, static_cast<int>(10000.0/f)); // number of peaks
639  PeakListType peaks;
640  for (int n=1; n<=N; ++n)
641  {
642  double fn = InharmonicPartial(f,n,B);
643  int m= locatePeak (spectrum, Key::FrequencyToIndex(fn), 20);
644  if (m>0)
645  {
646  double f = Key::IndexToFrequency(m);
647  double fc = findAccuratePeakFrequency(fftData, f);
648  peaks[fc]=spectrum[m];
649  }
650  }
651  return peaks;
652 }
653 
654 
655 //-----------------------------------------------------------------------------
656 // Write function for development purposes
657 //-----------------------------------------------------------------------------
658 
659 void FFTAnalyzer::Write(std::string filename, SpectrumType &v)
660 {
661 #if CONFIG_ENABLE_XMGRACE
662  std::ofstream os(filename);
663  for (uint m=0; m<v.size(); ++m)
664  {
665  if (v.size() != static_cast<size_t>(NumberOfBins)) os << m << "\t" << v[m] << std::endl;
666  else os << Key::IndexToFrequency(m) << "\t" << v[m] << std::endl;
667  }
668  os.close();
669 #else
670  (void)filename; (void)v; // suppress warnings
671 #endif // CONFIG_ENABLE_XMGRACE
672 }
673 
674 
675 
676 void FFTAnalyzer::Write(std::string filename, FFTComplexVector &v)
677 {
678 #if CONFIG_ENABLE_XMGRACE
679  std::ofstream os(filename);
680  for (uint m=0; m<v.size(); ++m)
681  {
682  os << m << "\t" << v[m].real() << std::endl;
683  }
684  os << "&" << std::endl;
685  for (uint m=0; m<v.size(); ++m)
686  {
687  os << m << "\t" << v[m].imag() << std::endl;
688  }
689  os.close();
690 #else
691  (void)filename; (void)v; // suppress warnings
692 #endif // CONFIG_ENABLE_XMGRACE
693 }
694 
695 
696 
697 void FFTAnalyzer::WritePeaks(std::string filename, SpectrumType &v,PeakListType &peaks)
698 {
699 #if CONFIG_ENABLE_XMGRACE
700  std::ofstream os(filename);
701  os << "@g0 type logxy" << std::endl;
702  os << "# logspec / peaks according to F&B / peaks in the list" << std::endl;
703  for (int m=0; m<Key::NumberOfBins; ++m) if (v[m]>1E-100) os << Key::IndexToFrequency(m) << "\t" << v[m] << std::endl;
704  os << "&" << std::endl;
705 // for (int n=1; n<=N; ++n)
706 // {
707 // double fn = InharmonicPartial(F,n,B);
708 // os << fn-0.00001 << tab << 1E-10 << std::endl;
709 // os << fn << tab << 1 << std::endl;
710 // os << fn+0.00001 << tab << 1E-10 << std::endl;
711 // }
712 // os << "&" << std::endl;
713  for (auto p:peaks)
714  {
715  double fn = Key::IndexToFrequency(p.first);
716  os << fn-0.00001 << '\t' << 1E-9 << std::endl;
717  os << fn << '\t' << p.second << std::endl;
718  os << fn+0.00001 << '\t' << 1E-9 << std::endl;
719  }
720 #else
721  (void)filename; (void)peaks; (void)v; // suppress warnings
722 #endif // CONFIG_ENABLE_XMGRACE
723 }
bool isRecorded() const
Get recorded flag.
Definition: key.h:102
double getRecordedFrequency() const
Get recorded frequency.
Definition: key.cpp:119
double computeMoment(const std::vector< double > &v, const int n)
Determine the n-th moment of a distribution stored in a vector.
Definition: mathtools.cpp:38
const double LOG2
Definition: mathtools.h:39
double findAccuratePeakFrequency(FFTDataPointer fftData, double f, int cents=5)
Find the accurate frequency of a spectral peak in the original FFT.
FFTAnalyzer()
Constructor.
Definition: fftanalyzer.cpp:41
#define LogV(...)
Definition: log.h:38
double computeRenyiEntropy(const std::vector< double > &v, const double q)
Compute the Renyi entropy of a normalized probability distribution.
Definition: mathtools.cpp:69
#define LogW(...)
Definition: log.h:56
Key::PeakListType PeakListType
Type for a peak map.
Definition: fftanalyzer.h:48
std::vector< FFTComplexType > FFTComplexVector
Definition: fftadapter.h:36
SpectrumType mCurrentKernel
The current kernel for the key detection.
Definition: fftanalyzer.h:70
Class describing a single piano key.
Definition: key.h:45
TuningDeviationCurveType computeTuningDeviation(const SpectrumType &kernel, const SpectrumType &signal, int searchSize)
double weightedArithmetricMean(const std::vector< double > &Y, size_t start=0, size_t end=std::numeric_limits< size_t >::max())
Computes the weighted arithmetric mean index of the given Y data.
Definition: mathtools.cpp:178
std::vector< double > TuningDeviationCurveType
The FrequencyDetectionResultStruct struct.
int roundToInteger(T x)
Round a floating point number to an integer.
Definition: mathtools.h:43
double getExpectedInharmonicity(double f)
Definition: piano.h:40
void coarseGrainSpectrum(const std::vector< double > &X, std::vector< double > &Y, std::function< double(double y)> f, double exponent=0)
Definition: mathtools.cpp:120
int findMaximum(const std::vector< double > &X, int i, int j)
Find the component where the vector has its maximum.
Definition: mathtools.cpp:151
Key::SpectrumType SpectrumType
Type of a log spectrum.
Definition: fftanalyzer.h:47
static double IndexToFrequency(double m)
Convert continuous slot index to frequency in Hz.
Definition: key.cpp:102
double estimateFrequencyShift()
PeakListType identifyPeaks(FFTDataPointer fftData, const SpectrumType &spectrum, const double f, const double B)
void Write(std::string filename, SpectrumType &v)
std::pair< FFTAnalyzerErrorTypes, std::shared_ptr< Key > > analyse(const Piano *piano, FFTDataPointer finalFFT, int finalKey)
Main analyzing function.
Definition: fftanalyzer.cpp:62
No intensity in the signal near expected peak.
const Keyboard & getKeyboard() const
Definition: piano.h:83
The recorded frequency is out of the piano range.
double getComputedFrequency() const
Get computed frequency.
Definition: key.cpp:174
const int NumberOfBins
Definition: fftanalyzer.h:66
static double FrequencyToRealIndex(double f)
Convert frequency to real-valued logbin index.
Definition: key.cpp:70
std::complex< double > FFTComplexType
Definition: fftadapter.h:34
FFT_Implementation mFFT
Instance of FFT implementation.
Definition: fftanalyzer.h:69
void normalize(std::vector< double > &vec)
Normalize a distribution stored in a vector.
Definition: mathtools.cpp:92
int findNearestKey(double f, double conertPitch, int numberOfKeys, int keyNumberOfA)
Compute the number of the nearest key.
int getKeyNumberOfA4() const
Definition: keyboard.h:73
SpectrumType constructKernel(const SpectrumType &originalSpectrum)
const double & getConcertPitch() const
Definition: piano.h:80
const SpectrumType & getSpectrum() const
Get a read-only reference to mSpectrum.
Definition: key.cpp:237
#define EptAssert(a, b)
Definition: eptexception.h:47
The analyzer needs a computed frequency.
FrequencyDetectionResult detectFrequencyOfKnownKey(FFTDataPointer finalFFT, const Piano *piano, const Key &key, int keyIndex)
FFTAnalyzer::detectFrequencyOfKnownKey.
const Key * mCurrentKernelKey
The key of which mCurrentKernel belongs to.
Definition: fftanalyzer.h:71
int locatePeak(const SpectrumType &spectrum, int m, int width)
static int FrequencyToIndex(double f)
Convert frequency to logbin index.
Definition: key.cpp:88
std::shared_ptr< FFTData > FFTDataPointer
Shared pointer of FFTData.
Definition: fftadapter.h:86
double interpolatePeakPosition(const SpectrumType &spectrum, int m, int width)
double estimateInharmonicity(FFTDataPointer fftData, SpectrumType &spectrum, double f)
Estimate the inharmonicity B.
double getExpectedInharmonicity(double f) const
Compute expected approximative inharmonicity.
Definition: piano.cpp:102
double estimateQuality()
std::shared_ptr< FrequencyDetectionResultStruct > FrequencyDetectionResult
void constructLogBinnedSpectrum(FFTDataPointer fftData, SpectrumType &spectrum)
Construct logarithmically binned spectrum from the mFinalFFT.
double estimateFrequency(int keynumber, double concertPitch, int keyNumberOfA)
Estimate the frequency for a given keynumber.
void WritePeaks(std::string filename, SpectrumType &v, PeakListType &peaks)
void calculateFFT(const FFTRealVector &in, FFTComplexVector &out)
Foward FFT, mapping a real vector with size N to a complex one with size N/2+1.
double computeNorm(std::vector< double > &vec)
Compute the norm of a vector.
Definition: mathtools.cpp:82
SpectrumType mOptimalSuperposition
Superposition of the partials.
Definition: fftanalyzer.h:68