ajout effets audio
This commit is contained in:
1 parent
f8a79b08bf
commit
44a43828f1
14 files changed
+541
-6
No files matched your search
@@ -0,0 +1,34 @@
|
||||
#include "audio_effects.h"
|
||||
|
||||
static AudioEffectsParams g_params;
|
||||
|
||||
static FxEqualizerState g_eqState;
|
||||
static FxOverdriveState g_overdriveState;
|
||||
static FxSaturationState g_saturationState;
|
||||
static FxEchoState g_echoState;
|
||||
static FxReverbState g_reverbState;
|
||||
|
||||
void AudioEffects_Init(void)
|
||||
{
|
||||
FxEqualizer_Init(&g_eqState, &g_params.equalizer);
|
||||
FxOverdrive_Init(&g_overdriveState, &g_params.overdrive);
|
||||
FxSaturation_Init(&g_saturationState, &g_params.saturation);
|
||||
FxEcho_Init(&g_echoState, &g_params.echo);
|
||||
FxReverb_Init(&g_reverbState, &g_params.reverb);
|
||||
}
|
||||
|
||||
float AudioEffects_Process(float input)
|
||||
{
|
||||
float sample = input;
|
||||
sample = FxEqualizer_Process(&g_eqState, &g_params.equalizer, sample);
|
||||
sample = FxOverdrive_Process(&g_overdriveState, &g_params.overdrive, sample);
|
||||
sample = FxSaturation_Process(&g_saturationState, &g_params.saturation, sample);
|
||||
sample = FxEcho_Process(&g_echoState, &g_params.echo, sample);
|
||||
sample = FxReverb_Process(&g_reverbState, &g_params.reverb, sample);
|
||||
return sample;
|
||||
}
|
||||
|
||||
AudioEffectsParams *AudioEffects_GetParams(void)
|
||||
{
|
||||
return &g_params;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "fx_echo.h"
|
||||
#include <string.h>
|
||||
|
||||
void FxEcho_Init(FxEchoState *state, FxEchoParams *params)
|
||||
{
|
||||
memset(state->buffer, 0, sizeof(state->buffer));
|
||||
state->write_idx = 0;
|
||||
|
||||
params->enabled = false;
|
||||
params->delay_ms = 300.0f;
|
||||
params->feedback = 0.35f;
|
||||
params->mix = 0.3f;
|
||||
}
|
||||
|
||||
float FxEcho_Process(FxEchoState *state, const FxEchoParams *params, float input)
|
||||
{
|
||||
if (!params->enabled)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
uint32_t delay_samples = (uint32_t)(params->delay_ms * (FX_ECHO_SAMPLE_RATE / 1000.0f));
|
||||
if (delay_samples >= FX_ECHO_BUFFER_SIZE)
|
||||
{
|
||||
delay_samples = FX_ECHO_BUFFER_SIZE - 1U;
|
||||
}
|
||||
|
||||
uint32_t read_idx = (state->write_idx + FX_ECHO_BUFFER_SIZE - delay_samples) % FX_ECHO_BUFFER_SIZE;
|
||||
float delayed = (float)state->buffer[read_idx] / 32768.0f;
|
||||
|
||||
float to_store = input + delayed * params->feedback;
|
||||
if (to_store > 1.0f) to_store = 1.0f;
|
||||
if (to_store < -1.0f) to_store = -1.0f;
|
||||
state->buffer[state->write_idx] = (int16_t)(to_store * 32767.0f);
|
||||
state->write_idx = (state->write_idx + 1U) % FX_ECHO_BUFFER_SIZE;
|
||||
|
||||
return input * (1.0f - params->mix) + delayed * params->mix;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "fx_equalizer.h"
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define FX_EQ_SAMPLE_RATE 48000.0f
|
||||
|
||||
static const float fx_eq_default_freqs[FX_EQ_NUM_BANDS] =
|
||||
{
|
||||
31.0f, 62.0f, 125.0f, 250.0f, 500.0f, 1000.0f, 2000.0f, 4000.0f, 8000.0f, 16000.0f
|
||||
};
|
||||
|
||||
void FxEqualizer_Init(FxEqualizerState *state, FxEqualizerParams *params)
|
||||
{
|
||||
params->enabled = false;
|
||||
for (uint32_t i = 0; i < FX_EQ_NUM_BANDS; i++)
|
||||
{
|
||||
params->bands[i].freq_hz = fx_eq_default_freqs[i];
|
||||
params->bands[i].gain_db = 0.0f;
|
||||
params->bands[i].q = 1.0f;
|
||||
|
||||
state->band_state[i].last_freq_hz = -1.0f; /* force le calcul des coefficients au 1er sample */
|
||||
state->band_state[i].last_gain_db = 0.0f;
|
||||
state->band_state[i].last_q = 0.0f;
|
||||
state->x1[i] = state->x2[i] = state->y1[i] = state->y2[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Filtre "peaking EQ" (cloche), formule de l'Audio EQ Cookbook (Robert Bristow-Johnson). */
|
||||
static void FxEq_ComputeBandCoeffs(FxEqBandState *bs, const FxEqBand *band)
|
||||
{
|
||||
float A = powf(10.0f, band->gain_db / 40.0f);
|
||||
float w0 = 2.0f * (float)M_PI * band->freq_hz / FX_EQ_SAMPLE_RATE;
|
||||
float alpha = sinf(w0) / (2.0f * band->q);
|
||||
float cosw0 = cosf(w0);
|
||||
|
||||
float a0 = 1.0f + alpha / A;
|
||||
bs->b0 = (1.0f + alpha * A) / a0;
|
||||
bs->b1 = (-2.0f * cosw0) / a0;
|
||||
bs->b2 = (1.0f - alpha * A) / a0;
|
||||
bs->a1 = (-2.0f * cosw0) / a0;
|
||||
bs->a2 = (1.0f - alpha / A) / a0;
|
||||
|
||||
bs->last_freq_hz = band->freq_hz;
|
||||
bs->last_gain_db = band->gain_db;
|
||||
bs->last_q = band->q;
|
||||
}
|
||||
|
||||
float FxEqualizer_Process(FxEqualizerState *state, const FxEqualizerParams *params, float input)
|
||||
{
|
||||
if (!params->enabled)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
float y = input;
|
||||
for (uint32_t i = 0; i < FX_EQ_NUM_BANDS; i++)
|
||||
{
|
||||
const FxEqBand *band = ¶ms->bands[i];
|
||||
FxEqBandState *bs = &state->band_state[i];
|
||||
|
||||
if (band->freq_hz != bs->last_freq_hz || band->gain_db != bs->last_gain_db || band->q != bs->last_q)
|
||||
{
|
||||
FxEq_ComputeBandCoeffs(bs, band);
|
||||
}
|
||||
|
||||
float x = y;
|
||||
y = bs->b0 * x + bs->b1 * state->x1[i] + bs->b2 * state->x2[i]
|
||||
- bs->a1 * state->y1[i] - bs->a2 * state->y2[i];
|
||||
|
||||
state->x2[i] = state->x1[i];
|
||||
state->x1[i] = x;
|
||||
state->y2[i] = state->y1[i];
|
||||
state->y1[i] = y;
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "fx_overdrive.h"
|
||||
#include <math.h>
|
||||
|
||||
void FxOverdrive_Init(FxOverdriveState *state, FxOverdriveParams *params)
|
||||
{
|
||||
state->tone_lp_state = 0.0f;
|
||||
|
||||
params->enabled = false;
|
||||
params->drive = 0.4f;
|
||||
params->tone = 0.6f;
|
||||
params->level = 0.7f;
|
||||
}
|
||||
|
||||
float FxOverdrive_Process(FxOverdriveState *state, const FxOverdriveParams *params, float input)
|
||||
{
|
||||
if (!params->enabled)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
float driven = input * (1.0f + params->drive * 20.0f);
|
||||
|
||||
/* Ecretage asymetrique (exponentiel) : plus caracteristique/agressif qu'un tanh symetrique. */
|
||||
float shaped;
|
||||
if (driven >= 0.0f)
|
||||
{
|
||||
shaped = 1.0f - expf(-driven);
|
||||
}
|
||||
else
|
||||
{
|
||||
shaped = -1.0f + expf(driven);
|
||||
}
|
||||
|
||||
/* Filtre passe-bas une pole pour la tonalite : coefficient = tone (0..1). */
|
||||
float coeff = 0.05f + params->tone * 0.9f;
|
||||
state->tone_lp_state += coeff * (shaped - state->tone_lp_state);
|
||||
|
||||
return state->tone_lp_state * params->level;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "fx_reverb.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Lignes a retard inspirees de Freeverb (Jezar), redimensionnees de 44.1kHz vers 48kHz. */
|
||||
#define FX_REVERB_COMB0 1215U
|
||||
#define FX_REVERB_COMB1 1293U
|
||||
#define FX_REVERB_COMB2 1390U
|
||||
#define FX_REVERB_COMB3 1476U
|
||||
#define FX_REVERB_COMB4 1548U
|
||||
#define FX_REVERB_COMB5 1623U
|
||||
#define FX_REVERB_COMB6 1695U
|
||||
#define FX_REVERB_COMB7 1761U
|
||||
|
||||
#define FX_REVERB_ALLPASS0 605U
|
||||
#define FX_REVERB_ALLPASS1 480U
|
||||
#define FX_REVERB_ALLPASS2 371U
|
||||
#define FX_REVERB_ALLPASS3 245U
|
||||
|
||||
static float fx_reverb_comb_buf0[FX_REVERB_COMB0];
|
||||
static float fx_reverb_comb_buf1[FX_REVERB_COMB1];
|
||||
static float fx_reverb_comb_buf2[FX_REVERB_COMB2];
|
||||
static float fx_reverb_comb_buf3[FX_REVERB_COMB3];
|
||||
static float fx_reverb_comb_buf4[FX_REVERB_COMB4];
|
||||
static float fx_reverb_comb_buf5[FX_REVERB_COMB5];
|
||||
static float fx_reverb_comb_buf6[FX_REVERB_COMB6];
|
||||
static float fx_reverb_comb_buf7[FX_REVERB_COMB7];
|
||||
|
||||
static float fx_reverb_allpass_buf0[FX_REVERB_ALLPASS0];
|
||||
static float fx_reverb_allpass_buf1[FX_REVERB_ALLPASS1];
|
||||
static float fx_reverb_allpass_buf2[FX_REVERB_ALLPASS2];
|
||||
static float fx_reverb_allpass_buf3[FX_REVERB_ALLPASS3];
|
||||
|
||||
static float FxComb_Process(FxCombState *c, float input, float feedback, float damp1, float damp2)
|
||||
{
|
||||
float output = c->buffer[c->idx];
|
||||
c->filter_store = (output * damp2) + (c->filter_store * damp1);
|
||||
c->buffer[c->idx] = input + (c->filter_store * feedback);
|
||||
c->idx = (c->idx + 1U) % c->size;
|
||||
return output;
|
||||
}
|
||||
|
||||
static float FxAllpass_Process(FxAllpassState *a, float input, float feedback)
|
||||
{
|
||||
float buf_out = a->buffer[a->idx];
|
||||
float output = -input + buf_out;
|
||||
a->buffer[a->idx] = input + buf_out * feedback;
|
||||
a->idx = (a->idx + 1U) % a->size;
|
||||
return output;
|
||||
}
|
||||
|
||||
void FxReverb_Init(FxReverbState *state, FxReverbParams *params)
|
||||
{
|
||||
params->enabled = false;
|
||||
params->room_size = 0.5f;
|
||||
params->damping = 0.5f;
|
||||
params->mix = 0.3f;
|
||||
|
||||
float *comb_buffers[FX_REVERB_NUM_COMBS] = {
|
||||
fx_reverb_comb_buf0, fx_reverb_comb_buf1, fx_reverb_comb_buf2, fx_reverb_comb_buf3,
|
||||
fx_reverb_comb_buf4, fx_reverb_comb_buf5, fx_reverb_comb_buf6, fx_reverb_comb_buf7
|
||||
};
|
||||
uint32_t comb_sizes[FX_REVERB_NUM_COMBS] = {
|
||||
FX_REVERB_COMB0, FX_REVERB_COMB1, FX_REVERB_COMB2, FX_REVERB_COMB3,
|
||||
FX_REVERB_COMB4, FX_REVERB_COMB5, FX_REVERB_COMB6, FX_REVERB_COMB7
|
||||
};
|
||||
for (uint32_t i = 0; i < FX_REVERB_NUM_COMBS; i++)
|
||||
{
|
||||
state->combs[i].buffer = comb_buffers[i];
|
||||
state->combs[i].size = comb_sizes[i];
|
||||
state->combs[i].idx = 0;
|
||||
state->combs[i].filter_store = 0.0f;
|
||||
memset(comb_buffers[i], 0, comb_sizes[i] * sizeof(float));
|
||||
}
|
||||
|
||||
float *allpass_buffers[FX_REVERB_NUM_ALLPASS] = {
|
||||
fx_reverb_allpass_buf0, fx_reverb_allpass_buf1, fx_reverb_allpass_buf2, fx_reverb_allpass_buf3
|
||||
};
|
||||
uint32_t allpass_sizes[FX_REVERB_NUM_ALLPASS] = {
|
||||
FX_REVERB_ALLPASS0, FX_REVERB_ALLPASS1, FX_REVERB_ALLPASS2, FX_REVERB_ALLPASS3
|
||||
};
|
||||
for (uint32_t i = 0; i < FX_REVERB_NUM_ALLPASS; i++)
|
||||
{
|
||||
state->allpass[i].buffer = allpass_buffers[i];
|
||||
state->allpass[i].size = allpass_sizes[i];
|
||||
state->allpass[i].idx = 0;
|
||||
memset(allpass_buffers[i], 0, allpass_sizes[i] * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
float FxReverb_Process(FxReverbState *state, const FxReverbParams *params, float input)
|
||||
{
|
||||
if (!params->enabled)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
float feedback = 0.28f + params->room_size * 0.7f;
|
||||
float damp1 = params->damping * 0.4f;
|
||||
float damp2 = 1.0f - damp1;
|
||||
|
||||
float out = 0.0f;
|
||||
for (uint32_t i = 0; i < FX_REVERB_NUM_COMBS; i++)
|
||||
{
|
||||
out += FxComb_Process(&state->combs[i], input, feedback, damp1, damp2);
|
||||
}
|
||||
out /= (float)FX_REVERB_NUM_COMBS;
|
||||
|
||||
for (uint32_t i = 0; i < FX_REVERB_NUM_ALLPASS; i++)
|
||||
{
|
||||
out = FxAllpass_Process(&state->allpass[i], out, 0.5f);
|
||||
}
|
||||
|
||||
return input * (1.0f - params->mix) + out * params->mix;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "fx_saturation.h"
|
||||
#include <math.h>
|
||||
|
||||
#define FX_SATURATION_DC_BLOCK_R 0.995f
|
||||
|
||||
void FxSaturation_Init(FxSaturationState *state, FxSaturationParams *params)
|
||||
{
|
||||
state->dc_block_x1 = 0.0f;
|
||||
state->dc_block_y1 = 0.0f;
|
||||
|
||||
params->enabled = false;
|
||||
params->drive = 0.3f;
|
||||
params->level = 0.8f;
|
||||
}
|
||||
|
||||
float FxSaturation_Process(FxSaturationState *state, const FxSaturationParams *params, float input)
|
||||
{
|
||||
if (!params->enabled)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
float driven = input * (1.0f + params->drive * 12.0f);
|
||||
float shaped = tanhf(driven);
|
||||
|
||||
/* Le tanh sur un signal non symetrique introduit un leger DC : filtre bloqueur de DC. */
|
||||
float y = shaped - state->dc_block_x1 + FX_SATURATION_DC_BLOCK_R * state->dc_block_y1;
|
||||
state->dc_block_x1 = shaped;
|
||||
state->dc_block_y1 = y;
|
||||
|
||||
return y * params->level;
|
||||
}
|
||||
+16
-5
@@ -25,6 +25,7 @@
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
#include "stm32h7s78_discovery_audio.h"
|
||||
#include "audio_effects.h"
|
||||
#include <string.h>
|
||||
/* USER CODE END Includes */
|
||||
|
||||
@@ -186,6 +187,8 @@ int main(void)
|
||||
fifo_write_idx = FIFO_SIZE / 2;
|
||||
fifo_read_idx = 0;
|
||||
|
||||
AudioEffects_Init();
|
||||
|
||||
sDmaConfig.Address = (uint32_t)adf_buf;
|
||||
sDmaConfig.DataLength = AUDIO_SAMPLES * sizeof(int32_t); /* OCTETS */
|
||||
sDmaConfig.MsbOnly = DISABLE; /* 32-bit/sample ; ENABLE si buffer int16 */
|
||||
@@ -768,11 +771,17 @@ static void MX_GPIO_Init(void)
|
||||
|
||||
/* USER CODE BEGIN 4 */
|
||||
|
||||
static inline int16_t adf_sample_to_pcm16(int32_t raw)
|
||||
static inline float adf_sample_to_float(int32_t raw)
|
||||
{
|
||||
int32_t v = raw >> 8; // 24 bits -> 16 bits (comme le fait le BSP)
|
||||
if (v > 32767) v = 32767;
|
||||
if (v < -32768) v = -32768;
|
||||
int32_t v = raw >> 8; // 24 bits -> valeur signee sur 24 bits
|
||||
return (float)v / 8388608.0f; // normalisation en [-1.0, 1.0]
|
||||
}
|
||||
|
||||
static inline int16_t float_to_pcm16(float x)
|
||||
{
|
||||
float v = x * 32767.0f;
|
||||
if (v > 32767.0f) v = 32767.0f;
|
||||
if (v < -32768.0f) v = -32768.0f;
|
||||
return (int16_t)v;
|
||||
}
|
||||
|
||||
@@ -782,7 +791,9 @@ static void push_to_fifo(int32_t raw)
|
||||
{
|
||||
return; // FIFO pleine : on jette l'échantillon plutôt que d'écraser du non-lu
|
||||
}
|
||||
int16_t s = adf_sample_to_pcm16(raw);
|
||||
float sample = adf_sample_to_float(raw);
|
||||
sample = AudioEffects_Process(sample);
|
||||
int16_t s = float_to_pcm16(sample);
|
||||
uint32_t idx = fifo_write_idx & (FIFO_SIZE - 1);
|
||||
audio_fifo[idx * 2] = s;
|
||||
audio_fifo[idx * 2 + 1] = s;
|
||||
|
||||
Reference in new issue
Block a user