osci-render/Source/audio/PitchDetector.cpp

69 wiersze
1.9 KiB
C++

#include "PitchDetector.h"
#include "../PluginProcessor.h"
PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : juce::Thread("PitchDetector"), audioProcessor(audioProcessor) {
startThread();
}
PitchDetector::~PitchDetector() {
audioProcessor.consumerStop(consumer);
stopThread(1000);
}
void PitchDetector::run() {
while (!threadShouldExit()) {
consumer = audioProcessor.consumerRegister(buffer);
audioProcessor.consumerRead(consumer);
// buffer is for 2 channels, so we need to only use one
for (int i = 0; i < fftSize; i++) {
fftData[i] = buffer[2 * i];
}
forwardFFT.performFrequencyOnlyForwardTransform(fftData.data());
// get frequency of the peak
int maxIndex = 0;
for (int i = 0; i < fftSize / 2; ++i) {
if (frequencyFromIndex(i) < 20 || frequencyFromIndex(i) > 20000) {
continue;
}
auto current = fftData[i];
if (current > fftData[maxIndex]) {
maxIndex = i;
}
}
frequency = frequencyFromIndex(maxIndex);
triggerAsyncUpdate();
}
}
void PitchDetector::handleAsyncUpdate() {
juce::SpinLock::ScopedLockType scope(lock);
for (auto& callback : callbacks) {
callback(frequency);
}
}
int PitchDetector::addCallback(std::function<void(float)> callback) {
juce::SpinLock::ScopedLockType scope(lock);
callbacks.push_back(callback);
return callbacks.size() - 1;
}
void PitchDetector::removeCallback(int index) {
juce::SpinLock::ScopedLockType scope(lock);
callbacks.erase(callbacks.begin() + index);
}
void PitchDetector::setSampleRate(float sampleRate) {
this->sampleRate = sampleRate;
}
float PitchDetector::frequencyFromIndex(int index) {
auto binWidth = sampleRate / fftSize;
return index * binWidth;
}