feat: add FFT-based audio spectrum analyzer module
- Implement radix-2 Cooley-Tukey FFT algorithm - Add windowing functions: Hann, Hamming, Blackman, Rectangular - Support configurable frequency bin count - Add peak detection with configurable decay rate - Include temporal smoothing for visualization - Add spectrum analysis example program - Proper memory management with init/close lifecycle Bug-fix: Add buffer bounds checking to prevent overflow when sample count is not power of 2 Signed-off-by: Srikanth Patchava <spatchava@meta.com>
This commit is contained in:
parent
f9bf646388
commit
872dbb7264
126
examples/audio/audio_spectrum.c
Normal file
126
examples/audio/audio_spectrum.c
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* raylib [audio] example - Audio Spectrum Analyzer
|
||||||
|
*
|
||||||
|
* Example originally created with raylib, using raudio_analyzer module
|
||||||
|
*
|
||||||
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
|
* BSD-like license that allows static linking with closed source software
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 raylib contributors
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "../src/raudio_analyzer.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 800
|
||||||
|
#define SCREEN_HEIGHT 450
|
||||||
|
#define NUM_BARS 64 // Number of frequency bars to draw
|
||||||
|
#define FFT_SIZE 512 // FFT bin count (must be power of 2)
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [audio] example - spectrum analyzer");
|
||||||
|
InitAudioDevice();
|
||||||
|
|
||||||
|
Music music = LoadMusicStream("resources/guitar_noodling.ogg");
|
||||||
|
PlayMusicStream(music);
|
||||||
|
|
||||||
|
// Configure and initialize spectrum analyzer
|
||||||
|
SpectrumConfig config = {
|
||||||
|
.binCount = FFT_SIZE,
|
||||||
|
.windowFunc = WINDOW_HANN,
|
||||||
|
.smoothingFactor = 0.8f,
|
||||||
|
.peakDecayRate = 0.95f,
|
||||||
|
.sampleRate = 44100,
|
||||||
|
};
|
||||||
|
InitSpectrumAnalyzer(config);
|
||||||
|
|
||||||
|
float samples[FFT_SIZE] = { 0 }; // Buffer for audio samples
|
||||||
|
|
||||||
|
SetTargetFPS(60);
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
while (!WindowShouldClose())
|
||||||
|
{
|
||||||
|
// Update
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
UpdateMusicStream(music);
|
||||||
|
|
||||||
|
// Feed samples into spectrum analyzer
|
||||||
|
UpdateSpectrum(samples, FFT_SIZE);
|
||||||
|
ApplySmoothing();
|
||||||
|
|
||||||
|
float *spectrum = GetSpectrumData();
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
BeginDrawing();
|
||||||
|
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
// Draw frequency bars
|
||||||
|
int barWidth = SCREEN_WIDTH / NUM_BARS;
|
||||||
|
int binsPerBar = (FFT_SIZE / 2) / NUM_BARS;
|
||||||
|
if (binsPerBar < 1) binsPerBar = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_BARS; i++)
|
||||||
|
{
|
||||||
|
// Average magnitudes across bins for this bar
|
||||||
|
float magnitude = 0.0f;
|
||||||
|
for (int j = 0; j < binsPerBar; j++)
|
||||||
|
{
|
||||||
|
int binIndex = i * binsPerBar + j;
|
||||||
|
if (binIndex < FFT_SIZE / 2 && spectrum != NULL)
|
||||||
|
{
|
||||||
|
magnitude += spectrum[binIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
magnitude /= (float)binsPerBar;
|
||||||
|
|
||||||
|
// Scale for visual display
|
||||||
|
int barHeight = (int)(magnitude * SCREEN_HEIGHT * 4.0f);
|
||||||
|
if (barHeight > SCREEN_HEIGHT) barHeight = SCREEN_HEIGHT;
|
||||||
|
|
||||||
|
// Color gradient from green (low) to red (high)
|
||||||
|
Color barColor = (Color){
|
||||||
|
(unsigned char)(255 * i / NUM_BARS),
|
||||||
|
(unsigned char)(255 * (NUM_BARS - i) / NUM_BARS),
|
||||||
|
50,
|
||||||
|
255
|
||||||
|
};
|
||||||
|
|
||||||
|
DrawRectangle(
|
||||||
|
i * barWidth,
|
||||||
|
SCREEN_HEIGHT - barHeight,
|
||||||
|
barWidth - 2,
|
||||||
|
barHeight,
|
||||||
|
barColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display info
|
||||||
|
DrawText("AUDIO SPECTRUM ANALYZER", 10, 10, 20, DARKGRAY);
|
||||||
|
DrawText(TextFormat("Peak Freq: %.1f Hz", GetPeakFrequency()), 10, 40, 16, GRAY);
|
||||||
|
DrawText(TextFormat("Peak Mag: %.4f", GetPeakMagnitude()), 10, 60, 16, GRAY);
|
||||||
|
DrawFPS(SCREEN_WIDTH - 90, 10);
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
// De-Initialization
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
CloseSpectrumAnalyzer();
|
||||||
|
UnloadMusicStream(music);
|
||||||
|
CloseAudioDevice();
|
||||||
|
CloseWindow();
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
332
src/raudio_analyzer.c
Normal file
332
src/raudio_analyzer.c
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
/**********************************************************************************************
|
||||||
|
*
|
||||||
|
* raudio_analyzer - Audio spectrum analysis module for raylib
|
||||||
|
*
|
||||||
|
* IMPLEMENTATION:
|
||||||
|
* Radix-2 Cooley-Tukey FFT with windowing, peak detection, and temporal smoothing.
|
||||||
|
* Handles non-power-of-2 input by padding/truncating to nearest power of 2.
|
||||||
|
*
|
||||||
|
* LICENSE: zlib/libpng
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 raylib contributors
|
||||||
|
*
|
||||||
|
* This software is provided "as-is", without any express or implied warranty. In no event
|
||||||
|
* will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
**********************************************************************************************/
|
||||||
|
|
||||||
|
#include "raudio_analyzer.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef PI
|
||||||
|
#define PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Internal state
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
static float *spectrumMagnitudes = NULL; // Current magnitude per frequency bin
|
||||||
|
static float *spectrumSmoothed = NULL; // Smoothed magnitude per frequency bin
|
||||||
|
static float *peakValues = NULL; // Peak-hold values per bin
|
||||||
|
static float *fftReal = NULL; // FFT real component buffer
|
||||||
|
static float *fftImag = NULL; // FFT imaginary component buffer
|
||||||
|
static float *windowCoeffs = NULL; // Precomputed window coefficients
|
||||||
|
static SpectrumConfig analyzerConfig = { 0 };
|
||||||
|
static bool analyzerReady = false;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Internal helpers: forward declarations
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
static int NextPowerOfTwo(int n);
|
||||||
|
static void BitReversalPermutation(float *real, float *imag, int n);
|
||||||
|
static void ComputeFFT(float *real, float *imag, int n);
|
||||||
|
static void ComputeWindowCoefficients(float *coeffs, int n, WindowFunction func);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Lifecycle management
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Initialize the spectrum analyzer, allocating all internal buffers
|
||||||
|
void InitSpectrumAnalyzer(SpectrumConfig config)
|
||||||
|
{
|
||||||
|
// Ensure bin count is a power of 2
|
||||||
|
config.binCount = NextPowerOfTwo(config.binCount);
|
||||||
|
if (config.binCount < 4) config.binCount = 4;
|
||||||
|
if (config.binCount > MAX_SPECTRUM_BINS) config.binCount = MAX_SPECTRUM_BINS;
|
||||||
|
|
||||||
|
// Clamp parameters to valid ranges
|
||||||
|
if (config.smoothingFactor < 0.0f) config.smoothingFactor = 0.0f;
|
||||||
|
if (config.smoothingFactor > 1.0f) config.smoothingFactor = 1.0f;
|
||||||
|
if (config.peakDecayRate < 0.0f) config.peakDecayRate = 0.0f;
|
||||||
|
if (config.peakDecayRate > 1.0f) config.peakDecayRate = 1.0f;
|
||||||
|
if (config.sampleRate <= 0) config.sampleRate = 44100;
|
||||||
|
|
||||||
|
analyzerConfig = config;
|
||||||
|
|
||||||
|
// Allocate buffers
|
||||||
|
spectrumMagnitudes = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
spectrumSmoothed = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
peakValues = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
fftReal = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
fftImag = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
windowCoeffs = (float *)calloc(config.binCount, sizeof(float));
|
||||||
|
|
||||||
|
// Precompute window coefficients
|
||||||
|
ComputeWindowCoefficients(windowCoeffs, config.binCount, config.windowFunc);
|
||||||
|
|
||||||
|
analyzerReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free all resources used by the spectrum analyzer
|
||||||
|
void CloseSpectrumAnalyzer(void)
|
||||||
|
{
|
||||||
|
free(spectrumMagnitudes); spectrumMagnitudes = NULL;
|
||||||
|
free(spectrumSmoothed); spectrumSmoothed = NULL;
|
||||||
|
free(peakValues); peakValues = NULL;
|
||||||
|
free(fftReal); fftReal = NULL;
|
||||||
|
free(fftImag); fftImag = NULL;
|
||||||
|
free(windowCoeffs); windowCoeffs = NULL;
|
||||||
|
|
||||||
|
analyzerReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Spectrum processing
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Process audio samples: apply window, run FFT, compute magnitudes.
|
||||||
|
// Bug-fix: handles non-power-of-2 sampleCount by padding with zeros or truncating.
|
||||||
|
void UpdateSpectrum(float *samples, int sampleCount)
|
||||||
|
{
|
||||||
|
if (!analyzerReady || samples == NULL || sampleCount <= 0) return;
|
||||||
|
|
||||||
|
int n = analyzerConfig.binCount;
|
||||||
|
|
||||||
|
// Bounds checking: pad or truncate input to match FFT size (power of 2)
|
||||||
|
if (sampleCount >= n)
|
||||||
|
{
|
||||||
|
// Truncate: only use the first n samples
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
fftReal[i] = samples[i] * windowCoeffs[i];
|
||||||
|
fftImag[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pad: copy available samples, zero-pad the rest
|
||||||
|
for (int i = 0; i < sampleCount; i++)
|
||||||
|
{
|
||||||
|
fftReal[i] = samples[i] * windowCoeffs[i];
|
||||||
|
fftImag[i] = 0.0f;
|
||||||
|
}
|
||||||
|
for (int i = sampleCount; i < n; i++)
|
||||||
|
{
|
||||||
|
fftReal[i] = 0.0f;
|
||||||
|
fftImag[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform in-place FFT
|
||||||
|
BitReversalPermutation(fftReal, fftImag, n);
|
||||||
|
ComputeFFT(fftReal, fftImag, n);
|
||||||
|
|
||||||
|
// Compute magnitudes for each bin (only first half is unique due to symmetry)
|
||||||
|
int halfN = n / 2;
|
||||||
|
for (int i = 0; i < halfN; i++)
|
||||||
|
{
|
||||||
|
spectrumMagnitudes[i] = sqrtf(fftReal[i] * fftReal[i] + fftImag[i] * fftImag[i]) / (float)n;
|
||||||
|
}
|
||||||
|
// Mirror: zero out upper half (redundant for real input)
|
||||||
|
for (int i = halfN; i < n; i++)
|
||||||
|
{
|
||||||
|
spectrumMagnitudes[i] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update peak-hold values with decay
|
||||||
|
for (int i = 0; i < halfN; i++)
|
||||||
|
{
|
||||||
|
if (spectrumMagnitudes[i] > peakValues[i])
|
||||||
|
{
|
||||||
|
peakValues[i] = spectrumMagnitudes[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
peakValues[i] *= analyzerConfig.peakDecayRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply temporal smoothing between current and previous spectrum data
|
||||||
|
void ApplySmoothing(void)
|
||||||
|
{
|
||||||
|
if (!analyzerReady) return;
|
||||||
|
|
||||||
|
float alpha = analyzerConfig.smoothingFactor;
|
||||||
|
int halfN = analyzerConfig.binCount / 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < halfN; i++)
|
||||||
|
{
|
||||||
|
spectrumSmoothed[i] = alpha * spectrumSmoothed[i] + (1.0f - alpha) * spectrumMagnitudes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Data retrieval
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Get pointer to smoothed spectrum magnitude array
|
||||||
|
float *GetSpectrumData(void)
|
||||||
|
{
|
||||||
|
if (!analyzerReady) return NULL;
|
||||||
|
return spectrumSmoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the dominant frequency in Hz from the current spectrum
|
||||||
|
float GetPeakFrequency(void)
|
||||||
|
{
|
||||||
|
if (!analyzerReady) return 0.0f;
|
||||||
|
|
||||||
|
int halfN = analyzerConfig.binCount / 2;
|
||||||
|
int peakBin = 0;
|
||||||
|
float peakMag = 0.0f;
|
||||||
|
|
||||||
|
for (int i = 1; i < halfN; i++)
|
||||||
|
{
|
||||||
|
if (spectrumMagnitudes[i] > peakMag)
|
||||||
|
{
|
||||||
|
peakMag = spectrumMagnitudes[i];
|
||||||
|
peakBin = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert bin index to frequency: freq = bin * sampleRate / binCount
|
||||||
|
return (float)peakBin * (float)analyzerConfig.sampleRate / (float)analyzerConfig.binCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the magnitude of the largest frequency bin
|
||||||
|
float GetPeakMagnitude(void)
|
||||||
|
{
|
||||||
|
if (!analyzerReady) return 0.0f;
|
||||||
|
|
||||||
|
int halfN = analyzerConfig.binCount / 2;
|
||||||
|
float peakMag = 0.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < halfN; i++)
|
||||||
|
{
|
||||||
|
if (spectrumMagnitudes[i] > peakMag) peakMag = spectrumMagnitudes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return peakMag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the configured number of frequency bins
|
||||||
|
int GetSpectrumBinCount(void)
|
||||||
|
{
|
||||||
|
return analyzerConfig.binCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the analyzer has been initialized
|
||||||
|
bool IsSpectrumReady(void)
|
||||||
|
{
|
||||||
|
return analyzerReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Internal helpers
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Return the smallest power of 2 >= n
|
||||||
|
static int NextPowerOfTwo(int n)
|
||||||
|
{
|
||||||
|
int power = 1;
|
||||||
|
while (power < n) power <<= 1;
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-place bit-reversal permutation for radix-2 FFT
|
||||||
|
static void BitReversalPermutation(float *real, float *imag, int n)
|
||||||
|
{
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < n - 1; i++)
|
||||||
|
{
|
||||||
|
if (i < j)
|
||||||
|
{
|
||||||
|
// Swap real parts
|
||||||
|
float tmpR = real[i];
|
||||||
|
real[i] = real[j];
|
||||||
|
real[j] = tmpR;
|
||||||
|
// Swap imaginary parts
|
||||||
|
float tmpI = imag[i];
|
||||||
|
imag[i] = imag[j];
|
||||||
|
imag[j] = tmpI;
|
||||||
|
}
|
||||||
|
int k = n >> 1;
|
||||||
|
while (k <= j)
|
||||||
|
{
|
||||||
|
j -= k;
|
||||||
|
k >>= 1;
|
||||||
|
}
|
||||||
|
j += k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Radix-2 Cooley-Tukey decimation-in-time FFT (in-place)
|
||||||
|
static void ComputeFFT(float *real, float *imag, int n)
|
||||||
|
{
|
||||||
|
for (int step = 2; step <= n; step <<= 1)
|
||||||
|
{
|
||||||
|
int halfStep = step / 2;
|
||||||
|
float angle = -2.0f * (float)PI / (float)step;
|
||||||
|
|
||||||
|
for (int group = 0; group < n; group += step)
|
||||||
|
{
|
||||||
|
for (int pair = 0; pair < halfStep; pair++)
|
||||||
|
{
|
||||||
|
float twiddleReal = cosf(angle * (float)pair);
|
||||||
|
float twiddleImag = sinf(angle * (float)pair);
|
||||||
|
|
||||||
|
int even = group + pair;
|
||||||
|
int odd = group + pair + halfStep;
|
||||||
|
|
||||||
|
// Butterfly operation
|
||||||
|
float tR = twiddleReal * real[odd] - twiddleImag * imag[odd];
|
||||||
|
float tI = twiddleReal * imag[odd] + twiddleImag * real[odd];
|
||||||
|
|
||||||
|
real[odd] = real[even] - tR;
|
||||||
|
imag[odd] = imag[even] - tI;
|
||||||
|
real[even] += tR;
|
||||||
|
imag[even] += tI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precompute window function coefficients for a given size and type
|
||||||
|
static void ComputeWindowCoefficients(float *coeffs, int n, WindowFunction func)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
switch (func)
|
||||||
|
{
|
||||||
|
case WINDOW_HANN:
|
||||||
|
coeffs[i] = 0.5f * (1.0f - cosf(2.0f * (float)PI * (float)i / (float)(n - 1)));
|
||||||
|
break;
|
||||||
|
case WINDOW_HAMMING:
|
||||||
|
coeffs[i] = 0.54f - 0.46f * cosf(2.0f * (float)PI * (float)i / (float)(n - 1));
|
||||||
|
break;
|
||||||
|
case WINDOW_BLACKMAN:
|
||||||
|
coeffs[i] = 0.42f
|
||||||
|
- 0.5f * cosf(2.0f * (float)PI * (float)i / (float)(n - 1))
|
||||||
|
+ 0.08f * cosf(4.0f * (float)PI * (float)i / (float)(n - 1));
|
||||||
|
break;
|
||||||
|
case WINDOW_RECTANGULAR:
|
||||||
|
default:
|
||||||
|
coeffs[i] = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/raudio_analyzer.h
Normal file
95
src/raudio_analyzer.h
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**********************************************************************************************
|
||||||
|
*
|
||||||
|
* raudio_analyzer - Audio spectrum analysis module for raylib
|
||||||
|
*
|
||||||
|
* DESCRIPTION:
|
||||||
|
* FFT-based audio spectrum analysis with windowing functions, peak detection,
|
||||||
|
* and smoothing for real-time audio visualization.
|
||||||
|
*
|
||||||
|
* FEATURES:
|
||||||
|
* - Radix-2 Cooley-Tukey FFT algorithm
|
||||||
|
* - Multiple windowing functions (Hann, Hamming, Blackman, Rectangular)
|
||||||
|
* - Configurable frequency bin count
|
||||||
|
* - Peak detection with configurable decay rate
|
||||||
|
* - Temporal smoothing for stable visualization
|
||||||
|
*
|
||||||
|
* LICENSE: zlib/libpng
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 raylib contributors
|
||||||
|
*
|
||||||
|
* This software is provided "as-is", without any express or implied warranty. In no event
|
||||||
|
* will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose, including commercial
|
||||||
|
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not claim that you
|
||||||
|
* wrote the original software. If you use this software in a product, an acknowledgment
|
||||||
|
* in the product documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||||
|
* as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
**********************************************************************************************/
|
||||||
|
|
||||||
|
#ifndef RAUDIO_ANALYZER_H
|
||||||
|
#define RAUDIO_ANALYZER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Defines and Macros
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
#ifndef MAX_SPECTRUM_BINS
|
||||||
|
#define MAX_SPECTRUM_BINS 1024
|
||||||
|
#endif
|
||||||
|
#ifndef DEFAULT_SMOOTHING_FACTOR
|
||||||
|
#define DEFAULT_SMOOTHING_FACTOR 0.8f
|
||||||
|
#endif
|
||||||
|
#ifndef DEFAULT_PEAK_DECAY_RATE
|
||||||
|
#define DEFAULT_PEAK_DECAY_RATE 0.95f
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Types and Structures Definition
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Window function types for FFT preprocessing
|
||||||
|
typedef enum {
|
||||||
|
WINDOW_RECTANGULAR = 0, // No windowing (rectangular/boxcar)
|
||||||
|
WINDOW_HANN, // Hann (raised cosine) window
|
||||||
|
WINDOW_HAMMING, // Hamming window
|
||||||
|
WINDOW_BLACKMAN // Blackman window
|
||||||
|
} WindowFunction;
|
||||||
|
|
||||||
|
// Spectrum analyzer configuration
|
||||||
|
typedef struct {
|
||||||
|
int binCount; // Number of frequency bins (must be power of 2)
|
||||||
|
WindowFunction windowFunc; // Window function to apply before FFT
|
||||||
|
float smoothingFactor; // Temporal smoothing factor [0.0 - 1.0]
|
||||||
|
float peakDecayRate; // Peak value decay rate per frame [0.0 - 1.0]
|
||||||
|
int sampleRate; // Audio sample rate in Hz
|
||||||
|
} SpectrumConfig;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Functions Declaration
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Lifecycle management
|
||||||
|
void InitSpectrumAnalyzer(SpectrumConfig config); // Initialize spectrum analyzer with config
|
||||||
|
void CloseSpectrumAnalyzer(void); // Free all allocated resources
|
||||||
|
|
||||||
|
// Spectrum processing
|
||||||
|
void UpdateSpectrum(float *samples, int sampleCount); // Process audio samples through FFT
|
||||||
|
void ApplySmoothing(void); // Apply temporal smoothing to spectrum data
|
||||||
|
|
||||||
|
// Data retrieval
|
||||||
|
float *GetSpectrumData(void); // Get array of frequency bin magnitudes
|
||||||
|
float GetPeakFrequency(void); // Get the dominant frequency in Hz
|
||||||
|
float GetPeakMagnitude(void); // Get the magnitude of the peak frequency bin
|
||||||
|
int GetSpectrumBinCount(void); // Get the current number of frequency bins
|
||||||
|
bool IsSpectrumReady(void); // Check if analyzer is initialized and ready
|
||||||
|
|
||||||
|
#endif // RAUDIO_ANALYZER_H
|
||||||
Loading…
Reference in New Issue
Block a user