This commit is contained in:
@@ -18,6 +18,11 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TAG "audio_router"
|
#define TAG "audio_router"
|
||||||
|
|
||||||
@@ -39,9 +44,11 @@ static volatile tone_mode_t s_mode = TONE_NONE;
|
|||||||
static volatile bool s_tone_idle = true;
|
static volatile bool s_tone_idle = true;
|
||||||
static TaskHandle_t s_task = NULL;
|
static TaskHandle_t s_task = NULL;
|
||||||
|
|
||||||
/* --- Gain numerique SCO (avec saturation) --- */
|
/* --- Gain numerique SCO (reglable a la volee, avec saturation) --- */
|
||||||
#define SCO_PLAY_GAIN 4 /* downlink : PCM mobile -> ecouteur */
|
static volatile int s_play_gain = 4; /* downlink : PCM mobile -> ecouteur */
|
||||||
#define SCO_MIC_GAIN 5 /* uplink : micro -> mobile */
|
static volatile int s_mic_gain = 8; /* uplink : micro -> mobile (filtre) */
|
||||||
|
static volatile bool s_mic_filter = true; /* passe-bande voix sur le micro */
|
||||||
|
|
||||||
static inline int16_t sat16(int32_t v)
|
static inline int16_t sat16(int32_t v)
|
||||||
{
|
{
|
||||||
if (v > 32767) return 32767;
|
if (v > 32767) return 32767;
|
||||||
@@ -49,6 +56,47 @@ static inline int16_t sat16(int32_t v)
|
|||||||
return (int16_t)v;
|
return (int16_t)v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Biquad (RBJ) : passe-haut + passe-bas en serie sur le micro --- */
|
||||||
|
typedef struct { float b0, b1, b2, a1, a2; float z1, z2; } biquad_t;
|
||||||
|
static biquad_t s_hp, s_lp; /* HP 300 Hz, LP 3400 Hz @ 16 kHz */
|
||||||
|
|
||||||
|
static void biquad_set(biquad_t *f, float fc, float fs, float q, bool highpass)
|
||||||
|
{
|
||||||
|
float w0 = 2.0f * (float)M_PI * fc / fs;
|
||||||
|
float c = cosf(w0), s = sinf(w0);
|
||||||
|
float alpha = s / (2.0f * q);
|
||||||
|
float a0;
|
||||||
|
if (highpass) {
|
||||||
|
f->b0 = (1.0f + c) / 2.0f; f->b1 = -(1.0f + c); f->b2 = (1.0f + c) / 2.0f;
|
||||||
|
} else {
|
||||||
|
f->b0 = (1.0f - c) / 2.0f; f->b1 = (1.0f - c); f->b2 = (1.0f - c) / 2.0f;
|
||||||
|
}
|
||||||
|
a0 = 1.0f + alpha;
|
||||||
|
f->b0 /= a0; f->b1 /= a0; f->b2 /= a0;
|
||||||
|
f->a1 = (-2.0f * c) / a0; f->a2 = (1.0f - alpha) / a0;
|
||||||
|
f->z1 = f->z2 = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float biquad_run(biquad_t *f, float x) /* direct form II transposed */
|
||||||
|
{
|
||||||
|
float y = f->b0 * x + f->z1;
|
||||||
|
f->z1 = f->b1 * x - f->a1 * y + f->z2;
|
||||||
|
f->z2 = f->b2 * x - f->a2 * y;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mic_dsp_init(void)
|
||||||
|
{
|
||||||
|
biquad_set(&s_hp, 300.0f, 16000.0f, 0.707f, true);
|
||||||
|
biquad_set(&s_lp, 3400.0f, 16000.0f, 0.707f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_router_set_play_gain(int g) { if (g >= 0 && g <= 32) s_play_gain = g; }
|
||||||
|
void audio_router_set_mic_gain(int g) { if (g >= 0 && g <= 32) s_mic_gain = g; }
|
||||||
|
void audio_router_set_mic_filter(bool on) { s_mic_filter = on; }
|
||||||
|
int audio_router_get_play_gain(void) { return s_play_gain; }
|
||||||
|
int audio_router_get_mic_gain(void) { return s_mic_gain; }
|
||||||
|
|
||||||
/* --- Compteurs de diagnostic SCO (temporaires) --- */
|
/* --- Compteurs de diagnostic SCO (temporaires) --- */
|
||||||
static volatile uint32_t s_dbg_in_bytes = 0; /* PCM recu du BT (feed_playback) */
|
static volatile uint32_t s_dbg_in_bytes = 0; /* PCM recu du BT (feed_playback) */
|
||||||
static volatile uint32_t s_dbg_cap = 0; /* frames micro capturees */
|
static volatile uint32_t s_dbg_cap = 0; /* frames micro capturees */
|
||||||
@@ -96,7 +144,7 @@ static void tone_task(void *a)
|
|||||||
/* Frame complete : mono -> stereo (CVSD double pour 8k->16k) */
|
/* Frame complete : mono -> stereo (CVSD double pour 8k->16k) */
|
||||||
int frames = 0;
|
int frames = 0;
|
||||||
for (int i = 0; i < target && frames < FRAME; i++) {
|
for (int i = 0; i < target && frames < FRAME; i++) {
|
||||||
int16_t v = sat16((int32_t)sco_acc[i] * SCO_PLAY_GAIN);
|
int16_t v = sat16((int32_t)sco_acc[i] * s_play_gain);
|
||||||
buf[frames * 2] = v; buf[frames * 2 + 1] = v; frames++;
|
buf[frames * 2] = v; buf[frames * 2 + 1] = v; frames++;
|
||||||
if (!s_sco_msbc && frames < FRAME) {
|
if (!s_sco_msbc && frames < FRAME) {
|
||||||
buf[frames * 2] = v; buf[frames * 2 + 1] = v; frames++;
|
buf[frames * 2] = v; buf[frames * 2 + 1] = v; frames++;
|
||||||
@@ -204,7 +252,15 @@ static void sco_mic_task(void *a)
|
|||||||
int n = hal_i2s_capture_read_frame(mono, FRAME, &e);
|
int n = hal_i2s_capture_read_frame(mono, FRAME, &e);
|
||||||
if (n <= 0) { vTaskDelay(pdMS_TO_TICKS(5)); continue; }
|
if (n <= 0) { vTaskDelay(pdMS_TO_TICKS(5)); continue; }
|
||||||
s_dbg_cap++;
|
s_dbg_cap++;
|
||||||
for (int i = 0; i < n; i++) mono[i] = sat16((int32_t)mono[i] * SCO_MIC_GAIN);
|
/* Filtre passe-bande voix (HP 300 Hz + LP 3400 Hz) puis gain. */
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
float x = (float)mono[i];
|
||||||
|
if (s_mic_filter) {
|
||||||
|
x = biquad_run(&s_hp, x);
|
||||||
|
x = biquad_run(&s_lp, x);
|
||||||
|
}
|
||||||
|
mono[i] = sat16((int32_t)(x * (float)s_mic_gain));
|
||||||
|
}
|
||||||
if (s_sco_msbc) {
|
if (s_sco_msbc) {
|
||||||
/* 16 kHz direct : envoyer tel quel */
|
/* 16 kHz direct : envoyer tel quel */
|
||||||
xRingbufferSend(s_sco_cap_rb, mono, (size_t)(n * (int)sizeof(int16_t)), 0);
|
xRingbufferSend(s_sco_cap_rb, mono, (size_t)(n * (int)sizeof(int16_t)), 0);
|
||||||
@@ -223,6 +279,7 @@ static void sco_mic_task(void *a)
|
|||||||
void audio_router_sco_begin(bool msbc)
|
void audio_router_sco_begin(bool msbc)
|
||||||
{
|
{
|
||||||
s_sco_msbc = msbc;
|
s_sco_msbc = msbc;
|
||||||
|
mic_dsp_init(); /* (re)calcule les coeffs du filtre + reset l'etat */
|
||||||
if (!s_sco_play_rb) s_sco_play_rb = xRingbufferCreate(SCO_RB_SIZE, RINGBUF_TYPE_BYTEBUF);
|
if (!s_sco_play_rb) s_sco_play_rb = xRingbufferCreate(SCO_RB_SIZE, RINGBUF_TYPE_BYTEBUF);
|
||||||
if (!s_sco_cap_rb) s_sco_cap_rb = xRingbufferCreate(SCO_RB_SIZE, RINGBUF_TYPE_BYTEBUF);
|
if (!s_sco_cap_rb) s_sco_cap_rb = xRingbufferCreate(SCO_RB_SIZE, RINGBUF_TYPE_BYTEBUF);
|
||||||
hal_i2s_capture_begin();
|
hal_i2s_capture_begin();
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ extern "C" {
|
|||||||
/* Create the tone task (idempotent). Call once after hal_i2s_init(). */
|
/* Create the tone task (idempotent). Call once after hal_i2s_init(). */
|
||||||
void audio_router_init(void);
|
void audio_router_init(void);
|
||||||
|
|
||||||
|
/* Gains SCO reglables a la volee (0..32) + filtre passe-bande voix sur le micro. */
|
||||||
|
void audio_router_set_play_gain(int g); /* downlink : ecouteur */
|
||||||
|
void audio_router_set_mic_gain(int g); /* uplink : micro -> mobile */
|
||||||
|
void audio_router_set_mic_filter(bool on);
|
||||||
|
int audio_router_get_play_gain(void);
|
||||||
|
int audio_router_get_mic_gain(void);
|
||||||
|
|
||||||
/* Start a continuous 440 Hz dial tone (enables PA). */
|
/* Start a continuous 440 Hz dial tone (enables PA). */
|
||||||
void audio_router_dialtone_start(void);
|
void audio_router_dialtone_start(void);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "cli.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES audio_router
|
||||||
|
)
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* cli.c — petit CLI serie (console UART0) pour regler les gains audio et le
|
||||||
|
* filtre micro a la volee, sans recompiler. Coexiste avec les logs ESP_LOG.
|
||||||
|
*/
|
||||||
|
#include "cli.h"
|
||||||
|
#include "audio_router.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAG "cli"
|
||||||
|
|
||||||
|
static void handle(char *l)
|
||||||
|
{
|
||||||
|
if (strncmp(l, "gmic", 4) == 0) {
|
||||||
|
audio_router_set_mic_gain(atoi(l + 4));
|
||||||
|
ESP_LOGI(TAG, "gain micro = %d", audio_router_get_mic_gain());
|
||||||
|
} else if (strncmp(l, "gspk", 4) == 0) {
|
||||||
|
audio_router_set_play_gain(atoi(l + 4));
|
||||||
|
ESP_LOGI(TAG, "gain ecouteur = %d", audio_router_get_play_gain());
|
||||||
|
} else if (strncmp(l, "filt", 4) == 0) {
|
||||||
|
int on = atoi(l + 4);
|
||||||
|
audio_router_set_mic_filter(on != 0);
|
||||||
|
ESP_LOGI(TAG, "filtre micro = %s", on ? "ON" : "OFF");
|
||||||
|
} else if (strncmp(l, "status", 6) == 0) {
|
||||||
|
ESP_LOGI(TAG, "gspk=%d gmic=%d",
|
||||||
|
audio_router_get_play_gain(), audio_router_get_mic_gain());
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "cmds: gmic N | gspk N | filt 0/1 | status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cli_task(void *a)
|
||||||
|
{
|
||||||
|
(void)a;
|
||||||
|
char line[48];
|
||||||
|
int pos = 0;
|
||||||
|
ESP_LOGI(TAG, "CLI prete — gmic N / gspk N / filt 0/1 / status");
|
||||||
|
for (;;) {
|
||||||
|
int c = getchar();
|
||||||
|
if (c == EOF) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
if (pos > 0) { line[pos] = '\0'; handle(line); pos = 0; }
|
||||||
|
} else if (pos < (int)sizeof(line) - 1) {
|
||||||
|
line[pos++] = (char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_start(void)
|
||||||
|
{
|
||||||
|
xTaskCreate(cli_task, "cli", 3072, NULL, 3, NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Demarre un CLI serie (console UART) pour regler les gains/filtre a la volee :
|
||||||
|
* gmic <0..32> gain micro (uplink)
|
||||||
|
* gspk <0..32> gain ecouteur (downlink)
|
||||||
|
* filt <0|1> filtre passe-bande voix sur le micro
|
||||||
|
* status affiche les valeurs courantes
|
||||||
|
*/
|
||||||
|
void cli_start(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "app_main.c"
|
SRCS "app_main.c"
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES nvs_flash config_store hal_es8388 hal_i2s audio_router slic_ks0835 dtmf dialer hook_monitor call_manager bt_hfp
|
REQUIRES nvs_flash config_store hal_es8388 hal_i2s audio_router slic_ks0835 dtmf dialer hook_monitor call_manager bt_hfp cli
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "dialer.h"
|
#include "dialer.h"
|
||||||
#include "call_manager.h"
|
#include "call_manager.h"
|
||||||
#include "bt_hfp.h"
|
#include "bt_hfp.h"
|
||||||
|
#include "cli.h"
|
||||||
|
|
||||||
static const char *TAG = "rtc_phone";
|
static const char *TAG = "rtc_phone";
|
||||||
|
|
||||||
@@ -28,5 +29,6 @@ void app_main(void)
|
|||||||
/* Pile BT HFP-HF — NVS déjà initialisé par config_store_init().
|
/* Pile BT HFP-HF — NVS déjà initialisé par config_store_init().
|
||||||
* call_manager_bt_event remplace le handler de log temporaire (Task 2). */
|
* call_manager_bt_event remplace le handler de log temporaire (Task 2). */
|
||||||
ESP_ERROR_CHECK(bt_hfp_init(call_manager_bt_event));
|
ESP_ERROR_CHECK(bt_hfp_init(call_manager_bt_event));
|
||||||
|
cli_start();
|
||||||
ESP_LOGI(TAG, "call_manager + bt_hfp prets — appairer sur 'RTC_BL_PHONE'");
|
ESP_LOGI(TAG, "call_manager + bt_hfp prets — appairer sur 'RTC_BL_PHONE'");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user