Skip to content

Commit 02d6661

Browse files
Thalleycarlescufi
authored andcommitted
Bluetooth: Shell: Add support for sending sine tone with the audio shell
added `audio start_sine` and `audio stop_sine` to start/stop sending an LC3 encoded sine tone. This code is heavily inspired from the sine tone implementation from the unicast_client sample implementation. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
1 parent 83d3a36 commit 02d6661

File tree

2 files changed

+273
-24
lines changed

2 files changed

+273
-24
lines changed

subsys/bluetooth/shell/audio.c

Lines changed: 265 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,219 @@ static struct named_lc3_preset lc3_broadcast_presets[] = {
144144
/* Default to 16_2_1 */
145145
static struct named_lc3_preset *default_preset = &lc3_unicast_presets[3];
146146

147+
static uint32_t get_next_seq_num(uint32_t interval_us)
148+
{
149+
static int64_t last_ticks;
150+
int64_t uptime_ticks, delta_ticks;
151+
uint64_t delta_us;
152+
uint64_t seq_num_incr;
153+
uint64_t next_seq_num;
154+
155+
/* Note: This does not handle wrapping of ticks when they go above
156+
* 2^(62-1)
157+
*/
158+
uptime_ticks = k_uptime_ticks();
159+
delta_ticks = uptime_ticks - last_ticks;
160+
last_ticks = uptime_ticks;
161+
162+
delta_us = k_ticks_to_us_near64((uint64_t)delta_ticks);
163+
seq_num_incr = delta_us / interval_us;
164+
next_seq_num = (seq_num_incr + seq_num);
165+
166+
return (uint32_t)next_seq_num;
167+
}
168+
169+
#if defined(CONFIG_LIBLC3)
170+
NET_BUF_POOL_FIXED_DEFINE(sine_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
171+
CONFIG_BT_ISO_TX_MTU + BT_ISO_CHAN_SEND_RESERVE,
172+
8, NULL);
173+
174+
#include "lc3.h"
175+
#include "math.h"
176+
177+
#define MAX_SAMPLE_RATE 48000
178+
#define MAX_FRAME_DURATION_US 10000
179+
#define MAX_NUM_SAMPLES ((MAX_FRAME_DURATION_US * MAX_SAMPLE_RATE) / USEC_PER_SEC)
180+
#define AUDIO_VOLUME (INT16_MAX - 3000) /* codec does clipping above INT16_MAX - 3000 */
181+
#define AUDIO_TONE_FREQUENCY_HZ 400
182+
183+
static int16_t audio_buf[MAX_NUM_SAMPLES];
184+
static lc3_encoder_t lc3_encoder;
185+
static lc3_encoder_mem_48k_t lc3_encoder_mem;
186+
static int freq_hz;
187+
static int frame_duration_us;
188+
static int frame_duration_100us;
189+
static int frames_per_sdu;
190+
static int octets_per_frame;
191+
192+
/**
193+
* Use the math lib to generate a sine-wave using 16 bit samples into a buffer.
194+
*
195+
* @param buf Destination buffer
196+
* @param length_us Length of the buffer in microseconds
197+
* @param frequency_hz frequency in Hz
198+
* @param sample_rate_hz sample-rate in Hz.
199+
*/
200+
static void fill_audio_buf_sin(int16_t *buf, int length_us, int frequency_hz, int sample_rate_hz)
201+
{
202+
const uint32_t sine_period_samples = sample_rate_hz / frequency_hz;
203+
const size_t num_samples = (length_us * sample_rate_hz) / USEC_PER_SEC;
204+
const float step = 2 * 3.1415 / sine_period_samples;
205+
206+
for (size_t i = 0; i < num_samples; i++) {
207+
const float sample = sin(i * step);
208+
209+
buf[i] = (int16_t)(AUDIO_VOLUME * sample);
210+
}
211+
}
212+
213+
static void init_lc3(void)
214+
{
215+
size_t num_samples;
216+
217+
freq_hz = bt_codec_cfg_get_freq(&default_preset->preset.codec);
218+
frame_duration_us = bt_codec_cfg_get_frame_duration_us(&default_preset->preset.codec);
219+
octets_per_frame = bt_codec_cfg_get_octets_per_frame(&default_preset->preset.codec);
220+
frames_per_sdu = bt_codec_cfg_get_frame_blocks_per_sdu(&default_preset->preset.codec, true);
221+
octets_per_frame = bt_codec_cfg_get_octets_per_frame(&default_preset->preset.codec);
222+
223+
if (freq_hz < 0) {
224+
printk("Error: Codec frequency not set, cannot start codec.");
225+
return;
226+
}
227+
228+
if (frame_duration_us < 0) {
229+
printk("Error: Frame duration not set, cannot start codec.");
230+
return;
231+
}
232+
233+
if (octets_per_frame < 0) {
234+
printk("Error: Octets per frame not set, cannot start codec.");
235+
return;
236+
}
237+
238+
frame_duration_100us = frame_duration_us / 100;
239+
240+
/* Fill audio buffer with Sine wave only once and repeat encoding the same tone frame */
241+
fill_audio_buf_sin(audio_buf, frame_duration_us, AUDIO_TONE_FREQUENCY_HZ, freq_hz);
242+
243+
num_samples = ((frame_duration_us * freq_hz) / USEC_PER_SEC);
244+
for (size_t i = 0; i < num_samples; i++) {
245+
printk("%zu: %6i\n", i, audio_buf[i]);
246+
}
247+
248+
/* Create the encoder instance. This shall complete before stream_started() is called. */
249+
lc3_encoder = lc3_setup_encoder(frame_duration_us, freq_hz,
250+
0, /* No resampling */
251+
&lc3_encoder_mem);
252+
253+
if (lc3_encoder == NULL) {
254+
printk("ERROR: Failed to setup LC3 encoder - wrong parameters?\n");
255+
}
256+
}
257+
258+
static void lc3_audio_timer_timeout(struct k_work *work)
259+
{
260+
/* For the first call-back we push multiple audio frames to the buffer to use the
261+
* controller ISO buffer to handle jitter.
262+
*/
263+
const uint8_t prime_count = 2;
264+
static bool lc3_initialized;
265+
static int64_t start_time;
266+
static int32_t sdu_cnt;
267+
int64_t run_time_100us;
268+
int32_t sdu_goal_cnt;
269+
int64_t run_time_ms;
270+
int64_t uptime;
271+
272+
if (!lc3_initialized) {
273+
init_lc3();
274+
lc3_initialized = true;
275+
}
276+
277+
if (lc3_encoder == NULL) {
278+
printk("LC3 encoder not setup, cannot encode data.\n");
279+
return;
280+
}
281+
282+
k_work_schedule(k_work_delayable_from_work(work),
283+
K_USEC(default_preset->preset.qos.interval));
284+
285+
if (start_time == 0) {
286+
/* Read start time and produce the number of frames needed to catch up with any
287+
* inaccuracies in the timer. by calculating the number of frames we should
288+
* have sent and compare to how many were actually sent.
289+
*/
290+
start_time = k_uptime_get();
291+
}
292+
293+
uptime = k_uptime_get();
294+
run_time_ms = uptime - start_time;
295+
296+
/* PDU count calculations done in 100us units to allow 7.5ms framelength in fixed-point */
297+
run_time_100us = run_time_ms * 10;
298+
sdu_goal_cnt = run_time_100us / (frame_duration_100us * frames_per_sdu);
299+
300+
/* Add primer value to ensure the controller do not run low on data due to jitter */
301+
sdu_goal_cnt += prime_count;
302+
303+
if ((sdu_cnt % 100) == 0) {
304+
printk("LC3 encode %d frames in %d SDUs\n",
305+
(sdu_goal_cnt - sdu_cnt) * frames_per_sdu,
306+
(sdu_goal_cnt - sdu_cnt));
307+
}
308+
309+
seq_num = get_next_seq_num(default_preset->preset.qos.interval);
310+
311+
while (sdu_cnt < sdu_goal_cnt) {
312+
const uint16_t tx_sdu_len = frames_per_sdu * octets_per_frame;
313+
struct net_buf *buf;
314+
uint8_t *net_buffer;
315+
off_t offset = 0;
316+
int err;
317+
318+
buf = net_buf_alloc(&sine_tx_pool, K_FOREVER);
319+
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
320+
321+
net_buffer = net_buf_tail(buf);
322+
buf->len += tx_sdu_len;
323+
324+
for (int i = 0; i < frames_per_sdu; i++) {
325+
int lc3_ret;
326+
327+
lc3_ret = lc3_encode(lc3_encoder, LC3_PCM_FORMAT_S16,
328+
audio_buf, 1, octets_per_frame,
329+
net_buffer + offset);
330+
offset += octets_per_frame;
331+
332+
if (lc3_ret == -1) {
333+
printk("LC3 encoder failed - wrong parameters?: %d",
334+
lc3_ret);
335+
net_buf_unref(buf);
336+
return;
337+
}
338+
}
339+
340+
err = bt_audio_stream_send(default_stream, buf, seq_num,
341+
BT_ISO_TIMESTAMP_NONE);
342+
if (err < 0) {
343+
printk("Failed to send LC3 audio data (%d)\n",
344+
err);
345+
net_buf_unref(buf);
346+
return;
347+
}
348+
349+
if ((sdu_cnt % 100) == 0) {
350+
printk("TX LC3: %zu\n", tx_sdu_len);
351+
}
352+
sdu_cnt++;
353+
seq_num++;
354+
}
355+
}
356+
357+
static K_WORK_DELAYABLE_DEFINE(audio_send_work, lc3_audio_timer_timeout);
358+
#endif /* CONFIG_LIBLC3 */
359+
147360
static void print_codec(const struct bt_codec *codec)
148361
{
149362
int i;
@@ -1155,11 +1368,32 @@ static void audio_recv(struct bt_audio_stream *stream,
11551368
{
11561369
shell_print(ctx_shell, "Incoming audio on stream %p len %u\n", stream, buf->len);
11571370
}
1371+
#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SINK */
1372+
1373+
static void stream_started_cb(struct bt_audio_stream *stream)
1374+
{
1375+
printk("Stream %p started\n", stream);
1376+
}
1377+
1378+
static void stream_stopped_cb(struct bt_audio_stream *stream)
1379+
{
1380+
printk("Stream %p stopped\n", stream);
1381+
1382+
1383+
#if defined(CONFIG_LIBLC3)
1384+
if (stream == default_stream) {
1385+
k_work_cancel_delayable(&audio_send_work);
1386+
}
1387+
#endif /* CONFIG_LIBLC3 */
1388+
}
11581389

11591390
static struct bt_audio_stream_ops stream_ops = {
1160-
.recv = audio_recv
1161-
};
1391+
#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SINK)
1392+
.recv = audio_recv,
11621393
#endif /* CONFIG_BT_AUDIO_UNICAST || CONFIG_BT_AUDIO_BROADCAST_SINK */
1394+
.started = stream_started_cb,
1395+
.stopped = stream_stopped_cb,
1396+
};
11631397

11641398
#if defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
11651399
static int cmd_select_broadcast_source(const struct shell *sh, size_t argc,
@@ -1517,34 +1751,19 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[])
15171751
&stream_ops);
15181752
}
15191753
#endif /* CONFIG_BT_AUDIO_BROADCAST_SOURCE */
1754+
1755+
#if defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
1756+
for (i = 0; i < ARRAY_SIZE(broadcast_source_streams); i++) {
1757+
bt_audio_stream_cb_register(&broadcast_source_streams[i],
1758+
&stream_ops);
1759+
}
1760+
#endif /* CONFIG_BT_AUDIO_BROADCAST_SOURCE */
15201761
return 0;
15211762
}
15221763

15231764
#define DATA_MTU CONFIG_BT_ISO_TX_MTU
15241765
NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1, DATA_MTU, 8, NULL);
15251766

1526-
static uint32_t get_next_seq_num(uint32_t interval_us)
1527-
{
1528-
static int64_t last_ticks;
1529-
int64_t uptime_ticks, delta_ticks;
1530-
uint64_t delta_us;
1531-
uint64_t seq_num_incr;
1532-
uint64_t next_seq_num;
1533-
1534-
/* Note: This does not handle wrapping of ticks when they go above
1535-
* 2^(62-1)
1536-
*/
1537-
uptime_ticks = k_uptime_ticks();
1538-
delta_ticks = uptime_ticks - last_ticks;
1539-
last_ticks = uptime_ticks;
1540-
1541-
delta_us = k_ticks_to_us_near64((uint64_t)delta_ticks);
1542-
seq_num_incr = delta_us / interval_us;
1543-
next_seq_num = (seq_num_incr + seq_num);
1544-
1545-
return (uint32_t)next_seq_num;
1546-
}
1547-
15481767
static int cmd_send(const struct shell *sh, size_t argc, char *argv[])
15491768
{
15501769
static uint8_t data[DATA_MTU - BT_ISO_CHAN_SEND_RESERVE];
@@ -1583,6 +1802,22 @@ static int cmd_send(const struct shell *sh, size_t argc, char *argv[])
15831802
return 0;
15841803
}
15851804

1805+
#if defined(CONFIG_LIBLC3)
1806+
static int cmd_start_sine(const struct shell *sh, size_t argc, char *argv[])
1807+
{
1808+
k_work_schedule(&audio_send_work, K_MSEC(0));
1809+
1810+
return 0;
1811+
}
1812+
1813+
static int cmd_stop_sine(const struct shell *sh, size_t argc, char *argv[])
1814+
{
1815+
k_work_cancel_delayable(&audio_send_work);
1816+
1817+
return 0;
1818+
}
1819+
#endif /* CONFIG_LIBLC3 */
1820+
15861821
SHELL_STATIC_SUBCMD_SET_CREATE(audio_cmds,
15871822
SHELL_CMD_ARG(init, NULL, NULL, cmd_init, 1, 0),
15881823
#if defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
@@ -1633,6 +1868,12 @@ SHELL_STATIC_SUBCMD_SET_CREATE(audio_cmds,
16331868
"<stream>", cmd_select_unicast, 2, 0),
16341869
SHELL_CMD_ARG(send, NULL, "Send to Audio Stream [data]",
16351870
cmd_send, 1, 1),
1871+
#if defined(CONFIG_LIBLC3)
1872+
SHELL_CMD_ARG(start_sine, NULL, "Start sending a LC3 encoded sine wave",
1873+
cmd_start_sine, 1, 0),
1874+
SHELL_CMD_ARG(stop_sine, NULL, "Stop sending a LC3 encoded sine wave",
1875+
cmd_stop_sine, 1, 0),
1876+
#endif /* CONFIG_LIBLC3 */
16361877
SHELL_COND_CMD_ARG(CONFIG_BT_AUDIO_CAPABILITY, set_location, NULL,
16371878
"<direction: sink, source> <location bitmask>",
16381879
cmd_set_loc, 3, 0),
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# For LC3 the following configs are needed
2+
CONFIG_FPU=y
3+
CONFIG_LIBLC3=y
4+
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
5+
# inctease stack size for that thread.
6+
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
7+
# LC3 lib requires floating point support in the c-lib NEWLIB is one way of getting that.
8+
CONFIG_NEWLIB_LIBC=y

0 commit comments

Comments
 (0)