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