REVIEWED: audio_fft_spectrum_visualizer, not working on web

This commit is contained in:
Ray 2025-11-19 12:30:38 +01:00
parent e3738c1b17
commit 5fdf178969
5 changed files with 133 additions and 49 deletions

View File

@ -19,11 +19,19 @@
********************************************************************************************/ ********************************************************************************************/
#include "raylib.h" #include "raylib.h"
#include "raymath.h" #include "raymath.h"
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
#define MONO 1 #define MONO 1
#define SAMPLE_RATE 44100 #define SAMPLE_RATE 44100
#define SAMPLE_RATE_F 44100.0f #define SAMPLE_RATE_F 44100.0f
@ -77,7 +85,8 @@ int main(void)
RenderTexture2D bufferA = LoadRenderTexture(screenWidth, screenHeight); RenderTexture2D bufferA = LoadRenderTexture(screenWidth, screenHeight);
Vector2 iResolution = { (float)screenWidth, (float)screenHeight }; Vector2 iResolution = { (float)screenWidth, (float)screenHeight };
Shader shader = LoadShader(NULL, "resources/fft.glsl"); Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/fft.fs", GLSL_VERSION));
int iResolutionLocation = GetShaderLocation(shader, "iResolution"); int iResolutionLocation = GetShaderLocation(shader, "iResolution");
int iChannel0Location = GetShaderLocation(shader, "iChannel0"); int iChannel0Location = GetShaderLocation(shader, "iChannel0");
SetShaderValue(shader, iResolutionLocation, &iResolution, SHADER_UNIFORM_VEC2); SetShaderValue(shader, iResolutionLocation, &iResolution, SHADER_UNIFORM_VEC2);
@ -86,6 +95,7 @@ int main(void)
InitAudioDevice(); InitAudioDevice();
SetAudioStreamBufferSizeDefault(AUDIO_STREAM_RING_BUFFER_SIZE); SetAudioStreamBufferSizeDefault(AUDIO_STREAM_RING_BUFFER_SIZE);
// WARNING: Memory out-of-bounds on PLATFORM_WEB
Wave wav = LoadWave("resources/country.mp3"); Wave wav = LoadWave("resources/country.mp3");
WaveFormat(&wav, SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO); WaveFormat(&wav, SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO);
@ -95,10 +105,10 @@ int main(void)
int fftHistoryLen = (int)ceilf(FFT_HISTORICAL_SMOOTHING_DUR/WINDOW_TIME) + 1; int fftHistoryLen = (int)ceilf(FFT_HISTORICAL_SMOOTHING_DUR/WINDOW_TIME) + 1;
FFTData fft = { FFTData fft = {
.spectrum = malloc(sizeof(FFTComplex)*FFT_WINDOW_SIZE), .spectrum = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
.workBuffer = malloc(sizeof(FFTComplex)*FFT_WINDOW_SIZE), .workBuffer = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
.prevMagnitudes = calloc(BUFFER_SIZE, sizeof(float)), .prevMagnitudes = RL_CALLOC(BUFFER_SIZE, sizeof(float)),
.fftHistory = calloc(fftHistoryLen, sizeof(float[BUFFER_SIZE])), .fftHistory = RL_CALLOC(fftHistoryLen, sizeof(float[BUFFER_SIZE])),
.fftHistoryLen = fftHistoryLen, .fftHistoryLen = fftHistoryLen,
.historyPos = 0, .historyPos = 0,
.lastFftTime = 0.0, .lastFftTime = 0.0,
@ -127,15 +137,12 @@ int main(void)
int right = (wav.channels == 2)? wavPCM16[wavCursor*2 + 1] : left; int right = (wav.channels == 2)? wavPCM16[wavCursor*2 + 1] : left;
chunkSamples[i] = (short)((left + right)/2); chunkSamples[i] = (short)((left + right)/2);
if (++wavCursor >= wav.frameCount) if (++wavCursor >= wav.frameCount) wavCursor = 0;
wavCursor = 0;
} }
UpdateAudioStream(audioStream, chunkSamples, AUDIO_STREAM_RING_BUFFER_SIZE); UpdateAudioStream(audioStream, chunkSamples, AUDIO_STREAM_RING_BUFFER_SIZE);
for (int i = 0; i < FFT_WINDOW_SIZE; i++) for (int i = 0; i < FFT_WINDOW_SIZE; i++) audioSamples[i] = (chunkSamples[i*2] + chunkSamples[i*2 + 1])*0.5f/32767.0f;
audioSamples[i] = (chunkSamples[i*2] + chunkSamples[i*2 + 1])*0.5f/32767.0f;
} }
CaptureFrame(&fft, audioSamples); CaptureFrame(&fft, audioSamples);
@ -146,14 +153,16 @@ int main(void)
// Draw // Draw
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK);
ClearBackground(RAYWHITE);
BeginShaderMode(shader); BeginShaderMode(shader);
SetShaderValueTexture(shader, iChannel0Location, fftTexture); SetShaderValueTexture(shader, iChannel0Location, fftTexture);
DrawTextureRec(bufferA.texture, DrawTextureRec(bufferA.texture,
(Rectangle){ 0, 0, (float)screenWidth, (float)-screenHeight }, (Rectangle){ 0, 0, (float)screenWidth, (float)-screenHeight },
(Vector2){ 0, 0 }, (Vector2){ 0, 0 }, WHITE);
WHITE);
EndShaderMode(); EndShaderMode();
EndDrawing(); EndDrawing();
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
} }
@ -168,10 +177,10 @@ int main(void)
UnloadWave(wav); UnloadWave(wav);
CloseAudioDevice(); CloseAudioDevice();
free(fft.spectrum); RL_FREE(fft.spectrum);
free(fft.workBuffer); RL_FREE(fft.workBuffer);
free(fft.prevMagnitudes); RL_FREE(fft.prevMagnitudes);
free(fft.fftHistory); RL_FREE(fft.fftHistory);
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------

View File

@ -1,32 +0,0 @@
#version 330
in vec2 fragTexCoord;
in vec4 fragColor;
out vec4 finalColor;
uniform vec2 iResolution;
uniform sampler2D iChannel0;
const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
const float FFT_ROW = 0.0;
const float NUM_OF_BINS = 512.0;
void main() {
vec2 fragCoord = fragTexCoord*iResolution;
float cell_width = iResolution.x/NUM_OF_BINS;
float bin_index = floor(fragCoord.x/cell_width);
float local_x = mod(fragCoord.x, cell_width);
float bar_width = cell_width - 1.0;
vec4 color = BLACK;
if (local_x <= bar_width) {
float sample_x = (bin_index + 0.5)/NUM_OF_BINS;
vec2 sample_coord = vec2(sample_x, FFT_ROW);
float amplitude = texture(iChannel0, sample_coord).r; // only filled the red channel, all channels left open for alternative use
if (fragTexCoord.y < amplitude) {
color = WHITE;
}
}
finalColor = color;
}

View File

@ -0,0 +1,37 @@
#version 100
precision mediump float;
// Input vertex attributes (from vertex shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
// Input uniform values
uniform vec2 iResolution;
uniform sampler2D iChannel0;
const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
const float FFT_ROW = 0.0;
const float NUM_OF_BINS = 512.0;
void main()
{
vec2 fragCoord = fragTexCoord*iResolution;
float cellWidth = iResolution.x/NUM_OF_BINS;
float binIndex = floor(fragCoord.x/cellWidth);
float localX = mod(fragCoord.x, cellWidth);
float barWidth = cellWidth - 1.0;
vec4 color = WHITE;
if (localX <= barWidth)
{
float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
vec2 sampleCoord = vec2(sampleX, FFT_ROW);
float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
if (fragTexCoord.y < amplitude) color = BLACK;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,35 @@
#version 120
// Input vertex attributes (from vertex shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
// Input uniform values
uniform vec2 iResolution;
uniform sampler2D iChannel0;
const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
const float FFT_ROW = 0.0;
const float NUM_OF_BINS = 512.0;
void main()
{
vec2 fragCoord = fragTexCoord*iResolution;
float cellWidth = iResolution.x/NUM_OF_BINS;
float binIndex = floor(fragCoord.x/cellWidth);
float localX = mod(fragCoord.x, cellWidth);
float barWidth = cellWidth - 1.0;
vec4 color = WHITE;
if (localX <= barWidth)
{
float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
vec2 sampleCoord = vec2(sampleX, FFT_ROW);
float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
if (fragTexCoord.y < amplitude) color = BLACK;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,35 @@
#version 330
in vec2 fragTexCoord;
in vec4 fragColor;
out vec4 finalColor;
uniform vec2 iResolution;
uniform sampler2D iChannel0;
const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
const float FFT_ROW = 0.0;
const float NUM_OF_BINS = 512.0;
void main()
{
vec2 fragCoord = fragTexCoord*iResolution;
float cellWidth = iResolution.x/NUM_OF_BINS;
float binIndex = floor(fragCoord.x/cellWidth);
float localX = mod(fragCoord.x, cellWidth);
float barWidth = cellWidth - 1.0;
vec4 color = WHITE;
if (localX <= barWidth)
{
float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
vec2 sampleCoord = vec2(sampleX, FFT_ROW);
float amplitude = texture(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
if (fragTexCoord.y < amplitude) color = BLACK;
}
finalColor = color;
}