Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions audio/FFTProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

using namespace std;

FFTProcessor::FFTProcessor(size_t bufferSize)
: bufferSize(bufferSize), magnitudes(bufferSize / 2, 0.0f) {
FFTProcessor::FFTProcessor(size_t bufferSize, int sampleRate)
: bufferSize(bufferSize), sampleRate(sampleRate), magnitudes(bufferSize / 2, 0.0f),
bands{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} {
// Allocate FFT input/output arrays
fftInput = new float[bufferSize];
fftOutput = new float[bufferSize];
Expand Down Expand Up @@ -45,8 +46,51 @@ void FFTProcessor::computeFFT(const vector<float>& audioData) {
float imag = (i == 0 || i == bufferSize / 2) ? 0 : fftOutput[bufferSize - i];
magnitudes[i] = sqrt(real * real + imag * imag);
}

computeFrequencyBands();
}

const vector<float>& FFTProcessor::getMagnitudes() const {
return magnitudes;
}

const FrequencyBands& FFTProcessor::getFrequencyBands() const {
return bands;
}

void FFTProcessor::computeFrequencyBands() {
const float bass = averageMagnitudeForRange(20.0f, 250.0f);
const float mid = averageMagnitudeForRange(250.0f, 4000.0f);
const float treble = averageMagnitudeForRange(4000.0f, sampleRate / 2.0f);

bands.bass = bass;
bands.mid = mid;
bands.treble = treble;
bands.bassAtt = smooth(bass, bands.bassAtt);
bands.midAtt = smooth(mid, bands.midAtt);
bands.trebleAtt = smooth(treble, bands.trebleAtt);
}

float FFTProcessor::averageMagnitudeForRange(float minFrequency, float maxFrequency) const {
if (sampleRate <= 0 || bufferSize == 0 || magnitudes.empty()) {
return 0.0f;
}

const float binWidth = static_cast<float>(sampleRate) / static_cast<float>(bufferSize);
float total = 0.0f;
size_t count = 0;

for (size_t i = 0; i < magnitudes.size(); ++i) {
const float frequency = static_cast<float>(i) * binWidth;
if (frequency >= minFrequency && frequency < maxFrequency) {
total += magnitudes[i];
++count;
}
}

return count > 0 ? total / static_cast<float>(count) : 0.0f;
}

float FFTProcessor::smooth(float currentValue, float previousValue) const {
return previousValue + EMA_ALPHA * (currentValue - previousValue);
}
20 changes: 19 additions & 1 deletion audio/FFTProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,38 @@

using namespace std;

struct FrequencyBands {
float bass;
float mid;
float treble;
float bassAtt;
float midAtt;
float trebleAtt;
};

class FFTProcessor {
public:
FFTProcessor(size_t bufferSize);
FFTProcessor(size_t bufferSize, int sampleRate = 44100);
~FFTProcessor();

void computeFFT(const vector<float>& audioData);
const vector<float>& getMagnitudes() const;
const FrequencyBands& getFrequencyBands() const;

private:
static constexpr float EMA_ALPHA = 0.1f;

size_t bufferSize;
int sampleRate;
vector<float> magnitudes;
FrequencyBands bands;
float* fftInput;
float* fftOutput;
void* fftPlan; // Plan type depends on FFTW version

void computeFrequencyBands();
float averageMagnitudeForRange(float minFrequency, float maxFrequency) const;
float smooth(float currentValue, float previousValue) const;
};

#endif // FFTPROCESSOR_H
10 changes: 9 additions & 1 deletion src/Audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ bool AudioProcessor::loadAudioFile(const string& fileName) {
cerr << "Failed to load audio file: " << fileName << endl;
return false;
}
fftProcessor = new FFTProcessor(bufferSize);
fftProcessor = new FFTProcessor(bufferSize, audioReader->getSampleRate());
return true;
}

Expand Down Expand Up @@ -62,6 +62,14 @@ vector<float> AudioProcessor::getFFTData() {
return fftProcessor->getMagnitudes();
}

FrequencyBands AudioProcessor::getFrequencyBands() const {
if (!fftProcessor) {
return {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}

return fftProcessor->getFrequencyBands();
}

int AudioProcessor::audioCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) {
AudioProcessor* processor = static_cast<AudioProcessor*>(userData);
Expand Down
2 changes: 2 additions & 0 deletions src/Audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <mutex>
#include <condition_variable>
#include <portaudio.h>
#include "../audio/FFTProcessor.h"

using namespace std;

Expand All @@ -17,6 +18,7 @@ class AudioProcessor {
bool loadAudioFile(const string& fileName);

vector<float> getFFTData();
FrequencyBands getFrequencyBands() const;

bool startProcessing();

Expand Down