90 lines
2.7 KiB
C
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;
|
|
}
|
|
}
|