| 15 | } |
| 16 | |
| 17 | Utils::SpecBuffer* HPCP::process(Utils::SpecBuffer* spec, double sampleRate) { |
| 18 | if (!spec) return nullptr; |
| 19 | mHPCP.clear(); |
| 20 | mSampleRate = sampleRate; |
| 21 | |
| 22 | for (size_t frame = 0; frame < spec->size(); ++frame) { |
| 23 | // updateProgress(mStartProgress + (mDiffProgress * (static_cast<double>(frame) / static_cast<double>(spec->size())))); |
| 24 | mHPCP.emplace_back(std::vector<float>(NUM_HPCP_BINS, 0.0f)); |
| 25 | |
| 26 | std::vector<float>& specFrame = (*spec)[frame]; |
| 27 | |
| 28 | // Find local peaks to compute HPCP with |
| 29 | std::vector<Peak> peaks = getPeaks(MAX_SPEC_PEAKS, specFrame); |
| 30 | |
| 31 | float curMax = 0.0; |
| 32 | for (size_t i = 0; i < peaks.size(); ++i) { |
| 33 | float peakFreq = ((peaks[i].binNum / (specFrame.size() - 1)) * mSampleRate) / 2; |
| 34 | if (peakFreq < MIN_FREQ || peakFreq > MAX_FREQ) continue; |
| 35 | |
| 36 | // Create sum for each pitch class |
| 37 | for (int pc = 0; pc < NUM_HPCP_BINS; ++pc) { |
| 38 | int pcIdx = (pc + PITCH_CLASS_OFFSET_BINS) % NUM_HPCP_BINS; |
| 39 | float centerFreq = REF_FREQ * std::pow(2.0f, pc / (float)NUM_HPCP_BINS); |
| 40 | |
| 41 | // Add contribution from each harmonic |
| 42 | for (size_t hIdx = 0; hIdx < mHarmonicWeights.size(); ++hIdx) { |
| 43 | float freq = peakFreq * pow(2., -mHarmonicWeights[hIdx].semitone / 12.0); |
| 44 | float harmonicWeight = mHarmonicWeights[hIdx].gain; |
| 45 | float d = std::fmod(12.0f * std::log2(freq / centerFreq), 12.0f); |
| 46 | if (std::abs(d) <= (0.5f * HPCP_WINDOW_LEN)) { |
| 47 | float w = std::pow(std::cos((juce::MathConstants<float>::pi * d) / HPCP_WINDOW_LEN), 2.0f); |
| 48 | mHPCP[frame][pcIdx] += (w * std::pow(peaks[i].gain, 2) * harmonicWeight * harmonicWeight); |
| 49 | if (mHPCP[frame][pcIdx] > curMax) curMax = mHPCP[frame][pcIdx]; |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // Normalize HPCP frame and clear low energy frames |
| 56 | float totalEnergy = 0.0f; |
| 57 | if (curMax > 0.0f) { |
| 58 | for (int pc = 0; pc < NUM_HPCP_BINS; ++pc) { |
| 59 | totalEnergy += mHPCP[frame][pc]; |
| 60 | mHPCP[frame][pc] /= curMax; |
| 61 | } |
| 62 | } |
| 63 | if (totalEnergy / NUM_HPCP_BINS < MIN_AVG_FRAME_ENERGY) { |
| 64 | std::fill(mHPCP[frame].begin(), mHPCP[frame].end(), 0.0f); |
| 65 | } |
| 66 | } |
| 67 | return &mHPCP; |
| 68 | } |
| 69 | |
| 70 | // From essentia: |
| 71 | // Builds a weighting table of harmonic contribution. Higher harmonics |