init
This commit is contained in:
commit
2c2202e60d
2491 files changed
+1295990
No files matched your search
@@ -0,0 +1,155 @@
|
||||
#include "audio_analysis.h"
|
||||
#include "arm_math.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FFT_SIZE AUDIO_ANALYSIS_FFT_SIZE
|
||||
#define NUM_BINS AUDIO_ANALYSIS_NUM_BINS
|
||||
|
||||
/* --- Capture en double buffer (ping-pong), ecrite depuis le chemin audio temps
|
||||
* reel, lue depuis la tache IHM. Un seul ecrivain, un seul lecteur : pas de verrou
|
||||
* necessaire, meme principe que la FIFO audio principale. --- */
|
||||
static float g_capture_buf[2][FFT_SIZE];
|
||||
static uint32_t g_capture_fill_idx = 0;
|
||||
static uint8_t g_capture_write_buf = 0;
|
||||
static volatile uint8_t g_capture_ready_buf = 0xFFU; /* 0xFF = aucun buffer complet en attente */
|
||||
|
||||
static bool g_analysis_enabled = false;
|
||||
|
||||
/* --- Fenetre precalculee (a l'init uniquement, jamais recalculee en boucle) --- */
|
||||
static float g_hann_window[FFT_SIZE];
|
||||
|
||||
/* --- FFT reelle CMSIS-DSP : instance initialisee une fois, buffers de travail --- */
|
||||
static arm_rfft_fast_instance_f32 g_rfft_instance;
|
||||
static float g_fft_work[FFT_SIZE]; /* echantillons fenetres ; ecrase (scratch) par arm_rfft_fast_f32 */
|
||||
static float g_fft_out[FFT_SIZE]; /* spectre complexe empaquete, format CMSIS (voir plus bas) */
|
||||
|
||||
static float g_magnitudes_db[NUM_BINS]; /* seule la moitie 0..Fe/2 est conservee */
|
||||
static float g_peak_freq_hz = 0.0f;
|
||||
|
||||
static void AudioAnalysis_BuildTables(void)
|
||||
{
|
||||
for (uint32_t i = 0; i < FFT_SIZE; i++)
|
||||
{
|
||||
/* Fenetre de Hann : reduit les fuites spectrales dues a la troncature du bloc. */
|
||||
g_hann_window[i] = 0.5f - 0.5f * cosf(2.0f * (float)M_PI * (float)i / (float)(FFT_SIZE - 1U));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioAnalysis_Init(void)
|
||||
{
|
||||
memset(g_capture_buf, 0, sizeof(g_capture_buf));
|
||||
g_capture_fill_idx = 0;
|
||||
g_capture_write_buf = 0;
|
||||
g_capture_ready_buf = 0xFFU;
|
||||
g_analysis_enabled = false;
|
||||
|
||||
memset(g_magnitudes_db, 0, sizeof(g_magnitudes_db));
|
||||
|
||||
AudioAnalysis_BuildTables();
|
||||
arm_rfft_fast_init_f32(&g_rfft_instance, FFT_SIZE);
|
||||
}
|
||||
|
||||
void AudioAnalysis_PushSample(float sample)
|
||||
{
|
||||
if (!g_analysis_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_capture_buf[g_capture_write_buf][g_capture_fill_idx] = sample;
|
||||
g_capture_fill_idx++;
|
||||
|
||||
if (g_capture_fill_idx >= FFT_SIZE)
|
||||
{
|
||||
g_capture_fill_idx = 0;
|
||||
g_capture_ready_buf = g_capture_write_buf;
|
||||
g_capture_write_buf = 1U - g_capture_write_buf;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioAnalysis_Update(void)
|
||||
{
|
||||
if (!g_analysis_enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t ready = g_capture_ready_buf;
|
||||
if (ready == 0xFFU)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
g_capture_ready_buf = 0xFFU;
|
||||
|
||||
for (uint32_t i = 0; i < FFT_SIZE; i++)
|
||||
{
|
||||
g_fft_work[i] = g_capture_buf[ready][i] * g_hann_window[i];
|
||||
}
|
||||
|
||||
/* arm_rfft_fast_f32 (forward, ifftFlag=0) empaquete le spectre d'un signal reel
|
||||
* en NUM_BINS paires {re,im}, sauf la premiere qui contient les deux seules
|
||||
* valeurs reelles (DC et Nyquist) : g_fft_out[0]=X[0] (DC), g_fft_out[1]=X[N/2]
|
||||
* (Nyquist, non utilise ici). Pour k=1..NUM_BINS-1 : re=g_fft_out[2k], im=g_fft_out[2k+1]. */
|
||||
arm_rfft_fast_f32(&g_rfft_instance, g_fft_work, g_fft_out, 0);
|
||||
|
||||
/* Magnitude normalisee (une sinusoide plein-echelle donne ~0dB) puis convertie
|
||||
* en dB, avec un plancher pour eviter log(0). Destine a un affichage visuel,
|
||||
* pas a une mesure calibree. Seule la moitie 0..Fe/2 du spectre est conservee :
|
||||
* le spectre d'un signal reel est symetrique, la seconde moitie n'apporte rien. */
|
||||
/* Recherche du pic en meme temps que le calcul des magnitudes. Le bin 0 (continu,
|
||||
* 0Hz) est ignore : une composante DC ne doit pas etre rapportee comme "frequence
|
||||
* dominante". */
|
||||
uint32_t peak_bin = 1U;
|
||||
float peak_mag = 0.0f;
|
||||
|
||||
for (uint32_t i = 0; i < NUM_BINS; i++)
|
||||
{
|
||||
float re = (i == 0U) ? g_fft_out[0] : g_fft_out[2U * i];
|
||||
float im = (i == 0U) ? 0.0f : g_fft_out[2U * i + 1U];
|
||||
float mag = sqrtf(re * re + im * im) / ((float)FFT_SIZE / 4.0f);
|
||||
|
||||
if (i >= 1U && mag > peak_mag)
|
||||
{
|
||||
peak_mag = mag;
|
||||
peak_bin = i;
|
||||
}
|
||||
|
||||
if (mag < 1.0e-6f)
|
||||
{
|
||||
mag = 1.0e-6f;
|
||||
}
|
||||
g_magnitudes_db[i] = 20.0f * log10f(mag);
|
||||
}
|
||||
|
||||
g_peak_freq_hz = (float)peak_bin * (float)AUDIO_ANALYSIS_SAMPLE_RATE_HZ / (float)FFT_SIZE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const float *AudioAnalysis_GetMagnitudesDb(void)
|
||||
{
|
||||
return g_magnitudes_db;
|
||||
}
|
||||
|
||||
float AudioAnalysis_GetPeakFrequencyHz(void)
|
||||
{
|
||||
return g_peak_freq_hz;
|
||||
}
|
||||
|
||||
void AudioAnalysis_SetEnabled(bool enabled)
|
||||
{
|
||||
if (enabled && !g_analysis_enabled)
|
||||
{
|
||||
/* Redemarre une capture propre a chaque activation. */
|
||||
g_capture_fill_idx = 0;
|
||||
g_capture_write_buf = 0;
|
||||
g_capture_ready_buf = 0xFFU;
|
||||
}
|
||||
g_analysis_enabled = enabled;
|
||||
}
|
||||
|
||||
bool AudioAnalysis_IsEnabled(void)
|
||||
{
|
||||
return g_analysis_enabled;
|
||||
}
|
||||
Reference in new issue
Block a user