diff --git a/audio/FFTProcessor.cpp b/audio/FFTProcessor.cpp index 2ed50fc..5596402 100644 --- a/audio/FFTProcessor.cpp +++ b/audio/FFTProcessor.cpp @@ -2,11 +2,15 @@ #include #include #include +#include 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), + bass(0.0f), mid(0.0f), treble(0.0f), + bassAtt(0.0f), midAtt(0.0f), trebleAtt(0.0f), + smoothingFactor(0.2f) { // ProjectM style EMA smoothing factor // Allocate FFT input/output arrays fftInput = new float[bufferSize]; fftOutput = new float[bufferSize]; @@ -45,6 +49,46 @@ void FFTProcessor::computeFFT(const vector& audioData) { float imag = (i == 0 || i == bufferSize / 2) ? 0 : fftOutput[bufferSize - i]; magnitudes[i] = sqrt(real * real + imag * imag); } + + // Calculate bass, mid, treble and their attenuated versions + calculateBands(); +} + +void FFTProcessor::calculateBands() { + // ProjectM style frequency bands (approximate) + // Bass: 20Hz - 250Hz + // Mid: 250Hz - 2000Hz + // Treble: 2000Hz - 20000Hz + + float hzPerBin = static_cast(sampleRate) / bufferSize; + + size_t bassStartBin = max((size_t)1, static_cast(20.0f / hzPerBin)); + size_t bassEndBin = min(bufferSize / 2, static_cast(250.0f / hzPerBin)); + + size_t midStartBin = bassEndBin; + size_t midEndBin = min(bufferSize / 2, static_cast(2000.0f / hzPerBin)); + + size_t trebleStartBin = midEndBin; + size_t trebleEndBin = min(bufferSize / 2, static_cast(20000.0f / hzPerBin)); + + auto calculateEnergy = [&](size_t start, size_t end) -> float { + if (start >= end) return 0.0f; + float energy = 0.0f; + for (size_t i = start; i < end; ++i) { + energy += magnitudes[i]; + } + return energy / (end - start); // Average energy per bin in this band + }; + + // Calculate raw values + bass = calculateEnergy(bassStartBin, bassEndBin); + mid = calculateEnergy(midStartBin, midEndBin); + treble = calculateEnergy(trebleStartBin, trebleEndBin); + + // Calculate smoothed/attenuated values using EMA (Exponential Moving Average) + bassAtt = (bass * smoothingFactor) + (bassAtt * (1.0f - smoothingFactor)); + midAtt = (mid * smoothingFactor) + (midAtt * (1.0f - smoothingFactor)); + trebleAtt = (treble * smoothingFactor) + (trebleAtt * (1.0f - smoothingFactor)); } const vector& FFTProcessor::getMagnitudes() const { diff --git a/audio/FFTProcessor.h b/audio/FFTProcessor.h index 82cad36..aa5cae3 100644 --- a/audio/FFTProcessor.h +++ b/audio/FFTProcessor.h @@ -7,18 +7,42 @@ using namespace std; class FFTProcessor { public: - FFTProcessor(size_t bufferSize); + FFTProcessor(size_t bufferSize, int sampleRate = 44100); ~FFTProcessor(); void computeFFT(const vector& audioData); const vector& getMagnitudes() const; + float getBass() const { return bass; } + float getMid() const { return mid; } + float getTreble() const { return treble; } + + float getBassAtt() const { return bassAtt; } + float getMidAtt() const { return midAtt; } + float getTrebleAtt() const { return trebleAtt; } + private: size_t bufferSize; + int sampleRate; vector magnitudes; float* fftInput; float* fftOutput; void* fftPlan; // Plan type depends on FFTW version + + // Raw band values + float bass; + float mid; + float treble; + + // Attenuated/smoothed band values using EMA + float bassAtt; + float midAtt; + float trebleAtt; + + // EMA smoothing factor (0.0 to 1.0) + float smoothingFactor; + + void calculateBands(); }; #endif // FFTPROCESSOR_H diff --git a/src/Audio.cpp b/src/Audio.cpp index aec08f6..2525f8d 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -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; } @@ -62,6 +62,14 @@ vector AudioProcessor::getFFTData() { return fftProcessor->getMagnitudes(); } +float AudioProcessor::getBass() const { return fftProcessor ? fftProcessor->getBass() : 0.0f; } +float AudioProcessor::getMid() const { return fftProcessor ? fftProcessor->getMid() : 0.0f; } +float AudioProcessor::getTreble() const { return fftProcessor ? fftProcessor->getTreble() : 0.0f; } + +float AudioProcessor::getBassAtt() const { return fftProcessor ? fftProcessor->getBassAtt() : 0.0f; } +float AudioProcessor::getMidAtt() const { return fftProcessor ? fftProcessor->getMidAtt() : 0.0f; } +float AudioProcessor::getTrebleAtt() const { return fftProcessor ? fftProcessor->getTrebleAtt() : 0.0f; } + int AudioProcessor::audioCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) { AudioProcessor* processor = static_cast(userData); diff --git a/src/Audio.h b/src/Audio.h index a5f365e..5439b09 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -22,6 +22,15 @@ class AudioProcessor { void cleanup(); + // Frequency bands + float getBass() const; + float getMid() const; + float getTreble() const; + + float getBassAtt() const; + float getMidAtt() const; + float getTrebleAtt() const; + private: size_t bufferSize; class AudioFileReader* audioReader;