Skip to content

Commit 518b0e6

Browse files
committed
audio: bt: add high-quality audio format presets, dynamic kcontrol, and shell format selector
1 parent 2127aa8 commit 518b0e6

6 files changed

Lines changed: 266 additions & 20 deletions

File tree

‎src/audio/bt_audio.c‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,13 @@ static struct comp_dev *bt_audio_new(const struct comp_driver *drv,
191191

192192
dev->ipc_config = *config;
193193

194-
bad = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*bad));
195-
if (!bad) {
196-
rfree(dev);
197-
return NULL;
194+
/* Use static instances to avoid dynamic heap allocations */
195+
if (config->pipeline_id == 1 || config->id == 15) {
196+
bad = &s_bt_playback_inst;
197+
g_bt_playback_data = bad;
198+
} else {
199+
bad = &s_bt_capture_inst;
200+
g_bt_capture_data = bad;
198201
}
199202

200203
comp_set_drvdata(dev, bad);
@@ -204,12 +207,6 @@ static struct comp_dev *bt_audio_new(const struct comp_driver *drv,
204207
bad->frame_bytes = 4;
205208
bad->period_bytes = (bad->sample_rate / 1000) * bad->frame_bytes;
206209

207-
if (config->pipeline_id == 1 || config->id == 15) {
208-
g_bt_playback_data = bad;
209-
} else {
210-
g_bt_capture_data = bad;
211-
}
212-
213210
dev->state = COMP_STATE_READY;
214211
return dev;
215212
}
@@ -225,7 +222,7 @@ static void bt_audio_free(struct comp_dev *dev)
225222
g_bt_capture_data = NULL;
226223
}
227224

228-
rfree(bad);
225+
/* bad points to static BSS instance, do not rfree */
229226
rfree(dev);
230227
}
231228

‎src/audio/bt_service.c‎

Lines changed: 165 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,110 @@
1515
LOG_MODULE_REGISTER(bt_service, CONFIG_SOF_LOG_LEVEL);
1616

1717
#define ESP32P4_GPIO_C6_EN_BIT (1U << 22) /* GPIO 54 = 32 + 22 */
18-
#define BT_AUDIO_FRAME_MS 10
19-
#define BT_AUDIO_FRAME_SAMPLES 480 /* 10ms at 48kHz */
20-
#define BT_AUDIO_FRAME_BYTES (BT_AUDIO_FRAME_SAMPLES * 2 * sizeof(int16_t)) /* 1920 bytes */
18+
19+
static const struct bt_audio_format_desc s_formats[BT_AUDIO_FMT_COUNT] = {
20+
[BT_AUDIO_FMT_48K_STD] = {
21+
.format = BT_AUDIO_FMT_48K_STD,
22+
.name = "48k_std",
23+
.codec_name = "LC3",
24+
.sample_rate = 48000,
25+
.bit_depth = 16,
26+
.frame_duration_us = 10000,
27+
.frame_duration_ms = 10,
28+
.octets_per_codec_frame = 100,
29+
.bitrate_kbps = 160,
30+
.frame_samples = 480,
31+
.frame_bytes = 1920,
32+
},
33+
[BT_AUDIO_FMT_48K_HQ] = {
34+
.format = BT_AUDIO_FMT_48K_HQ,
35+
.name = "48k_hq",
36+
.codec_name = "LC3",
37+
.sample_rate = 48000,
38+
.bit_depth = 16,
39+
.frame_duration_us = 10000,
40+
.frame_duration_ms = 10,
41+
.octets_per_codec_frame = 120,
42+
.bitrate_kbps = 192,
43+
.frame_samples = 480,
44+
.frame_bytes = 1920,
45+
},
46+
[BT_AUDIO_FMT_48K_MAX] = {
47+
.format = BT_AUDIO_FMT_48K_MAX,
48+
.name = "48k_max",
49+
.codec_name = "LC3",
50+
.sample_rate = 48000,
51+
.bit_depth = 24,
52+
.frame_duration_us = 10000,
53+
.frame_duration_ms = 10,
54+
.octets_per_codec_frame = 240,
55+
.bitrate_kbps = 384,
56+
.frame_samples = 480,
57+
.frame_bytes = 1920,
58+
},
59+
[BT_AUDIO_FMT_44K] = {
60+
.format = BT_AUDIO_FMT_44K,
61+
.name = "44k",
62+
.codec_name = "LC3",
63+
.sample_rate = 44100,
64+
.bit_depth = 16,
65+
.frame_duration_us = 10000,
66+
.frame_duration_ms = 10,
67+
.octets_per_codec_frame = 130,
68+
.bitrate_kbps = 208,
69+
.frame_samples = 441,
70+
.frame_bytes = 1764,
71+
},
72+
[BT_AUDIO_FMT_48K_LL] = {
73+
.format = BT_AUDIO_FMT_48K_LL,
74+
.name = "48k_ll",
75+
.codec_name = "LC3",
76+
.sample_rate = 48000,
77+
.bit_depth = 16,
78+
.frame_duration_us = 7500,
79+
.frame_duration_ms = 8,
80+
.octets_per_codec_frame = 75,
81+
.bitrate_kbps = 160,
82+
.frame_samples = 360,
83+
.frame_bytes = 1440,
84+
},
85+
[BT_AUDIO_FMT_96K_HR] = {
86+
.format = BT_AUDIO_FMT_96K_HR,
87+
.name = "96k_hr",
88+
.codec_name = "LC3plus-HR",
89+
.sample_rate = 96000,
90+
.bit_depth = 24,
91+
.frame_duration_us = 5000,
92+
.frame_duration_ms = 5,
93+
.octets_per_codec_frame = 160,
94+
.bitrate_kbps = 512,
95+
.frame_samples = 480,
96+
.frame_bytes = 1920,
97+
},
98+
[BT_AUDIO_FMT_LPCM] = {
99+
.format = BT_AUDIO_FMT_LPCM,
100+
.name = "lpcm",
101+
.codec_name = "LPCM (Raw)",
102+
.sample_rate = 32000,
103+
.bit_depth = 16,
104+
.frame_duration_us = 10000,
105+
.frame_duration_ms = 10,
106+
.octets_per_codec_frame = 640,
107+
.bitrate_kbps = 1024,
108+
.frame_samples = 320,
109+
.frame_bytes = 1280,
110+
},
111+
};
21112

22113
static struct bt_service_status s_status = {
23114
.c6_powered = false,
24115
.state = BT_STATE_DISABLED,
25116
.route = SOF_AUDIO_ROUTE_USB_DAI,
117+
.format = BT_AUDIO_FMT_48K_STD,
26118
.sample_rate = 48000,
119+
.bitrate_kbps = 160,
120+
.frame_bytes = 1920,
121+
.codec_name = "LC3",
27122
.tx_packets = 0,
28123
.rx_packets = 0,
29124
.tx_bytes = 0,
@@ -61,8 +156,10 @@ int bt_service_start_broadcast(void)
61156
k_msleep(150);
62157
}
63158

159+
const struct bt_audio_format_desc *desc = &s_formats[s_status.format];
64160
s_status.state = BT_STATE_BROADCASTING;
65-
LOG_INF("Bluetooth LE Audio Broadcast started at %u Hz stereo (10ms ISO SDU)", s_status.sample_rate);
161+
LOG_INF("Bluetooth LE Audio Broadcast started: '%s' (%s @ %u Hz, %u kbps, %u ms SDU)",
162+
desc->name, desc->codec_name, desc->sample_rate, desc->bitrate_kbps, desc->frame_duration_ms);
66163
return 0;
67164
}
68165

@@ -102,6 +199,62 @@ enum sof_audio_route bt_service_get_route(void)
102199
return s_status.route;
103200
}
104201

202+
int bt_service_set_format(enum bt_audio_format fmt)
203+
{
204+
if (fmt >= BT_AUDIO_FMT_COUNT) {
205+
LOG_ERR("Invalid Bluetooth audio format: %d", fmt);
206+
return -EINVAL;
207+
}
208+
209+
const struct bt_audio_format_desc *desc = &s_formats[fmt];
210+
s_status.format = fmt;
211+
s_status.sample_rate = desc->sample_rate;
212+
s_status.bitrate_kbps = desc->bitrate_kbps;
213+
s_status.frame_bytes = desc->frame_bytes;
214+
s_status.codec_name = desc->codec_name;
215+
216+
bt_audio_set_playback_rate(desc->sample_rate);
217+
bt_audio_set_capture_rate(desc->sample_rate);
218+
219+
LOG_INF("Bluetooth audio format set to '%s' (%s @ %u Hz %u-bit, %u kbps, %u ms SDU, %u B PCM)",
220+
desc->name, desc->codec_name, desc->sample_rate, desc->bit_depth,
221+
desc->bitrate_kbps, desc->frame_duration_ms, desc->frame_bytes);
222+
return 0;
223+
}
224+
225+
enum bt_audio_format bt_service_get_format(void)
226+
{
227+
return s_status.format;
228+
}
229+
230+
const struct bt_audio_format_desc *bt_service_get_format_desc(enum bt_audio_format fmt)
231+
{
232+
if (fmt >= BT_AUDIO_FMT_COUNT) {
233+
return NULL;
234+
}
235+
return &s_formats[fmt];
236+
}
237+
238+
const struct bt_audio_format_desc *bt_service_get_current_format_desc(void)
239+
{
240+
return &s_formats[s_status.format];
241+
}
242+
243+
int bt_service_format_from_name(const char *name, enum bt_audio_format *fmt)
244+
{
245+
if (!name || !fmt) {
246+
return -EINVAL;
247+
}
248+
249+
for (int i = 0; i < BT_AUDIO_FMT_COUNT; i++) {
250+
if (strcmp(name, s_formats[i].name) == 0) {
251+
*fmt = (enum bt_audio_format)i;
252+
return 0;
253+
}
254+
}
255+
return -ENOENT;
256+
}
257+
105258
void bt_service_get_status(struct bt_service_status *status)
106259
{
107260
if (status) {
@@ -116,12 +269,16 @@ static void bt_audio_stream_task(void *p1, void *p2, void *p3)
116269
ARG_UNUSED(p2);
117270
ARG_UNUSED(p3);
118271

119-
static uint8_t s_audio_buf[BT_AUDIO_FRAME_BYTES] __aligned(4);
272+
static uint8_t s_audio_buf[1920] __aligned(4);
120273

121274
while (s_thread_running) {
275+
const struct bt_audio_format_desc *desc = &s_formats[s_status.format];
276+
uint32_t frame_bytes = desc->frame_bytes;
277+
uint32_t sleep_ms = desc->frame_duration_ms ? desc->frame_duration_ms : 10;
278+
122279
if (s_status.state == BT_STATE_BROADCASTING) {
123280
/* Fetch audio from pipeline/bt_audio component */
124-
size_t fetched = bt_audio_fetch_capture_data(s_audio_buf, BT_AUDIO_FRAME_BYTES);
281+
size_t fetched = bt_audio_fetch_capture_data(s_audio_buf, frame_bytes);
125282
if (fetched > 0) {
126283
s_status.tx_packets++;
127284
s_status.tx_bytes += fetched;
@@ -135,10 +292,10 @@ static void bt_audio_stream_task(void *p1, void *p2, void *p3)
135292
}
136293
} else if (s_status.state == BT_STATE_RECEIVING) {
137294
s_status.rx_packets++;
138-
s_status.rx_bytes += BT_AUDIO_FRAME_BYTES;
295+
s_status.rx_bytes += frame_bytes;
139296
}
140297

141-
k_msleep(BT_AUDIO_FRAME_MS);
298+
k_msleep(sleep_ms);
142299
}
143300
}
144301

‎src/audio/pipeline/static_pipeline_loader.c‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <sof/audio/pipeline/static_pipeline.h>
77
#include <sof/audio/pipeline/sof_static_pipeline.h>
8+
#include <sof/audio/bt_service.h>
89
#include <sof/audio/component_ext.h>
910
#include <sof/audio/pipeline.h>
1011
#include <sof/audio/buffer.h>
@@ -544,6 +545,12 @@ int sof_static_kcontrol_set(uint32_t ctrl_id, int32_t val)
544545
return sof_static_pipeline_set_route((enum sof_audio_route)val);
545546
}
546547

548+
if (ctl->id == 14 || (ctl->name && !strcmp(ctl->name, "BT Audio Format"))) {
549+
s_control_vals[ctl_idx] = val;
550+
LOG_INF("Kcontrol [%u] '%s' set to %d", ctl->id, ctl->name, val);
551+
return bt_service_set_format((enum bt_audio_format)val);
552+
}
553+
547554
struct comp_dev *dev = sof_static_comp_get(ctl->target_comp_id);
548555
if (!dev)
549556
return -ENODEV;

‎src/debug/shell/esp32_shell.c‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ static int cmd_sof_bt_status(const struct shell *sh, size_t argc, char **argv)
364364

365365
struct bt_service_status st;
366366
bt_service_get_status(&st);
367+
const struct bt_audio_format_desc *desc = bt_service_get_current_format_desc();
367368

368369
shell_print(sh, "=== Bluetooth LE Audio / C6 Co-Processor Status ===");
369370
shell_print(sh, " ESP32-C6 Power: %s (GPIO 54 asserted)", st.c6_powered ? "ON" : "OFF");
@@ -372,14 +373,57 @@ static int cmd_sof_bt_status(const struct shell *sh, size_t argc, char **argv)
372373
(st.state == BT_STATE_READY ? "READY" :
373374
(st.state == BT_STATE_BROADCASTING ? "BROADCASTING" :
374375
(st.state == BT_STATE_RECEIVING ? "RECEIVING" : "SCANNING"))));
375-
shell_print(sh, " Profile / Codec: BAP Broadcast / LC3 @ %u Hz 2ch (10ms ISO SDU)", st.sample_rate);
376+
shell_print(sh, " Active Format: %s (%s)", desc ? desc->name : "unknown", desc ? desc->codec_name : "LC3");
377+
shell_print(sh, " Audio Quality: %u Hz, %u-bit stereo, %u kbps (%u.%u ms SDU, %u B PCM)",
378+
st.sample_rate, desc ? desc->bit_depth : 16, st.bitrate_kbps,
379+
desc ? (desc->frame_duration_us / 1000) : 10,
380+
desc ? ((desc->frame_duration_us % 1000) / 100) : 0,
381+
st.frame_bytes);
376382
shell_print(sh, " TX Packets / Bytes:%u pkts / %u bytes", st.tx_packets, st.tx_bytes);
377383
shell_print(sh, " RX Packets / Bytes:%u pkts / %u bytes", st.rx_packets, st.rx_bytes);
378384
shell_print(sh, " Link RSSI: %d dBm", st.rssi);
379385
shell_print(sh, "====================================================");
380386
return 0;
381387
}
382388

389+
static int cmd_sof_bt_format(const struct shell *sh, size_t argc, char **argv)
390+
{
391+
if (argc < 2) {
392+
enum bt_audio_format current = bt_service_get_format();
393+
shell_print(sh, "=== Available Bluetooth Audio Formats ===");
394+
shell_print(sh, " ID | Format Key | Codec | Sample Rate | Bits | Frame | Bitrate | Frame PCM");
395+
shell_print(sh, "-----+------------+------------+-------------+------+-------+----------+----------");
396+
for (int i = 0; i < BT_AUDIO_FMT_COUNT; i++) {
397+
const struct bt_audio_format_desc *d = bt_service_get_format_desc((enum bt_audio_format)i);
398+
if (!d) continue;
399+
char cur_mark = (i == (int)current) ? '*' : ' ';
400+
shell_print(sh, " %c%d | %-10s | %-10s | %6u Hz | %2u | %2u.%1u ms| %4u kbps| %5u B",
401+
cur_mark, i, d->name, d->codec_name, d->sample_rate, d->bit_depth,
402+
d->frame_duration_us / 1000, (d->frame_duration_us % 1000) / 100,
403+
d->bitrate_kbps, d->frame_bytes);
404+
}
405+
shell_print(sh, "=========================================");
406+
shell_print(sh, "Usage: sof bt format <name|id>");
407+
return 0;
408+
}
409+
410+
enum bt_audio_format target_fmt;
411+
char *endptr;
412+
long val = strtol(argv[1], &endptr, 10);
413+
if (*endptr == '\0' && val >= 0 && val < BT_AUDIO_FMT_COUNT) {
414+
target_fmt = (enum bt_audio_format)val;
415+
} else if (bt_service_format_from_name(argv[1], &target_fmt) != 0) {
416+
shell_error(sh, "Unknown format: %s. Run 'sof bt format' to see available formats.", argv[1]);
417+
return -EINVAL;
418+
}
419+
420+
sof_static_kcontrol_set(14, (int32_t)target_fmt);
421+
const struct bt_audio_format_desc *desc = bt_service_get_format_desc(target_fmt);
422+
shell_print(sh, "Bluetooth audio format switched to: %s (%s @ %u Hz, %u kbps)",
423+
desc->name, desc->codec_name, desc->sample_rate, desc->bitrate_kbps);
424+
return 0;
425+
}
426+
383427
static int cmd_sof_bt_broadcast(const struct shell *sh, size_t argc, char **argv)
384428
{
385429
if (argc < 2) {
@@ -456,6 +500,7 @@ static int cmd_sof_route(const struct shell *sh, size_t argc, char **argv)
456500

457501
SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds,
458502
SHELL_CMD(status, NULL, "Print Bluetooth LE Audio / C6 coprocessor status", cmd_sof_bt_status),
503+
SHELL_CMD(format, NULL, "Get or set BT audio format (sof bt format [name|id])", cmd_sof_bt_format),
459504
SHELL_CMD(broadcast, NULL, "Start/stop LE Audio broadcast (sof bt broadcast <start|stop>)", cmd_sof_bt_broadcast),
460505
SHELL_CMD(scan, NULL, "Scan for nearby LE Audio devices", cmd_sof_bt_scan),
461506
SHELL_CMD(power, NULL, "Control ESP32-C6 power (sof bt power <on|off>)", cmd_sof_bt_power),

0 commit comments

Comments
 (0)