From efd646368bf8412624c1011ddd1be29269afc918 Mon Sep 17 00:00:00 2001 From: AI Date: Fri, 10 Jul 2026 13:59:43 +0530 Subject: [PATCH 1/2] Convert 2D Bar Graph visualization into 3D using Perspective Projection --- src/visualizations/BarVisualization.cpp | 138 +++++++++++++++++++++--- src/visualizations/vertexShader3D.glsl | 11 ++ 2 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 src/visualizations/vertexShader3D.glsl diff --git a/src/visualizations/BarVisualization.cpp b/src/visualizations/BarVisualization.cpp index cba47b7..fd0ccc6 100644 --- a/src/visualizations/BarVisualization.cpp +++ b/src/visualizations/BarVisualization.cpp @@ -3,7 +3,98 @@ #include "ColorUtils.h" #include #include +#include +namespace { + +void multiplyMatrix(const float* A, const float* B, float* C) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + C[i * 4 + j] = 0; + for (int k = 0; k < 4; k++) { + C[i * 4 + j] += A[i * 4 + k] * B[k * 4 + j]; + } + } + } +} + +void getPerspectiveMatrix(float fov, float aspect, float nearZ, float farZ, float* result) { + float f = 1.0f / tan(fov / 2.0f); + for(int i = 0; i < 16; i++) result[i] = 0.0f; + result[0 * 4 + 0] = f / aspect; + result[1 * 4 + 1] = f; + result[2 * 4 + 2] = (farZ + nearZ) / (nearZ - farZ); + result[2 * 4 + 3] = (2.0f * farZ * nearZ) / (nearZ - farZ); + result[3 * 4 + 2] = -1.0f; +} + +void getRotationMatrixX(float angle, float* result) { + for(int i = 0; i < 16; i++) result[i] = 0.0f; + float c = cos(angle); + float s = sin(angle); + result[0 * 4 + 0] = 1.0f; + result[1 * 4 + 1] = c; + result[1 * 4 + 2] = -s; + result[2 * 4 + 1] = s; + result[2 * 4 + 2] = c; + result[3 * 4 + 3] = 1.0f; +} + +void getRotationMatrixY(float angle, float* result) { + for(int i = 0; i < 16; i++) result[i] = 0.0f; + float c = cos(angle); + float s = sin(angle); + result[0 * 4 + 0] = c; + result[0 * 4 + 2] = s; + result[1 * 4 + 1] = 1.0f; + result[2 * 4 + 0] = -s; + result[2 * 4 + 2] = c; + result[3 * 4 + 3] = 1.0f; +} + +void getTranslationMatrix(float tx, float ty, float tz, float* result) { + for(int i = 0; i < 16; i++) result[i] = 0.0f; + result[0 * 4 + 0] = 1.0f; + result[1 * 4 + 1] = 1.0f; + result[2 * 4 + 2] = 1.0f; + result[3 * 4 + 3] = 1.0f; + result[0 * 4 + 3] = tx; + result[1 * 4 + 3] = ty; + result[2 * 4 + 3] = tz; +} + +void addCube(std::vector& vertices, float x, float y, float z, float w, float h, float d, Color c) { + float v[8][3] = { + {x, y, z}, {x+w, y, z}, {x+w, y+h, z}, {x, y+h, z}, + {x, y, z-d}, {x+w, y, z-d}, {x+w, y+h, z-d}, {x, y+h, z-d} + }; + int indices[36] = { + 0, 1, 2, 2, 3, 0, // front + 1, 5, 6, 6, 2, 1, // right + 5, 4, 7, 7, 6, 5, // back + 4, 0, 3, 3, 7, 4, // left + 3, 2, 6, 6, 7, 3, // top + 4, 5, 1, 1, 0, 4 // bottom + }; + for (int i = 0; i < 6; i++) { + float faceShade = 1.0f; + if (i == 1 || i == 3) faceShade = 0.7f; + else if (i == 4 || i == 5) faceShade = 0.5f; + else if (i == 2) faceShade = 0.8f; + + for (int j = 0; j < 6; j++) { + int idx = indices[i * 6 + j]; + vertices.push_back(v[idx][0]); + vertices.push_back(v[idx][1]); + vertices.push_back(v[idx][2]); + vertices.push_back(c.r * faceShade); + vertices.push_back(c.g * faceShade); + vertices.push_back(c.b * faceShade); + } + } +} + +} BarVisualization::BarVisualization() : window(nullptr), vbo(0), vao(0), smoothedFFT(128, 0.0f) {} BarVisualization::~BarVisualization() { @@ -43,16 +134,18 @@ bool BarVisualization::initialize(int windowWidth, int windowHeight) { glBindBuffer(GL_ARRAY_BUFFER, vbo); // ✅ Define vertex attribute layout (Position: location 0) - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // ✅ Define vertex attribute layout (Color: location 1) - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); + glEnable(GL_DEPTH_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - shaderProgram = createShaderProgram("visualizations/vertexShader.glsl", "visualizations/fragmentShader.glsl"); + shaderProgram = createShaderProgram("visualizations/vertexShader3D.glsl", "visualizations/fragmentShader.glsl"); if (shaderProgram == 0) { std::cerr << "ERROR: Failed to create shader program!" << std::endl; exit(1); @@ -64,9 +157,28 @@ bool BarVisualization::initialize(int windowWidth, int windowHeight) { } void BarVisualization::render(const std::vector& fftMagnitudes) { - glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(shaderProgram); // ✅ Ensure shaders are active before drawing + int width, height; + glfwGetWindowSize(window, &width, &height); + float aspect = (float)width / (height == 0 ? 1 : height); + + float proj[16], view[16], rotX[16], rotY[16], modelMatrix[16], viewProj[16], mvp[16]; + getPerspectiveMatrix(45.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f, proj); + getTranslationMatrix(0.0f, -0.5f, -3.0f, view); + + // Rotate to look down slightly and from an angle + getRotationMatrixX(20.0f * (M_PI / 180.0f), rotX); + getRotationMatrixY(30.0f * (M_PI / 180.0f), rotY); + multiplyMatrix(rotX, rotY, modelMatrix); + + multiplyMatrix(proj, view, viewProj); + multiplyMatrix(viewProj, modelMatrix, mvp); + + GLint mvpLoc = glGetUniformLocation(shaderProgram, "MVP"); + glUniformMatrix4fv(mvpLoc, 1, GL_TRUE, mvp); + if (fftMagnitudes.empty()) { std::cerr << "ERROR: No FFT data received!\n"; return; @@ -75,9 +187,9 @@ void BarVisualization::render(const std::vector& fftMagnitudes) { size_t numBars = fftMagnitudes.size() / 8; // Reduce number of bars for clarity std::vector vertices; - float barWidth = 2.0f / numBars; // Normalize to OpenGL's -1 to 1 range - float maxHeight = 0.9f; // Ensure bars stay within screen height - float minHeight = 0.02f; // Minimum bar height to keep them visible + float barWidth = 4.0f / numBars; // Wider range for 3D + float maxHeight = 3.0f; // Taller bars in 3D space + float minHeight = 0.05f; // Minimum bar height to keep them visible // Ensure smoothedFFT is the correct size if (smoothedFFT.size() != numBars) { @@ -113,18 +225,10 @@ void BarVisualization::render(const std::vector& fftMagnitudes) { float height = smoothedFFT[i] * maxHeight; if (height < minHeight) height = minHeight; // Prevents bars from disappearing - float x = -1.0f + i * barWidth; + float x = -2.0f + i * barWidth; Color color = getColorFromMagnitude(smoothedFFT[i], 0.0f, 1.0f); // ✅ Dynamic color - vertices.insert(vertices.end(), { - x, -1.0f, color.r, color.g, color.b, - x + barWidth * 0.8f, -1.0f, color.r, color.g, color.b, - x, height - 1.0f, color.r, color.g, color.b, - - x + barWidth * 0.8f, -1.0f, color.r, color.g, color.b, - x + barWidth * 0.8f, height - 1.0f, color.r, color.g, color.b, - x, height - 1.0f, color.r, color.g, color.b - }); + addCube(vertices, x, 0.0f, 0.0f, barWidth * 0.8f, height, 0.4f, color); } diff --git a/src/visualizations/vertexShader3D.glsl b/src/visualizations/vertexShader3D.glsl new file mode 100644 index 0000000..3e31651 --- /dev/null +++ b/src/visualizations/vertexShader3D.glsl @@ -0,0 +1,11 @@ +#version 330 core +layout(location = 0) in vec3 aPos; +layout(location = 1) in vec3 aColor; + +out vec3 vertexColor; +uniform mat4 MVP; + +void main() { + gl_Position = MVP * vec4(aPos, 1.0); + vertexColor = aColor; +} From 9877ac98693fc21861db455fecd1c8fb279fdcab Mon Sep 17 00:00:00 2001 From: AI Date: Sun, 12 Jul 2026 11:46:12 +0530 Subject: [PATCH 2/2] Feat: Add Audio Variables Resolves #2. Adds bass, mid, and treble extraction with attenuated versions. --- audio/FFTProcessor.cpp | 57 ++++++++++++++++++++++++++++++++++++++++-- audio/FFTProcessor.h | 13 +++++++++- src/Audio.cpp | 9 ++++++- src/Audio.h | 7 ++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/audio/FFTProcessor.cpp b/audio/FFTProcessor.cpp index 2ed50fc..b433005 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), + bass(0.0f), mid(0.0f), treble(0.0f), bassAtt(0.0f), midAtt(0.0f), trebleAtt(0.0f) { // Allocate FFT input/output arrays fftInput = new float[bufferSize]; fftOutput = new float[bufferSize]; @@ -45,8 +46,60 @@ void FFTProcessor::computeFFT(const vector& audioData) { float imag = (i == 0 || i == bufferSize / 2) ? 0 : fftOutput[bufferSize - i]; magnitudes[i] = sqrt(real * real + imag * imag); } + + // Compute Audio Variables (Bass, Mid, Treble) + float newBass = 0.0f; + float newMid = 0.0f; + float newTreble = 0.0f; + + int bassCount = 0; + int midCount = 0; + int trebleCount = 0; + + for (size_t i = 1; i < bufferSize / 2; ++i) { + float freq = static_cast(i * sampleRate) / bufferSize; + float mag = magnitudes[i]; + + if (freq >= 20.0f && freq < 250.0f) { + newBass += mag; + bassCount++; + } else if (freq >= 250.0f && freq < 2000.0f) { + newMid += mag; + midCount++; + } else if (freq >= 2000.0f && freq < 20000.0f) { + newTreble += mag; + trebleCount++; + } + } + + if (bassCount > 0) newBass /= bassCount; + if (midCount > 0) newMid /= midCount; + if (trebleCount > 0) newTreble /= trebleCount; + + bass = newBass; + mid = newMid; + treble = newTreble; + + // EMA for attenuated values (alpha = 0.2) + float alpha = 0.2f; + if (bassAtt == 0.0f && midAtt == 0.0f && trebleAtt == 0.0f) { + bassAtt = bass; + midAtt = mid; + trebleAtt = treble; + } else { + bassAtt = alpha * bass + (1.0f - alpha) * bassAtt; + midAtt = alpha * mid + (1.0f - alpha) * midAtt; + trebleAtt = alpha * treble + (1.0f - alpha) * trebleAtt; + } } const vector& FFTProcessor::getMagnitudes() const { return magnitudes; } + +float FFTProcessor::getBass() const { return bass; } +float FFTProcessor::getMid() const { return mid; } +float FFTProcessor::getTreble() const { return treble; } +float FFTProcessor::getBassAtt() const { return bassAtt; } +float FFTProcessor::getMidAtt() const { return midAtt; } +float FFTProcessor::getTrebleAtt() const { return trebleAtt; } diff --git a/audio/FFTProcessor.h b/audio/FFTProcessor.h index 82cad36..90e79b1 100644 --- a/audio/FFTProcessor.h +++ b/audio/FFTProcessor.h @@ -7,18 +7,29 @@ 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; + float getMid() const; + float getTreble() const; + float getBassAtt() const; + float getMidAtt() const; + float getTrebleAtt() const; + private: size_t bufferSize; + int sampleRate; vector magnitudes; float* fftInput; float* fftOutput; void* fftPlan; // Plan type depends on FFTW version + + float bass, mid, treble; + float bassAtt, midAtt, trebleAtt; }; #endif // FFTPROCESSOR_H diff --git a/src/Audio.cpp b/src/Audio.cpp index aec08f6..5ad4842 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,13 @@ 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..05dd13a 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -18,6 +18,13 @@ class AudioProcessor { vector getFFTData(); + float getBass() const; + float getMid() const; + float getTreble() const; + float getBassAtt() const; + float getMidAtt() const; + float getTrebleAtt() const; + bool startProcessing(); void cleanup();