-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
streamctrl.c
442 lines (347 loc) · 10.6 KB
/
streamctrl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include "streamctrl.h"
#include <zephyr/kernel.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/device.h>
#include <zephyr/debug/stack.h>
#include "ctrl_events.h"
#include "led.h"
#include "button_assignments.h"
#include "macros_common.h"
#include "audio_system.h"
#include "button_handler.h"
#include "data_fifo.h"
#include "board.h"
#include "le_audio.h"
#include "audio_datapath.h"
#include "audio_sync_timer.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(streamctrl, CONFIG_LOG_STREAMCTRL_LEVEL);
struct ble_iso_data {
uint8_t data[CONFIG_BT_ISO_RX_MTU];
size_t data_size;
bool bad_frame;
uint32_t sdu_ref;
uint32_t recv_frame_ts;
} __packed;
DATA_FIFO_DEFINE(ble_fifo_rx, CONFIG_BUF_BLE_RX_PACKET_NUM, WB_UP(sizeof(struct ble_iso_data)));
#define TEST_TONE_BASE_FREQ_HZ 1000
static struct k_thread audio_datapath_thread_data;
static k_tid_t audio_datapath_thread_id;
K_THREAD_STACK_DEFINE(audio_datapath_thread_stack, CONFIG_AUDIO_DATAPATH_STACK_SIZE);
static enum stream_state strm_state = STATE_PAUSED;
#if (CONFIG_BLE_ISO_TEST_PATTERN)
struct iso_recv_stats {
uint32_t iso_rx_packets_received;
uint32_t iso_rx_packets_lost;
};
static struct iso_recv_stats stats_overall;
/* Print statistics from test pattern run */
static void stats_print(char const *const name, struct iso_recv_stats const *const stats)
{
uint32_t total_packets;
total_packets = stats->iso_rx_packets_received + stats->iso_rx_packets_lost;
LOG_INF("%s: Received %u/%u (%.2f%%) - Lost %u", name, stats->iso_rx_packets_received,
total_packets, (float)stats->iso_rx_packets_received * 100 / total_packets,
stats->iso_rx_packets_lost);
}
/* Separate function for assessing link quality using a test pattern sent from gateway */
static void ble_test_pattern_receive(uint8_t const *const p_data, size_t data_size, bool bad_frame)
{
uint32_t total_packets;
static uint8_t expected_packet_value;
stats_overall.iso_rx_packets_received++;
if (bad_frame) {
LOG_WRN("Received bad frame");
}
total_packets = stats_overall.iso_rx_packets_received + stats_overall.iso_rx_packets_lost;
/* Only check first value in packet */
if (p_data[0] == expected_packet_value) {
expected_packet_value++;
} else {
/* First packet will always be a mismatch
* if gateway hasn't been reset before connection
*/
if (stats_overall.iso_rx_packets_received != 1) {
stats_overall.iso_rx_packets_lost++;
LOG_WRN("Missing packet: value: %d, expected: %d", p_data[0],
expected_packet_value);
}
expected_packet_value = p_data[0] + 1;
}
if ((total_packets % 100) == 0) {
stats_print("Overall ", &stats_overall);
}
}
#endif /* (CONFIG_BLE_ISO_TEST_PATTERN) */
/* Callback for handling BLE RX */
static void le_audio_rx_data_handler(uint8_t const *const p_data, size_t data_size, bool bad_frame,
uint32_t sdu_ref)
{
/* Capture timestamp of when audio frame is received */
uint32_t recv_frame_ts = audio_sync_timer_curr_time_get();
/* Since the audio datapath thread is preemptive, no actions on the
* FIFO can happen whilst in this handler.
*/
if (strm_state != STATE_STREAMING) {
/* Throw away data */
LOG_DBG("Not in streaming state, throwing data: %d", strm_state);
return;
}
int ret;
struct ble_iso_data *iso_received = NULL;
#if (CONFIG_BLE_ISO_TEST_PATTERN)
ble_test_pattern_receive(p_data, data_size, bad_frame);
return;
#endif /* (CONFIG_BLE_ISO_TEST_PATTERN) */
uint32_t blocks_alloced_num, blocks_locked_num;
ret = data_fifo_num_used_get(&ble_fifo_rx, &blocks_alloced_num, &blocks_locked_num);
ERR_CHK(ret);
if (blocks_alloced_num >= CONFIG_BUF_BLE_RX_PACKET_NUM) {
/* FIFO buffer is full, swap out oldest frame for a new one */
void *stale_data;
size_t stale_size;
LOG_WRN("BLE ISO RX overrun");
ret = data_fifo_pointer_last_filled_get(&ble_fifo_rx, &stale_data, &stale_size,
K_NO_WAIT);
ERR_CHK(ret);
ret = data_fifo_block_free(&ble_fifo_rx, &stale_data);
ERR_CHK(ret);
}
ret = data_fifo_pointer_first_vacant_get(&ble_fifo_rx, (void *)&iso_received, K_NO_WAIT);
ERR_CHK_MSG(ret, "Unable to get FIFO pointer");
if (data_size > ARRAY_SIZE(iso_received->data)) {
ERR_CHK_MSG(-ENOMEM, "Data size too large for buffer");
}
memcpy(iso_received->data, p_data, data_size);
iso_received->bad_frame = bad_frame;
iso_received->data_size = data_size;
iso_received->sdu_ref = sdu_ref;
iso_received->recv_frame_ts = recv_frame_ts;
ret = data_fifo_block_lock(&ble_fifo_rx, (void *)&iso_received,
sizeof(struct ble_iso_data));
ERR_CHK_MSG(ret, "Failed to lock block");
}
/* Thread to receive data from BLE through a k_fifo and send to audio datapath */
static void audio_datapath_thread(void *dummy1, void *dummy2, void *dummy3)
{
int ret;
struct ble_iso_data *iso_received = NULL;
size_t iso_received_size;
while (1) {
ret = data_fifo_pointer_last_filled_get(&ble_fifo_rx, (void *)&iso_received,
&iso_received_size, K_FOREVER);
ERR_CHK(ret);
#if ((CONFIG_AUDIO_DEV == GATEWAY) && (CONFIG_AUDIO_SOURCE_USB))
ret = audio_decode(iso_received->data, iso_received->data_size,
iso_received->bad_frame);
ERR_CHK(ret);
#else
audio_datapath_stream_out(iso_received->data, iso_received->data_size,
iso_received->sdu_ref, iso_received->bad_frame,
iso_received->recv_frame_ts);
#endif
ret = data_fifo_block_free(&ble_fifo_rx, (void *)&iso_received);
ERR_CHK(ret);
STACK_USAGE_PRINT("audio_datapath_thread", &audio_datapath_thread_data);
}
}
/* Function for handling all stream state changes */
static void stream_state_set(enum stream_state stream_state_new)
{
strm_state = stream_state_new;
}
uint8_t stream_state_get(void)
{
return strm_state;
}
void streamctrl_encoded_data_send(void const *const data, size_t len)
{
int ret;
static int prev_ret;
if (strm_state == STATE_STREAMING) {
ret = le_audio_send(data, len);
if (ret != 0 && ret != prev_ret) {
LOG_WRN("Problem with sending LE audio data, ret: %d", ret);
}
prev_ret = ret;
}
}
/* Handle button activity events */
static void button_evt_handler(struct button_evt event)
{
int ret;
static uint32_t test_tone_hz;
LOG_DBG("Got btn evt from queue - id = %d, action = %d", event.button_pin,
event.button_action);
if (event.button_action != BUTTON_PRESS) {
LOG_WRN("Unhandled button action");
return;
}
switch (event.button_pin) {
case BUTTON_PLAY_PAUSE:
/* Starts/pauses the audio stream */
switch (strm_state) {
case STATE_PAUSED:
LOG_INF("Playing stream");
ret = le_audio_play();
if (ret) {
LOG_WRN("Failed to start playing");
}
break;
case STATE_STREAMING:
LOG_INF("Pausing stream");
ret = le_audio_pause();
if (ret) {
LOG_WRN("Failed to pause stream");
}
break;
default:
/* We got the play/pause button in a state where we
* can not use it. This should be expected, buttons
* can be pressed at any time. For now, inform about it.
*/
LOG_DBG("Got play/pause in state %d", strm_state);
}
break;
case BUTTON_VOLUME_UP:
ret = le_audio_volume_up();
if (ret) {
LOG_WRN("Failed to increase volume");
break;
}
break;
case BUTTON_VOLUME_DOWN:
ret = le_audio_volume_down();
if (ret) {
LOG_WRN("Failed to decrease volume");
break;
}
break;
case BUTTON_MUTE:
ret = le_audio_volume_mute();
if (ret) {
LOG_WRN("Failed to mute volume");
break;
}
break;
case BUTTON_TEST_TONE:
switch (strm_state) {
case STATE_STREAMING:
if (CONFIG_AUDIO_BIT_DEPTH_BITS != 16) {
LOG_WRN("Tone gen only supports 16 bits");
break;
}
if (test_tone_hz == 0) {
test_tone_hz = TEST_TONE_BASE_FREQ_HZ;
} else if (test_tone_hz >= TEST_TONE_BASE_FREQ_HZ * 4) {
test_tone_hz = 0;
} else {
test_tone_hz = test_tone_hz * 2;
}
if (test_tone_hz != 0) {
LOG_INF("Test tone set at %d Hz", test_tone_hz);
} else {
LOG_INF("Test tone off");
}
ret = audio_encode_test_tone_set(test_tone_hz);
ERR_CHK_MSG(ret, "Failed to generate test tone");
break;
default:
LOG_WRN("Test tone can only be set in streaming mode");
break;
}
break;
default:
LOG_WRN("Unexpected/unhandled button id: %d", event.button_pin);
}
}
/* Handle Bluetooth LE audio events */
static void le_audio_evt_handler(enum le_audio_evt_type event)
{
int ret;
LOG_DBG("Received event = %d, current state = %d", event, strm_state);
switch (event) {
case LE_AUDIO_EVT_STREAMING:
LOG_DBG("LE audio evt streaming");
if (strm_state == STATE_STREAMING) {
LOG_DBG("Got streaming event in streaming state");
break;
}
audio_system_start();
stream_state_set(STATE_STREAMING);
ret = led_blink(LED_APP_1_BLUE);
ERR_CHK(ret);
break;
case LE_AUDIO_EVT_NOT_STREAMING:
LOG_DBG("LE audio evt not_streaming");
if (strm_state == STATE_PAUSED) {
LOG_DBG("Got not_streaming event in paused state");
break;
}
stream_state_set(STATE_PAUSED);
audio_system_stop();
ret = led_on(LED_APP_1_BLUE);
ERR_CHK(ret);
break;
case LE_AUDIO_EVT_CONFIG_RECEIVED:
LOG_DBG("Config received");
int bitrate;
int sampling_rate;
ret = le_audio_config_get(&bitrate, &sampling_rate);
if (ret) {
LOG_WRN("Failed to get config");
break;
}
LOG_DBG("Sampling rate: %d", sampling_rate);
LOG_DBG("Bitrate: %d", bitrate);
break;
default:
LOG_WRN("Unexpected/unhandled event: %d", event);
break;
}
}
void streamctrl_event_handler(void)
{
struct event_t my_event;
/* As long as this timeout is K_FOREVER, ctrl_events_get should
* never return unless it has an event, that is why we can ignore the
* return value
*/
(void)ctrl_events_get(&my_event, K_FOREVER);
switch (my_event.event_source) {
case EVT_SRC_BUTTON:
button_evt_handler(my_event.button_activity);
break;
case EVT_SRC_LE_AUDIO:
le_audio_evt_handler(my_event.le_audio_activity.le_audio_evt_type);
break;
default:
LOG_WRN("Unhandled event from queue - source = %d", my_event.event_source);
}
}
int streamctrl_start(void)
{
int ret;
ret = audio_datapath_tone_play(440, 500, 0.2);
ERR_CHK(ret);
ret = data_fifo_init(&ble_fifo_rx);
ERR_CHK_MSG(ret, "Failed to set up ble_rx FIFO");
audio_datapath_thread_id =
k_thread_create(&audio_datapath_thread_data, audio_datapath_thread_stack,
CONFIG_AUDIO_DATAPATH_STACK_SIZE,
(k_thread_entry_t)audio_datapath_thread, NULL, NULL, NULL,
K_PRIO_PREEMPT(CONFIG_AUDIO_DATAPATH_THREAD_PRIO), 0, K_NO_WAIT);
ret = k_thread_name_set(audio_datapath_thread_id, "AUDIO DATAPATH");
ERR_CHK(ret);
ret = le_audio_enable(le_audio_rx_data_handler);
ERR_CHK_MSG(ret, "Failed to enable LE Audio");
return 0;
}