#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; } }