Lots of small but important changes to undertone detection:
authorsgunderson@bigfoot.com <>
Mon, 19 Jul 2010 22:25:08 +0000 (00:25 +0200)
committersgunderson@bigfoot.com <>
Mon, 19 Jul 2010 22:25:08 +0000 (00:25 +0200)
 - When comparing peak amplitudes, compare interpolated peaks, not raw bins.
 - Find the best candidate bin _before_ sliding up or down one bin.
 - Verify that a candidate bin is indeed a local peak before testing it.
 - Remove some pointless out-of-bounds checks.
 - Formatting changes to undertone debug printouts.
 - Change the undertone limit from 10dB to 20dB.

Seems to work quite a lot better, especially for the low E string.
It's still not perfect, of course, and I'm sure there are regression
situations, but I think it's a significant improvement.

pitchdetector.cpp

index a73fb86..8805840 100644 (file)
@@ -93,9 +93,13 @@ std::pair<double, double> PitchDetector::find_peak(double *in, unsigned num_samp
        if (best_bin == 0 || best_bin == num_samples / 2) {
                return std::make_pair(-1.0, 0.0);
        }
+       std::pair<double, double> peak = 
+               interpolate_peak(in[best_bin - 1],
+                                in[best_bin],
+                                in[best_bin + 1]);
 
 #if 0
-       printf("undertone strength: %+4.2f %+4.2f %+4.2f [%+4.2f] %+4.2f %+4.2f %+4.2f\n",
+       printf("undertone strength: %+6.2f %+6.2f %+6.2f [%+6.2f] %+6.2f %+6.2f %+6.2f\n",
                20.0 * log10(in[best_bin*4] / fft_length),
                20.0 * log10(in[best_bin*3] / fft_length),
                20.0 * log10(in[best_bin*2] / fft_length),
@@ -105,35 +109,43 @@ std::pair<double, double> PitchDetector::find_peak(double *in, unsigned num_samp
                20.0 * log10(in[best_bin/4] / fft_length));
 #endif
 
-       // see if we might have hit an overtone (set a limit of 10dB)
+       // see if we might have hit an overtone (set a limit of 20dB)
        for (unsigned i = 6; i >= 1; --i) {
-               if (best_bin != best_bin / i &&
-                   20.0 * log10(in[best_bin] / in[best_bin / i]) < 10.0f &&
-                   best_bin / i >= 5) {
+               unsigned candidate_bin = best_bin / i;
+               if (best_bin == candidate_bin ||
+                   candidate_bin < 5 ||
+                   candidate_bin >= num_samples / 2 - 1) {
+                       continue;
+               }
+
+               // Check that we indeed have a peak in this area.
+               if (candidate_bin - 1 != best_bin &&
+                   in[candidate_bin - 1] > in[candidate_bin] &&
+                   in[candidate_bin - 1] > in[candidate_bin - 2]) {
+                       --candidate_bin;
+               } else if (candidate_bin + 1 != best_bin &&
+                          in[candidate_bin + 1] > in[candidate_bin] &&
+                          in[candidate_bin + 1] > in[candidate_bin + 2]) {
+                       ++candidate_bin;
+               } else if (in[candidate_bin] < in[candidate_bin - 1] ||
+                          in[candidate_bin] < in[candidate_bin + 1]) {
+                       continue;
+               }
+
+               std::pair<double, double> candidate_peak = 
+                       interpolate_peak(in[candidate_bin - 1],
+                                        in[candidate_bin],
+                                        in[candidate_bin + 1]);
+               if (candidate_peak.second + 20.0f > peak.second) {
 #if 0
                        printf("Overtone of degree %u!\n", i);
 #endif
-                       best_bin /= i;
-
-                       // consider sliding one bin up or down
-                       if (best_bin > 1 && in[best_bin - 1] > in[best_bin] && in[best_bin - 1] > in[best_bin - 2]) {
-                               --best_bin;
-                       } else if (best_bin < num_samples / 2 - 1 && in[best_bin + 1] > in[best_bin] && in[best_bin + 1] > in[best_bin + 2]) {
-                               ++best_bin;
-                       }
-                          
+                       best_bin = candidate_bin;
+                       peak = candidate_peak;
                        break;
                }
        }
 
-       if (best_bin == 0 || best_bin == num_samples / 2) {
-               return std::make_pair(-1.0, 0.0);
-       }
-       std::pair<double, double> peak = 
-               interpolate_peak(in[best_bin - 1],
-                                in[best_bin],
-                                in[best_bin + 1]);
-               
        return std::make_pair(bin_to_freq(double(best_bin) + peak.first, num_samples), peak.second);
 }