feat(bt): audio_router mode SCO (pont PCM I2S)
- Ajoute TONE_SCO a l enum tone_mode_t - Ring buffers FreeRTOS (play + cap, 4 KB chacun) - sco_mic_task : capture I2S -> ring sortant (decimation CVSD 8k) - tone_task : lit ring entrant, mono->stereo, underrun = silence - 4 fonctions pub : sco_begin/end/feed_playback/take_capture - CMakeLists.txt : + esp_ringbuf dans REQUIRES - Header : + stdbool.h / stdint.h (types bool/uint8_t/uint32_t)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "audio_router.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_driver_i2s bsp tone_gen hal_i2s
|
||||
REQUIRES esp_driver_i2s bsp tone_gen hal_i2s esp_ringbuf
|
||||
)
|
||||
|
||||
@@ -11,15 +11,24 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TAG "audio_router"
|
||||
|
||||
typedef enum { TONE_NONE, TONE_DIAL, TONE_BUSY, TONE_RINGBACK } tone_mode_t;
|
||||
typedef enum { TONE_NONE, TONE_DIAL, TONE_BUSY, TONE_RINGBACK, TONE_SCO } tone_mode_t;
|
||||
|
||||
#define SCO_RB_SIZE 4096 /* ~128 ms @ 16 kHz mono 16-bit */
|
||||
static RingbufHandle_t s_sco_play_rb = NULL; /* PCM entrant -> écouteur (TX) */
|
||||
static RingbufHandle_t s_sco_cap_rb = NULL; /* micro -> PCM sortant */
|
||||
static volatile bool s_sco_active = false;
|
||||
static volatile bool s_sco_msbc = true;
|
||||
static TaskHandle_t s_sco_mic_task = NULL;
|
||||
|
||||
#define SR PLIP_SAMPLE_RATE /* 16000 — doit correspondre à hal_i2s */
|
||||
#define FRAME 320 /* 20 ms @ 16 kHz */
|
||||
@@ -47,6 +56,43 @@ static void tone_task(void *a)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* --- Mode SCO : pont PCM entrant → I2S TX (mono→stéréo) --- */
|
||||
if (m == TONE_SCO) {
|
||||
/* Lire ~20 ms de PCM mono entrant depuis le ring de lecture */
|
||||
size_t want = s_sco_msbc ? (FRAME) : (FRAME / 2); /* samples mono voulus */
|
||||
size_t got_bytes = 0;
|
||||
int16_t *in = (int16_t *)xRingbufferReceiveUpTo(s_sco_play_rb, &got_bytes, 0,
|
||||
want * sizeof(int16_t));
|
||||
int frames = 0;
|
||||
if (in) {
|
||||
int n = (int)(got_bytes / sizeof(int16_t));
|
||||
for (int i = 0; i < n && frames < FRAME; i++) {
|
||||
int16_t v = in[i];
|
||||
buf[frames * 2] = v;
|
||||
buf[frames * 2 + 1] = v;
|
||||
frames++;
|
||||
if (!s_sco_msbc && frames < FRAME) { /* CVSD 8k -> 16k : doubler */
|
||||
buf[frames * 2] = v;
|
||||
buf[frames * 2 + 1] = v;
|
||||
frames++;
|
||||
}
|
||||
}
|
||||
vRingbufferReturnItem(s_sco_play_rb, in);
|
||||
}
|
||||
/* Underrun : compléter avec du silence */
|
||||
while (frames < FRAME) {
|
||||
buf[frames * 2] = 0;
|
||||
buf[frames * 2 + 1] = 0;
|
||||
frames++;
|
||||
}
|
||||
size_t written = 0;
|
||||
s_tone_idle = false;
|
||||
i2s_channel_write(hal_i2s_spk_handle(), buf, sizeof(buf), &written,
|
||||
pdMS_TO_TICKS(50));
|
||||
t += 20;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool on = true;
|
||||
if (m == TONE_BUSY) {
|
||||
on = (t % 1000) < 500;
|
||||
@@ -116,3 +162,75 @@ void audio_router_tones_stop(void)
|
||||
ESP_LOGW(TAG, "tones_stop: timeout waiting for tone task idle");
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* SCO bridge — Phase 4 HFP */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void sco_mic_task(void *a)
|
||||
{
|
||||
(void)a;
|
||||
int16_t mono[FRAME];
|
||||
while (s_sco_active) {
|
||||
int64_t e = 0;
|
||||
int n = hal_i2s_capture_read_frame(mono, FRAME, &e);
|
||||
if (n <= 0) { vTaskDelay(pdMS_TO_TICKS(5)); continue; }
|
||||
if (s_sco_msbc) {
|
||||
/* 16 kHz direct : envoyer tel quel */
|
||||
xRingbufferSend(s_sco_cap_rb, mono, (size_t)(n * (int)sizeof(int16_t)), 0);
|
||||
} else {
|
||||
/* 16k -> 8k : décimer 1 sur 2 */
|
||||
int16_t dec[FRAME / 2];
|
||||
int m = 0;
|
||||
for (int i = 0; i < n; i += 2) dec[m++] = mono[i];
|
||||
xRingbufferSend(s_sco_cap_rb, dec, (size_t)(m * (int)sizeof(int16_t)), 0);
|
||||
}
|
||||
}
|
||||
s_sco_mic_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void audio_router_sco_begin(bool msbc)
|
||||
{
|
||||
s_sco_msbc = msbc;
|
||||
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);
|
||||
hal_i2s_capture_begin();
|
||||
hal_audio_pa_set(true);
|
||||
audio_router_init(); /* s'assure que tone_task existe */
|
||||
s_sco_active = true;
|
||||
s_mode = TONE_SCO;
|
||||
if (!s_sco_mic_task) {
|
||||
xTaskCreatePinnedToCore(sco_mic_task, "sco_mic", 4096, NULL, 6, &s_sco_mic_task, 1);
|
||||
}
|
||||
ESP_LOGI(TAG, "SCO bridge ON (%s)", msbc ? "mSBC 16k" : "CVSD 8k");
|
||||
}
|
||||
|
||||
void audio_router_sco_end(void)
|
||||
{
|
||||
s_sco_active = false;
|
||||
s_mode = TONE_NONE;
|
||||
hal_audio_pa_set(false);
|
||||
hal_i2s_capture_end();
|
||||
ESP_LOGI(TAG, "SCO bridge OFF");
|
||||
}
|
||||
|
||||
void audio_router_sco_feed_playback(const uint8_t *pcm, uint32_t len)
|
||||
{
|
||||
if (s_sco_play_rb && s_sco_active) {
|
||||
xRingbufferSend(s_sco_play_rb, pcm, len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t audio_router_sco_take_capture(uint8_t *pcm, uint32_t len)
|
||||
{
|
||||
if (!s_sco_cap_rb) return 0;
|
||||
size_t got = 0;
|
||||
uint8_t *d = (uint8_t *)xRingbufferReceiveUpTo(s_sco_cap_rb, &got, 0, len);
|
||||
if (d) {
|
||||
memcpy(pcm, d, got);
|
||||
vRingbufferReturnItem(s_sco_cap_rb, d);
|
||||
return (uint32_t)got;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -19,6 +22,12 @@ void audio_router_ringback_start(void);
|
||||
/* Stop tones; waits (<=200 ms) until the tone task leaves i2s_channel_write. */
|
||||
void audio_router_tones_stop(void);
|
||||
|
||||
/* SCO bridge (Phase 4 HFP). audio_router stays the single I2S TX writer. */
|
||||
void audio_router_sco_begin(bool msbc);
|
||||
void audio_router_sco_end(void);
|
||||
void audio_router_sco_feed_playback(const uint8_t *pcm, uint32_t len);
|
||||
uint32_t audio_router_sco_take_capture(uint8_t *pcm, uint32_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user