Files
CelloEffects/Appli/Core/Src/audio_looper.c
T
2026-10-18 14:06:40 +02:00

90 lines
2.7 KiB
C

#include "audio_looper.h"
/* 5s a 48kHz en float = 937.5 Ko : ne rentre pas en RAM interne, place en EXTRAM (meme
* section/region que le framebuffer TouchGFX, cf. AudioLooperBuffer dans le script de
* link). Accede uniquement par ce coeur (pas de DMA dessus) : pas de probleme de
* coherence de cache a gerer explicitement. */
__attribute__((section("AudioLooperBuffer")))
static float g_buffer[AUDIO_LOOPER_MAX_SAMPLES];
typedef enum
{
LOOPER_IDLE = 0,
LOOPER_RECORDING,
LOOPER_PLAYBACK
} LooperState;
/* g_state est ecrit depuis la tache IHM (Start/Stop) et lu/ecrit depuis le contexte
* audio temps reel (Process) : meme principe "un seul ecrivain a la fois par variable,
* volatile pour le flag d'etat partage" que le reste du projet (cf. audio_analysis.c). */
static volatile LooperState g_state = LOOPER_IDLE;
static uint32_t g_record_len = 0; /* nb d'echantillons valides captures, fige au relachement */
static uint32_t g_write_idx = 0; /* ecrit uniquement depuis Process() en RECORDING */
static float g_read_pos = 0.0f; /* lu/ecrit uniquement depuis Process() en PLAYBACK */
void AudioLooper_Init(void)
{
g_state = LOOPER_IDLE;
g_record_len = 0;
g_write_idx = 0;
g_read_pos = 0.0f;
}
void AudioLooper_StartRecording(void)
{
g_write_idx = 0;
g_state = LOOPER_RECORDING;
}
void AudioLooper_StopRecordingAndPlay(void)
{
if (g_state != LOOPER_RECORDING)
{
return;
}
g_record_len = g_write_idx;
g_read_pos = 0.0f;
g_state = (g_record_len > 0U) ? LOOPER_PLAYBACK : LOOPER_IDLE;
}
float AudioLooper_Process(float sample_in)
{
switch (g_state)
{
case LOOPER_RECORDING:
if (g_write_idx < AUDIO_LOOPER_MAX_SAMPLES)
{
g_buffer[g_write_idx++] = sample_in;
}
/* Buffer plein (5s atteintes) : on cesse de capturer mais on reste en
* enregistrement, l'ecoute live continue jusqu'au relachement du bouton. */
return sample_in;
case LOOPER_PLAYBACK:
{
uint32_t idx0 = (uint32_t)g_read_pos;
if (idx0 >= g_record_len - 1U)
{
g_state = LOOPER_IDLE;
return sample_in;
}
/* Interpolation lineaire entre les deux echantillons encadrant la position de
* lecture fractionnaire : necessaire car AUDIO_LOOPER_PLAYBACK_RATIO avance le
* pointeur de lecture plus vite qu'un pas entier. */
float frac = g_read_pos - (float)idx0;
float s0 = g_buffer[idx0];
float s1 = g_buffer[idx0 + 1U];
float out = s0 + (s1 - s0) * frac;
g_read_pos += AUDIO_LOOPER_PLAYBACK_RATIO;
return out;
}
case LOOPER_IDLE:
default:
return sample_in;
}
}