diff --git a/audio/FFTProcessor.cpp b/audio/FFTProcessor.cpp index 2ed50fc..ecdd678 100644 --- a/audio/FFTProcessor.cpp +++ b/audio/FFTProcessor.cpp @@ -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]; @@ -45,8 +46,51 @@ void FFTProcessor::computeFFT(const vector& audioData) { float imag = (i == 0 || i == bufferSize / 2) ? 0 : fftOutput[bufferSize - i]; magnitudes[i] = sqrt(real * real + imag * imag); } + + computeFrequencyBands(); } const vector& 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(sampleRate) / static_cast(bufferSize); + float total = 0.0f; + size_t count = 0; + + for (size_t i = 0; i < magnitudes.size(); ++i) { + const float frequency = static_cast(i) * binWidth; + if (frequency >= minFrequency && frequency < maxFrequency) { + total += magnitudes[i]; + ++count; + } + } + + return count > 0 ? total / static_cast(count) : 0.0f; +} + +float FFTProcessor::smooth(float currentValue, float previousValue) const { + return previousValue + EMA_ALPHA * (currentValue - previousValue); +} diff --git a/audio/FFTProcessor.h b/audio/FFTProcessor.h index 82cad36..3fe5e6b 100644 --- a/audio/FFTProcessor.h +++ b/audio/FFTProcessor.h @@ -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& audioData); const vector& getMagnitudes() const; + const FrequencyBands& getFrequencyBands() const; private: + static constexpr float EMA_ALPHA = 0.1f; + size_t bufferSize; + int sampleRate; vector 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 diff --git a/src/Audio.cpp b/src/Audio.cpp index aec08f6..0187240 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(); } +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(userData); diff --git a/src/Audio.h b/src/Audio.h index a5f365e..7523ec7 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -6,6 +6,7 @@ #include #include #include +#include "../audio/FFTProcessor.h" using namespace std; @@ -17,6 +18,7 @@ class AudioProcessor { bool loadAudioFile(const string& fileName); vector getFFTData(); + FrequencyBands getFrequencyBands() const; bool startProcessing();