// Algorithm: first interpolate the spectral peak corresponding to fApprox, // then locate the (near-)integer multiples of its frequency Spectrum Analyzer::findHarmonics(const Spectrum spectrum, qreal fApprox) const { Spectrum harmonics; if (fApprox <= 0 || std::isinf(fApprox)) return harmonics; const auto peaks = spectrum.findPeaks(0.01); if (peaks.isEmpty()) return harmonics; harmonics.reserve(peaks.size()); const auto iFund = std::floor(fApprox / m_binFreq) + 1; const auto fundamental = quadraticInterpolation(&spectrum[iFund]); harmonics.append(fundamental); for (const auto peak : peaks) { if (peak->frequency > fundamental.frequency) { const Tone t = quadraticInterpolation(peak); const qreal ratio = t.frequency / fundamental.frequency; if (qAbs(1200 * std::log2(ratio / qRound(ratio))) < 10) harmonics.append(t); } } return harmonics; }
Tone Analyzer::determineSnacFundamental(const Spectrum snac) const { Tone result; const auto peaks = snac.findPeaks(); if (peaks.isEmpty()) return result; // First find the highest peak other than the first SNAC value, which // should be 1.0, then pick the first peak after the first zero crossing // that exceeds 0.8 times that value const auto maxPeak = *std::max_element(peaks.constBegin(), peaks.constEnd(), [](const Tone *t1, const Tone *t2) { return *t1 < *t2; }); const auto zeros = snac.findZeros(1); auto pick = std::find_if(peaks.begin(), peaks.end(), [&](const Tone *t) { return t > zeros.first() && t->amplitude > 0.8 * maxPeak->amplitude; }); if (pick != peaks.end()) { result = quadraticInterpolation(*pick); Q_ASSERT(result.frequency > 0); } return result; }