Bluetooth: BAP: Shell: Add USB out support for the BAP shell
Add USB out support for the BAP shell, so that decoded LC3 data can be sent to the host (e.g. a PC). Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
55ea78f30b
commit
65e787be58
|
@ -81,6 +81,9 @@ zephyr_library_sources_ifdef(
|
|||
CONFIG_BT_BAP_STREAM
|
||||
bap.c
|
||||
)
|
||||
if (CONFIG_BT_AUDIO_RX AND CONFIG_LIBLC3 AND CONFIG_USB_DEVICE_AUDIO)
|
||||
zephyr_library_sources(bap_usb.c)
|
||||
endif()
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_BT_BAP_SCAN_DELEGATOR
|
||||
bap_scan_delegator.c
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "shell/bt.h"
|
||||
|
||||
#define SHELL_PRINT_INDENT_LEVEL_SIZE 2
|
||||
#define MAX_CODEC_FRAMES_PER_SDU 4U
|
||||
|
||||
extern struct bt_csip_set_member_svc_inst *svc_inst;
|
||||
|
||||
|
@ -47,8 +48,17 @@ ssize_t cap_initiator_pa_data_add(struct bt_data *data_array, const size_t data_
|
|||
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
unsigned long bap_get_recv_stats_interval(void);
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
#include "lc3.h"
|
||||
|
||||
#define USB_SAMPLE_RATE 48000U
|
||||
#define LC3_MAX_SAMPLE_RATE 48000U
|
||||
#define LC3_MAX_FRAME_DURATION_US 10000U
|
||||
#define LC3_MAX_NUM_SAMPLES_MONO ((LC3_MAX_FRAME_DURATION_US * LC3_MAX_SAMPLE_RATE) / \
|
||||
USEC_PER_SEC)
|
||||
#define LC3_MAX_NUM_SAMPLES_STEREO (LC3_MAX_NUM_SAMPLES_MONO * 2U)
|
||||
#endif /* CONFIG_LIBLC3 */
|
||||
|
||||
#define LOCATION BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT
|
||||
|
@ -68,6 +78,12 @@ struct named_lc3_preset {
|
|||
const struct named_lc3_preset *bap_get_named_preset(bool is_unicast, enum bt_audio_dir dir,
|
||||
const char *preset_arg);
|
||||
|
||||
size_t bap_get_rx_streaming_cnt(void);
|
||||
int bap_usb_init(void);
|
||||
int bap_usb_add_frame_to_usb(enum bt_audio_location lc3_chan_allocation, const int16_t *frame,
|
||||
size_t frame_size, uint32_t ts);
|
||||
void bap_usb_clear_frames_to_usb(void);
|
||||
|
||||
struct shell_stream {
|
||||
struct bt_cap_stream stream;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
|
@ -91,7 +107,7 @@ struct shell_stream {
|
|||
int64_t connected_at_ticks;
|
||||
uint16_t seq_num;
|
||||
struct k_work_delayable audio_send_work;
|
||||
bool tx_active;
|
||||
bool active;
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
atomic_t lc3_enqueue_cnt;
|
||||
size_t lc3_sdu_cnt;
|
||||
|
@ -183,6 +199,22 @@ int cap_ac_unicast(const struct shell *sh, const struct bap_unicast_ac_param *pa
|
|||
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
|
||||
#endif /* CONFIG_BT_BAP_UNICAST */
|
||||
|
||||
static inline uint8_t get_chan_cnt(enum bt_audio_location chan_allocation)
|
||||
{
|
||||
uint8_t cnt = 0U;
|
||||
|
||||
if (chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (chan_allocation != 0) {
|
||||
cnt += chan_allocation & 1U;
|
||||
chan_allocation >>= 1;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static inline void print_qos(const struct shell *sh, const struct bt_audio_codec_qos *qos)
|
||||
{
|
||||
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) || defined(CONFIG_BT_BAP_UNICAST)
|
||||
|
|
|
@ -36,10 +36,6 @@
|
|||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
|
||||
#define LC3_MAX_SAMPLE_RATE 48000
|
||||
#define LC3_MAX_FRAME_DURATION_US 10000
|
||||
#define LC3_MAX_NUM_SAMPLES ((LC3_MAX_FRAME_DURATION_US * LC3_MAX_SAMPLE_RATE) / USEC_PER_SEC)
|
||||
|
||||
static void clear_lc3_sine_data(struct bt_bap_stream *bap_stream);
|
||||
static void lc3_decoder_stream_clear(struct shell_stream *sh_stream);
|
||||
|
||||
|
@ -224,7 +220,7 @@ NET_BUF_POOL_FIXED_DEFINE(sine_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, SINE_TX_POOL
|
|||
#define AUDIO_VOLUME (INT16_MAX - 3000) /* codec does clipping above INT16_MAX - 3000 */
|
||||
#define AUDIO_TONE_FREQUENCY_HZ 400
|
||||
|
||||
static int16_t lc3_tx_buf[LC3_MAX_NUM_SAMPLES];
|
||||
static int16_t lc3_tx_buf[LC3_MAX_NUM_SAMPLES_MONO];
|
||||
static lc3_encoder_t lc3_encoder;
|
||||
static lc3_encoder_mem_48k_t lc3_encoder_mem;
|
||||
static int lc3_encoder_freq_hz;
|
||||
|
@ -235,7 +231,6 @@ static void clear_lc3_sine_data(struct bt_bap_stream *bap_stream)
|
|||
struct shell_stream *sh_stream = shell_stream_from_bap_stream(bap_stream);
|
||||
|
||||
if (sh_stream->is_tx) {
|
||||
sh_stream->tx.tx_active = false;
|
||||
(void)k_work_cancel_delayable(&sh_stream->tx.audio_send_work);
|
||||
}
|
||||
}
|
||||
|
@ -314,7 +309,7 @@ static void lc3_audio_send_data(struct k_work *work)
|
|||
off_t offset = 0;
|
||||
int err;
|
||||
|
||||
if (!sh_stream->is_tx || !sh_stream->tx.tx_active) {
|
||||
if (!sh_stream->is_tx || !sh_stream->tx.active) {
|
||||
/* TX has been aborted */
|
||||
return;
|
||||
}
|
||||
|
@ -415,7 +410,7 @@ static void lc3_sent_cb(struct bt_bap_stream *bap_stream)
|
|||
|
||||
atomic_inc(&sh_stream->tx.lc3_enqueue_cnt);
|
||||
|
||||
if (!sh_stream->tx.tx_active) {
|
||||
if (!sh_stream->tx.active) {
|
||||
/* TX has been aborted */
|
||||
return;
|
||||
}
|
||||
|
@ -452,9 +447,9 @@ const struct named_lc3_preset *bap_get_named_preset(bool is_unicast, enum bt_aud
|
|||
}
|
||||
|
||||
#if defined(CONFIG_BT_PACS)
|
||||
static const struct bt_audio_codec_cap lc3_codec_cap =
|
||||
BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY,
|
||||
BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2), 30, 240, 2, CONTEXT);
|
||||
static const struct bt_audio_codec_cap lc3_codec_cap = BT_AUDIO_CODEC_CAP_LC3(
|
||||
BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY,
|
||||
BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2), 30, 240, MAX_CODEC_FRAMES_PER_SDU, CONTEXT);
|
||||
|
||||
#if defined(CONFIG_BT_PAC_SNK)
|
||||
static struct bt_pacs_cap cap_sink = {
|
||||
|
@ -2364,21 +2359,36 @@ static struct bt_le_scan_cb bap_scan_cb = {
|
|||
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */
|
||||
|
||||
#if defined(CONFIG_BT_AUDIO_RX)
|
||||
static unsigned long recv_stats_interval = 100U;
|
||||
static unsigned long recv_stats_interval = 1000U;
|
||||
static size_t rx_streaming_cnt;
|
||||
|
||||
size_t bap_get_rx_streaming_cnt(void)
|
||||
{
|
||||
return rx_streaming_cnt;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
struct lc3_data {
|
||||
void *fifo_reserved; /* 1st word reserved for use by FIFO */
|
||||
struct net_buf *buf;
|
||||
struct shell_stream *sh_stream;
|
||||
uint32_t ts;
|
||||
bool do_plc;
|
||||
};
|
||||
|
||||
K_MEM_SLAB_DEFINE(lc3_data_slab, sizeof(struct lc3_data), CONFIG_BT_ISO_RX_BUF_COUNT,
|
||||
__alignof__(struct lc3_data));
|
||||
|
||||
static int16_t lc3_rx_buf[LC3_MAX_NUM_SAMPLES];
|
||||
static int16_t lc3_rx_buf[LC3_MAX_NUM_SAMPLES_MONO];
|
||||
static K_FIFO_DEFINE(lc3_in_fifo);
|
||||
|
||||
/* We only want to send USB to left/right from a single stream. If we have 2 left streams, the
|
||||
* outgoing audio is going to be terrible.
|
||||
* Since a stream can contain stereo data, both of these may be the same stream.
|
||||
*/
|
||||
static struct shell_stream *usb_left_stream;
|
||||
static struct shell_stream *usb_right_stream;
|
||||
|
||||
static int init_lc3_decoder(struct shell_stream *sh_stream)
|
||||
{
|
||||
if (sh_stream == NULL) {
|
||||
|
@ -2407,9 +2417,10 @@ static int init_lc3_decoder(struct shell_stream *sh_stream)
|
|||
"Initializing the LC3 decoder with %u us duration and %u Hz frequency",
|
||||
sh_stream->lc3_frame_duration_us, sh_stream->lc3_freq_hz);
|
||||
/* Create the decoder instance. This shall complete before stream_started() is called. */
|
||||
sh_stream->rx.lc3_decoder = lc3_setup_decoder(sh_stream->lc3_frame_duration_us,
|
||||
sh_stream->lc3_freq_hz, 0, /* No resampling */
|
||||
&sh_stream->rx.lc3_decoder_mem);
|
||||
sh_stream->rx.lc3_decoder =
|
||||
lc3_setup_decoder(sh_stream->lc3_frame_duration_us, sh_stream->lc3_freq_hz,
|
||||
IS_ENABLED(CONFIG_USB_DEVICE_AUDIO) ? USB_SAMPLE_RATE : 0,
|
||||
&sh_stream->rx.lc3_decoder_mem);
|
||||
if (sh_stream->rx.lc3_decoder == NULL) {
|
||||
shell_error(ctx_shell, "Failed to setup LC3 decoder - wrong parameters?\n");
|
||||
return -EINVAL;
|
||||
|
@ -2425,21 +2436,23 @@ static void lc3_decoder_stream_clear(struct shell_stream *sh_stream)
|
|||
}
|
||||
}
|
||||
|
||||
static bool decode_frame(struct net_buf *buf, struct shell_stream *sh_stream, size_t frame_cnt)
|
||||
static bool decode_frame(struct lc3_data *data, size_t frame_cnt)
|
||||
{
|
||||
const struct shell_stream *sh_stream = data->sh_stream;
|
||||
const size_t total_frames = sh_stream->lc3_chan_cnt * sh_stream->lc3_frame_blocks_per_sdu;
|
||||
const uint16_t octets_per_frame = sh_stream->lc3_octets_per_frame;
|
||||
struct net_buf *buf = data->buf;
|
||||
void *iso_data;
|
||||
int err;
|
||||
|
||||
if (buf->len == 0U) {
|
||||
if (data->do_plc) {
|
||||
iso_data = NULL; /* perform PLC */
|
||||
|
||||
if ((sh_stream->rx.decoded_cnt % recv_stats_interval) == 0) {
|
||||
shell_print(ctx_shell, "[%zu]: Performing PLC", sh_stream->rx.decoded_cnt);
|
||||
}
|
||||
} else {
|
||||
iso_data = net_buf_pull_mem(buf, octets_per_frame);
|
||||
iso_data = net_buf_pull_mem(data->buf, octets_per_frame);
|
||||
|
||||
if ((sh_stream->rx.decoded_cnt % recv_stats_interval) == 0) {
|
||||
shell_print(ctx_shell, "[%zu]: Decoding frame of size %u (%u/%u)",
|
||||
|
@ -2459,35 +2472,96 @@ static bool decode_frame(struct net_buf *buf, struct shell_stream *sh_stream, si
|
|||
return true;
|
||||
}
|
||||
|
||||
static size_t decode_frame_block(struct net_buf *buf, struct shell_stream *sh_stream,
|
||||
size_t frame_cnt)
|
||||
static int get_chan_alloc_from_index(const struct shell_stream *sh_stream, uint8_t index,
|
||||
enum bt_audio_location *chan_alloc)
|
||||
{
|
||||
const bool has_left = (sh_stream->lc3_chan_allocation & BT_AUDIO_LOCATION_FRONT_LEFT) != 0;
|
||||
const bool has_right =
|
||||
(sh_stream->lc3_chan_allocation & BT_AUDIO_LOCATION_FRONT_RIGHT) != 0;
|
||||
const bool is_mono = sh_stream->lc3_chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
const bool is_left = index == 0 && has_left;
|
||||
const bool is_right = has_right && (index == 0U || (index == 1U && has_left));
|
||||
|
||||
/* LC3 is always Left before Right, so we can use the index and the stream channel
|
||||
* allocation to determine if index 0 is left or right.
|
||||
*/
|
||||
if (is_left) {
|
||||
*chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT;
|
||||
} else if (is_right) {
|
||||
*chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT;
|
||||
} else if (is_mono) {
|
||||
*chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
} else {
|
||||
/* Not suitable for USB */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_frame_block(struct lc3_data *data, size_t frame_cnt)
|
||||
{
|
||||
const struct shell_stream *sh_stream = data->sh_stream;
|
||||
const uint8_t chan_cnt = sh_stream->lc3_chan_cnt;
|
||||
size_t decoded_frames = 0U;
|
||||
|
||||
for (uint8_t j = 0U; j < chan_cnt; j++) {
|
||||
for (uint8_t i = 0U; i < chan_cnt; i++) {
|
||||
/* We provide the total number of decoded frames to `decode_frame` for logging
|
||||
* purposes
|
||||
*/
|
||||
if (!decode_frame(buf, sh_stream, frame_cnt + decoded_frames)) {
|
||||
if (decode_frame(data, frame_cnt + decoded_frames)) {
|
||||
decoded_frames++;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
enum bt_audio_location chan_alloc;
|
||||
int err;
|
||||
|
||||
err = get_chan_alloc_from_index(sh_stream, i, &chan_alloc);
|
||||
if (err != 0) {
|
||||
/* Not suitable for USB */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We only want to left or right from one stream to USB */
|
||||
if ((chan_alloc == BT_AUDIO_LOCATION_FRONT_LEFT &&
|
||||
sh_stream != usb_left_stream) ||
|
||||
(chan_alloc == BT_AUDIO_LOCATION_FRONT_RIGHT &&
|
||||
sh_stream != usb_right_stream)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
err = bap_usb_add_frame_to_usb(chan_alloc, lc3_rx_buf,
|
||||
sizeof(lc3_rx_buf), data->ts);
|
||||
if (err == -EINVAL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If decoding failed, we clear the data to USB as it would contain
|
||||
* invalid data
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
bap_usb_clear_frames_to_usb();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
decoded_frames++;
|
||||
}
|
||||
|
||||
return decoded_frames;
|
||||
}
|
||||
|
||||
static void do_lc3_decode(struct shell_stream *sh_stream, struct net_buf *buf)
|
||||
static void do_lc3_decode(struct lc3_data *data)
|
||||
{
|
||||
struct shell_stream *sh_stream = data->sh_stream;
|
||||
|
||||
if (sh_stream->is_rx && sh_stream->rx.lc3_decoder != NULL) {
|
||||
const uint8_t frame_blocks_per_sdu = sh_stream->lc3_frame_blocks_per_sdu;
|
||||
size_t frame_cnt;
|
||||
|
||||
frame_cnt = 0;
|
||||
for (uint8_t i = 0U; i < frame_blocks_per_sdu; i++) {
|
||||
const size_t decoded_frames = decode_frame_block(buf, sh_stream, frame_cnt);
|
||||
const size_t decoded_frames = decode_frame_block(data, frame_cnt);
|
||||
|
||||
if (decoded_frames == 0) {
|
||||
break;
|
||||
|
@ -2499,7 +2573,7 @@ static void do_lc3_decode(struct shell_stream *sh_stream, struct net_buf *buf)
|
|||
sh_stream->rx.decoded_cnt++;
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
net_buf_unref(data->buf);
|
||||
}
|
||||
|
||||
static void lc3_decoder_thread_func(void *arg1, void *arg2, void *arg3)
|
||||
|
@ -2514,7 +2588,7 @@ static void lc3_decoder_thread_func(void *arg1, void *arg2, void *arg3)
|
|||
continue; /* Wait for new data */
|
||||
}
|
||||
|
||||
do_lc3_decode(data->sh_stream, data->buf);
|
||||
do_lc3_decode(data);
|
||||
|
||||
k_mem_slab_free(&lc3_data_slab, (void *)data);
|
||||
}
|
||||
|
@ -2522,6 +2596,11 @@ static void lc3_decoder_thread_func(void *arg1, void *arg2, void *arg3)
|
|||
|
||||
#endif /* CONFIG_LIBLC3*/
|
||||
|
||||
unsigned long bap_get_recv_stats_interval(void)
|
||||
{
|
||||
return recv_stats_interval;
|
||||
}
|
||||
|
||||
static void audio_recv(struct bt_bap_stream *stream,
|
||||
const struct bt_iso_recv_info *info,
|
||||
struct net_buf *buf)
|
||||
|
@ -2585,7 +2664,7 @@ static void audio_recv(struct bt_bap_stream *stream,
|
|||
}
|
||||
|
||||
if ((info->flags & BT_ISO_FLAGS_VALID) == 0) {
|
||||
buf->len = 0U; /* Set length to 0 to mark it as invalid for PLC */
|
||||
data->do_plc = true;
|
||||
} else if (buf->len != (octets_per_frame * chan_cnt * frame_blocks_per_sdu)) {
|
||||
if (buf->len != 0U) {
|
||||
shell_error(
|
||||
|
@ -2593,12 +2672,18 @@ static void audio_recv(struct bt_bap_stream *stream,
|
|||
"Expected %u frame blocks with %u channels of size %u, but "
|
||||
"length is %u",
|
||||
frame_blocks_per_sdu, chan_cnt, octets_per_frame, buf->len);
|
||||
buf->len = 0U; /* Set length to 0 to mark it as invalid for PLC */
|
||||
}
|
||||
|
||||
data->do_plc = true;
|
||||
}
|
||||
|
||||
data->buf = net_buf_ref(buf);
|
||||
data->sh_stream = sh_stream;
|
||||
if (info->flags & BT_ISO_FLAGS_TS) {
|
||||
data->ts = info->ts;
|
||||
} else {
|
||||
data->ts = 0U;
|
||||
}
|
||||
|
||||
k_fifo_put(&lc3_in_fifo, data);
|
||||
}
|
||||
|
@ -2645,24 +2730,6 @@ static void stream_enabled_cb(struct bt_bap_stream *stream)
|
|||
}
|
||||
#endif /* CONFIG_BT_BAP_UNICAST */
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
static uint8_t get_chan_cnt(enum bt_audio_location chan_allocation)
|
||||
{
|
||||
uint8_t cnt = 0U;
|
||||
|
||||
if (chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (chan_allocation != 0) {
|
||||
cnt += chan_allocation & 1U;
|
||||
chan_allocation >>= 1;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
#endif /* CONFIG_LIBLC3 */
|
||||
|
||||
static void stream_started_cb(struct bt_bap_stream *bap_stream)
|
||||
{
|
||||
struct shell_stream *sh_stream = shell_stream_from_bap_stream(bap_stream);
|
||||
|
@ -2765,6 +2832,34 @@ static void stream_started_cb(struct bt_bap_stream *bap_stream)
|
|||
}
|
||||
|
||||
sh_stream->rx.decoded_cnt = 0U;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
if ((sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_LEFT) != 0) {
|
||||
if (usb_left_stream == NULL) {
|
||||
shell_info(ctx_shell,
|
||||
"Setting USB left stream to %p",
|
||||
sh_stream);
|
||||
usb_left_stream = sh_stream;
|
||||
} else {
|
||||
shell_warn(ctx_shell,
|
||||
"Multiple left streams started");
|
||||
}
|
||||
}
|
||||
|
||||
if ((sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_RIGHT) != 0) {
|
||||
if (usb_right_stream == NULL) {
|
||||
shell_info(ctx_shell,
|
||||
"Setting USB right stream to %p",
|
||||
sh_stream);
|
||||
usb_right_stream = sh_stream;
|
||||
} else {
|
||||
shell_warn(ctx_shell,
|
||||
"Multiple right streams started");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_AUDIO_RX */
|
||||
}
|
||||
|
@ -2784,12 +2879,101 @@ static void stream_started_cb(struct bt_bap_stream *bap_stream)
|
|||
sh_stream->rx.dup_psn = 0U;
|
||||
sh_stream->rx.rx_cnt = 0U;
|
||||
sh_stream->rx.dup_ts = 0U;
|
||||
|
||||
rx_streaming_cnt++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
static void update_usb_streams(struct shell_stream *sh_stream)
|
||||
{
|
||||
/* If the @p sh_stream is the left or right USB stream, we look through other streams to see
|
||||
* if any of them can be assigned as the USB stream(s)
|
||||
*/
|
||||
if (usb_left_stream == sh_stream) {
|
||||
shell_info(ctx_shell, "Clearing USB left stream (%p)", usb_left_stream);
|
||||
usb_left_stream = NULL;
|
||||
|
||||
#if defined(CONFIG_BT_BAP_UNICAST)
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
|
||||
struct shell_stream *tmp_sh_stream = &unicast_streams[i];
|
||||
|
||||
if (usb_left_stream != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp_sh_stream->is_rx && (tmp_sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_LEFT) != 0) {
|
||||
usb_left_stream = tmp_sh_stream;
|
||||
shell_info(ctx_shell, "Setting new USB left stream to %p",
|
||||
tmp_sh_stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_UNICAST */
|
||||
|
||||
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
|
||||
struct shell_stream *tmp_sh_stream = &unicast_streams[i];
|
||||
|
||||
if (usb_left_stream != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp_sh_stream->is_rx && (tmp_sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_LEFT) != 0) {
|
||||
usb_left_stream = tmp_sh_stream;
|
||||
shell_info(ctx_shell, "Setting new USB right stream to %p",
|
||||
tmp_sh_stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
|
||||
}
|
||||
|
||||
if (usb_right_stream == sh_stream) {
|
||||
shell_info(ctx_shell, "Clearing USB right stream (%p)", usb_right_stream);
|
||||
usb_right_stream = NULL;
|
||||
|
||||
#if defined(CONFIG_BT_BAP_UNICAST)
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) {
|
||||
struct shell_stream *tmp_sh_stream = &unicast_streams[i];
|
||||
|
||||
if (usb_right_stream != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp_sh_stream->is_rx && (tmp_sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_RIGHT) != 0) {
|
||||
usb_right_stream = tmp_sh_stream;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_UNICAST */
|
||||
|
||||
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
|
||||
struct shell_stream *tmp_sh_stream = &unicast_streams[i];
|
||||
|
||||
if (usb_right_stream != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp_sh_stream->is_rx && (tmp_sh_stream->lc3_chan_allocation &
|
||||
BT_AUDIO_LOCATION_FRONT_RIGHT) != 0) {
|
||||
usb_right_stream = tmp_sh_stream;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_LIBLC3 */
|
||||
|
||||
static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
||||
{
|
||||
struct shell_stream *sh_stream = shell_stream_from_bap_stream(stream);
|
||||
|
||||
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
|
@ -2797,7 +2981,6 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
|||
#endif /* CONFIG_LIBLC3 */
|
||||
|
||||
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
|
||||
struct shell_stream *sh_stream = shell_stream_from_bap_stream(stream);
|
||||
|
||||
if (IS_ARRAY_ELEMENT(broadcast_sink_streams, sh_stream)) {
|
||||
if (default_broadcast_sink.stream_cnt != 0) {
|
||||
|
@ -2813,6 +2996,25 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
|||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */
|
||||
|
||||
#if defined(CONFIG_BT_AUDIO_RX)
|
||||
if (sh_stream->is_rx) {
|
||||
rx_streaming_cnt--;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_BT_AUDIO_TX)
|
||||
if (sh_stream->is_tx) {
|
||||
sh_stream->tx.active = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
sh_stream->is_rx = sh_stream->is_tx = false;
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
update_usb_streams(sh_stream);
|
||||
}
|
||||
#endif /* CONFIG_LIBLC3 */
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_BAP_UNICAST)
|
||||
|
@ -2871,6 +3073,12 @@ static void stream_released_cb(struct bt_bap_stream *stream)
|
|||
|
||||
sh_stream->is_tx = false;
|
||||
sh_stream->is_rx = false;
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
update_usb_streams(sh_stream);
|
||||
}
|
||||
#endif /* CONFIG_LIBLC3 */
|
||||
}
|
||||
#endif /* CONFIG_BT_BAP_UNICAST */
|
||||
|
||||
|
@ -3449,14 +3657,19 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[])
|
|||
|
||||
#if defined(CONFIG_LIBLC3) && defined(CONFIG_BT_AUDIO_RX)
|
||||
static K_KERNEL_STACK_DEFINE(lc3_decoder_thread_stack, 4096);
|
||||
/* make it slightly lower priority than the RX thread */
|
||||
int lc3_decoder_thread_prio = K_PRIO_COOP(CONFIG_BT_RX_PRIO + 1);
|
||||
int lc3_decoder_thread_prio = K_PRIO_PREEMPT(5);
|
||||
|
||||
static struct k_thread lc3_decoder_thread;
|
||||
|
||||
k_thread_create(&lc3_decoder_thread, lc3_decoder_thread_stack,
|
||||
K_KERNEL_STACK_SIZEOF(lc3_decoder_thread_stack), lc3_decoder_thread_func,
|
||||
NULL, NULL, NULL, lc3_decoder_thread_prio, 0, K_NO_WAIT);
|
||||
k_thread_name_set(&lc3_decoder_thread, "LC3 Decode");
|
||||
k_thread_name_set(&lc3_decoder_thread, "LC3 Decoder");
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) {
|
||||
err = bap_usb_init();
|
||||
__ASSERT(err == 0, "Failed to enable USB: %d", err);
|
||||
}
|
||||
#endif /* CONFIG_LIBLC3 && CONFIG_BT_AUDIO_RX */
|
||||
|
||||
initialized = true;
|
||||
|
@ -3566,7 +3779,7 @@ static int stream_start_sine(struct shell_stream *sh_stream)
|
|||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
sh_stream->tx.tx_active = true;
|
||||
sh_stream->tx.active = true;
|
||||
sh_stream->tx.seq_num = get_next_seq_num(bap_stream_from_shell_stream(sh_stream));
|
||||
|
||||
return 0;
|
||||
|
@ -3694,7 +3907,7 @@ static int cmd_stop_sine(const struct shell *sh, size_t argc, char *argv[])
|
|||
struct bt_bap_stream *bap_stream =
|
||||
bap_stream_from_shell_stream(&unicast_streams[i]);
|
||||
|
||||
if (unicast_streams[i].is_tx && unicast_streams[i].tx.tx_active) {
|
||||
if (unicast_streams[i].is_tx && unicast_streams[i].tx.active) {
|
||||
clear_lc3_sine_data(bap_stream);
|
||||
shell_print(sh, "Stopped transmitting on stream %p", bap_stream);
|
||||
}
|
||||
|
@ -3703,7 +3916,7 @@ static int cmd_stop_sine(const struct shell *sh, size_t argc, char *argv[])
|
|||
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) {
|
||||
struct bt_bap_stream *bap_stream =
|
||||
bap_stream_from_shell_stream(&broadcast_source_streams[i]);
|
||||
if (unicast_streams[i].is_tx && unicast_streams[i].tx.tx_active) {
|
||||
if (unicast_streams[i].is_tx && unicast_streams[i].tx.active) {
|
||||
clear_lc3_sine_data(bap_stream);
|
||||
shell_print(sh, "Stopped transmitting on stream %p", bap_stream);
|
||||
}
|
||||
|
@ -3711,7 +3924,7 @@ static int cmd_stop_sine(const struct shell *sh, size_t argc, char *argv[])
|
|||
} else {
|
||||
struct shell_stream *sh_stream = shell_stream_from_bap_stream(default_stream);
|
||||
|
||||
if (sh_stream->is_tx && sh_stream->tx.tx_active) {
|
||||
if (sh_stream->is_tx && sh_stream->tx.active) {
|
||||
clear_lc3_sine_data(default_stream);
|
||||
shell_print(sh, "Stopped transmitting on stream %p", default_stream);
|
||||
}
|
||||
|
|
339
subsys/bluetooth/audio/shell/bap_usb.c
Normal file
339
subsys/bluetooth/audio/shell/bap_usb.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
/**
|
||||
* @file
|
||||
* @brief Bluetooth Basic Audio Profile shell USB extension
|
||||
*
|
||||
* This files handles all the USB related functionality to audio in/out for the BAP shell
|
||||
*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/usb/class/usb_audio.h>
|
||||
|
||||
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
|
||||
#include <nrfx_clock.h>
|
||||
#endif /* CONFIG_SOC_NRF5340_CPUAPP */
|
||||
|
||||
#include "shell/bt.h"
|
||||
#include "audio.h"
|
||||
|
||||
LOG_MODULE_REGISTER(bap_usb, CONFIG_BT_BAP_STREAM_LOG_LEVEL);
|
||||
|
||||
#define USB_ENQUEUE_COUNT 30U /* 30ms */
|
||||
#define USB_FRAME_DURATION_US 1000U
|
||||
#define USB_MONO_SAMPLE_SIZE \
|
||||
((USB_FRAME_DURATION_US * USB_SAMPLE_RATE * sizeof(int16_t)) / USEC_PER_SEC)
|
||||
#define USB_STEREO_SAMPLE_SIZE (USB_MONO_SAMPLE_SIZE * 2U)
|
||||
#define USB_RING_BUF_SIZE (CONFIG_BT_ISO_RX_BUF_COUNT * LC3_MAX_NUM_SAMPLES_STEREO)
|
||||
|
||||
struct decoded_sdu {
|
||||
int16_t right_frames[MAX_CODEC_FRAMES_PER_SDU][LC3_MAX_NUM_SAMPLES_MONO];
|
||||
int16_t left_frames[MAX_CODEC_FRAMES_PER_SDU][LC3_MAX_NUM_SAMPLES_MONO];
|
||||
size_t right_frames_cnt;
|
||||
size_t left_frames_cnt;
|
||||
size_t mono_frames_cnt;
|
||||
uint32_t ts;
|
||||
} decoded_sdu;
|
||||
|
||||
RING_BUF_DECLARE(usb_out_ring_buf, USB_RING_BUF_SIZE);
|
||||
NET_BUF_POOL_DEFINE(usb_tx_buf_pool, USB_ENQUEUE_COUNT, USB_STEREO_SAMPLE_SIZE, 0, net_buf_destroy);
|
||||
|
||||
/* USB consumer callback, called every 1ms, consumes data from ring-buffer */
|
||||
static void usb_data_request_cb(const struct device *dev)
|
||||
{
|
||||
uint8_t usb_audio_data[USB_STEREO_SAMPLE_SIZE] = {0};
|
||||
struct net_buf *pcm_buf;
|
||||
uint32_t size;
|
||||
int err;
|
||||
|
||||
if (bap_get_rx_streaming_cnt() == 0) {
|
||||
/* no-op as we have no streams that receive data */
|
||||
return;
|
||||
}
|
||||
|
||||
pcm_buf = net_buf_alloc(&usb_tx_buf_pool, K_NO_WAIT);
|
||||
if (pcm_buf == NULL) {
|
||||
LOG_WRN("Could not allocate pcm_buf");
|
||||
return;
|
||||
}
|
||||
|
||||
/* This may fail without causing issues since usb_audio_data is 0-initialized */
|
||||
size = ring_buf_get(&usb_out_ring_buf, usb_audio_data, sizeof(usb_audio_data));
|
||||
|
||||
net_buf_add_mem(pcm_buf, usb_audio_data, sizeof(usb_audio_data));
|
||||
|
||||
if (size != 0) {
|
||||
static size_t cnt;
|
||||
|
||||
if ((++cnt % bap_get_recv_stats_interval()) == 0U) {
|
||||
LOG_INF("[%zu]: Sending USB audio", cnt);
|
||||
}
|
||||
} else {
|
||||
static size_t cnt;
|
||||
|
||||
if ((++cnt % bap_get_recv_stats_interval()) == 0U) {
|
||||
LOG_INF("[%zu]: Sending empty USB audio", cnt);
|
||||
}
|
||||
}
|
||||
|
||||
err = usb_audio_send(dev, pcm_buf, sizeof(usb_audio_data));
|
||||
if (err != 0) {
|
||||
static size_t cnt;
|
||||
|
||||
cnt++;
|
||||
if ((cnt % 1000) == 0) {
|
||||
LOG_ERR("Failed to send USB audio: %d (%zu)", err, cnt);
|
||||
}
|
||||
|
||||
net_buf_unref(pcm_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_data_written_cb(const struct device *dev, struct net_buf *buf, size_t size)
|
||||
{
|
||||
/* Unreference the buffer now that the USB is done with it */
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
|
||||
static void bap_usb_send_frames_to_usb(void)
|
||||
{
|
||||
const bool is_left_only =
|
||||
decoded_sdu.right_frames_cnt == 0U && decoded_sdu.mono_frames_cnt == 0U;
|
||||
const bool is_right_only =
|
||||
decoded_sdu.left_frames_cnt == 0U && decoded_sdu.mono_frames_cnt == 0U;
|
||||
const bool is_mono_only =
|
||||
decoded_sdu.left_frames_cnt == 0U && decoded_sdu.right_frames_cnt == 0U;
|
||||
const bool is_single_channel = is_left_only || is_right_only || is_mono_only;
|
||||
const size_t frame_cnt =
|
||||
MAX(decoded_sdu.mono_frames_cnt,
|
||||
MAX(decoded_sdu.left_frames_cnt, decoded_sdu.right_frames_cnt));
|
||||
static size_t cnt;
|
||||
|
||||
/* Send frames to USB - If we only have a single channel we mix it to stereo */
|
||||
for (size_t i = 0U; i < frame_cnt; i++) {
|
||||
static int16_t stereo_frame[LC3_MAX_NUM_SAMPLES_STEREO];
|
||||
const int16_t *right_frame = decoded_sdu.right_frames[i];
|
||||
const int16_t *left_frame = decoded_sdu.left_frames[i];
|
||||
const int16_t *mono_frame = decoded_sdu.left_frames[i]; /* use left as mono */
|
||||
static size_t fail_cnt;
|
||||
uint32_t rb_size;
|
||||
|
||||
/* Not enough space to store data */
|
||||
if (ring_buf_space_get(&usb_out_ring_buf) < sizeof(stereo_frame)) {
|
||||
if ((fail_cnt % bap_get_recv_stats_interval()) == 0U) {
|
||||
LOG_WRN("[%zu] Could not send more than %zu frames to USB",
|
||||
fail_cnt, i);
|
||||
}
|
||||
|
||||
fail_cnt++;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
fail_cnt = 0U;
|
||||
|
||||
/* Generate the stereo frame
|
||||
*
|
||||
* If we only have single channel then we mix that to stereo
|
||||
*/
|
||||
for (int j = 0; j < LC3_MAX_NUM_SAMPLES_MONO; j++) {
|
||||
if (is_single_channel) {
|
||||
int16_t sample = 0;
|
||||
|
||||
/* Mix to stereo as LRLRLRLR */
|
||||
if (is_left_only) {
|
||||
sample = left_frame[j];
|
||||
} else if (is_right_only) {
|
||||
sample = right_frame[j];
|
||||
} else if (is_mono_only) {
|
||||
sample = mono_frame[j];
|
||||
}
|
||||
|
||||
stereo_frame[j * 2] = sample;
|
||||
stereo_frame[j * 2 + 1] = sample;
|
||||
} else {
|
||||
stereo_frame[j * 2] = left_frame[j];
|
||||
stereo_frame[j * 2 + 1] = right_frame[j];
|
||||
}
|
||||
}
|
||||
|
||||
rb_size = ring_buf_put(&usb_out_ring_buf, (uint8_t *)stereo_frame,
|
||||
sizeof(stereo_frame));
|
||||
if (rb_size != sizeof(stereo_frame)) {
|
||||
LOG_WRN("Failed to put frame on USB ring buf");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((++cnt % bap_get_recv_stats_interval()) == 0U) {
|
||||
LOG_INF("[%zu]: Sending %u USB audio frame", cnt, frame_cnt);
|
||||
}
|
||||
|
||||
bap_usb_clear_frames_to_usb();
|
||||
}
|
||||
|
||||
static bool ts_overflowed(uint32_t ts)
|
||||
{
|
||||
/* If the timestamp is a factor of 10 in difference, then we assume that TS overflowed
|
||||
* We cannot simply check if `ts < decoded_sdu.ts` as that could also indicate old data
|
||||
*/
|
||||
return ((uint64_t)ts * 10 < decoded_sdu.ts);
|
||||
}
|
||||
|
||||
int bap_usb_add_frame_to_usb(enum bt_audio_location chan_allocation, const int16_t *frame,
|
||||
size_t frame_size, uint32_t ts)
|
||||
{
|
||||
const bool is_left = (chan_allocation & BT_AUDIO_LOCATION_FRONT_LEFT) != 0;
|
||||
const bool is_right = (chan_allocation & BT_AUDIO_LOCATION_FRONT_RIGHT) != 0;
|
||||
const bool is_mono = chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
const uint8_t ts_jitter_us = 100; /* timestamps may have jitter */
|
||||
|
||||
static size_t cnt;
|
||||
|
||||
if ((++cnt % bap_get_recv_stats_interval()) == 0U) {
|
||||
LOG_INF("[%zu]: Adding USB audio frame", cnt);
|
||||
}
|
||||
|
||||
if (frame_size > LC3_MAX_NUM_SAMPLES_MONO * sizeof(int16_t) || frame_size == 0U) {
|
||||
LOG_DBG("Invalid frame of size %zu", frame_size);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (get_chan_cnt(chan_allocation) != 1) {
|
||||
LOG_DBG("Invalid channel allocation %d", chan_allocation);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (((is_left || is_right) && decoded_sdu.mono_frames_cnt != 0) ||
|
||||
(is_mono &&
|
||||
(decoded_sdu.left_frames_cnt != 0U || decoded_sdu.right_frames_cnt != 0U))) {
|
||||
LOG_DBG("Cannot mix and match mono with left or right");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if the frame can be combined with a previous frame from another channel, of if
|
||||
* we have to send previous data to USB and then store the current frame
|
||||
*
|
||||
* This is done by comparing the timestamps of the frames, and in the case that they are the
|
||||
* same, there are additional checks to see if we have received more left than right frames,
|
||||
* in which case we also send existing data
|
||||
*/
|
||||
|
||||
if (ts + ts_jitter_us < decoded_sdu.ts && !ts_overflowed(ts)) {
|
||||
/* Old data, discard */
|
||||
return -ENOEXEC;
|
||||
} else if (ts > decoded_sdu.ts + ts_jitter_us || ts_overflowed(ts)) {
|
||||
/* We are getting new data - Send existing data to ring buffer */
|
||||
bap_usb_send_frames_to_usb();
|
||||
} else { /* same timestamp */
|
||||
bool send = false;
|
||||
|
||||
if (is_left && decoded_sdu.left_frames_cnt > decoded_sdu.right_frames_cnt) {
|
||||
/* We are receiving left again before a right, send to USB */
|
||||
send = true;
|
||||
} else if (is_right && decoded_sdu.right_frames_cnt > decoded_sdu.left_frames_cnt) {
|
||||
/* We are receiving right again before a left, send to USB */
|
||||
send = true;
|
||||
} else if (is_mono) {
|
||||
/* always send mono as it comes */
|
||||
send = true;
|
||||
}
|
||||
|
||||
if (send) {
|
||||
bap_usb_send_frames_to_usb();
|
||||
}
|
||||
}
|
||||
|
||||
if (is_left) {
|
||||
if (decoded_sdu.left_frames_cnt >= ARRAY_SIZE(decoded_sdu.left_frames)) {
|
||||
LOG_WRN("Could not add more left frames");
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(decoded_sdu.left_frames[decoded_sdu.left_frames_cnt++], frame, frame_size);
|
||||
} else if (is_right) {
|
||||
if (decoded_sdu.right_frames_cnt >= ARRAY_SIZE(decoded_sdu.right_frames)) {
|
||||
LOG_WRN("Could not add more right frames");
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(decoded_sdu.right_frames[decoded_sdu.right_frames_cnt++], frame, frame_size);
|
||||
} else if (is_mono) {
|
||||
/* Use left as mono*/
|
||||
if (decoded_sdu.mono_frames_cnt >= ARRAY_SIZE(decoded_sdu.left_frames)) {
|
||||
LOG_WRN("Could not add more mono frames");
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(decoded_sdu.left_frames[decoded_sdu.mono_frames_cnt++], frame, frame_size);
|
||||
} else {
|
||||
/* Unsupported channel */
|
||||
LOG_DBG("Unsupported channel %d", chan_allocation);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
decoded_sdu.ts = ts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bap_usb_clear_frames_to_usb(void)
|
||||
{
|
||||
decoded_sdu.mono_frames_cnt = 0U;
|
||||
decoded_sdu.right_frames_cnt = 0U;
|
||||
decoded_sdu.left_frames_cnt = 0U;
|
||||
decoded_sdu.ts = 0U;
|
||||
}
|
||||
|
||||
int bap_usb_init(void)
|
||||
{
|
||||
const struct device *hs_dev = DEVICE_DT_GET(DT_NODELABEL(hs_0));
|
||||
static const struct usb_audio_ops usb_ops = {
|
||||
.data_request_cb = usb_data_request_cb,
|
||||
.data_written_cb = usb_data_written_cb,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (!device_is_ready(hs_dev)) {
|
||||
LOG_ERR("Cannot get USB Headset Device");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
usb_audio_register(hs_dev, &usb_ops);
|
||||
err = usb_enable(NULL);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to enable USB");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_SOC_NRF5340_CPUAPP)) {
|
||||
/* Use this to turn on 128 MHz clock for the nRF5340 cpu_app
|
||||
* This may not be required, but reduces the risk of not decoding fast enough
|
||||
* to keep up with USB
|
||||
*/
|
||||
err = nrfx_clock_divider_set(NRF_CLOCK_DOMAIN_HFCLK, NRF_CLOCK_HFCLK_DIV_1);
|
||||
|
||||
err -= NRFX_ERROR_BASE_NUM;
|
||||
if (err != 0) {
|
||||
LOG_WRN("Failed to set 128 MHz: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
# For LC3 the following configs are needed
|
||||
CONFIG_FPU=y
|
||||
CONFIG_LIBLC3=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_DEVICE_AUDIO=y
|
||||
CONFIG_USB_DEVICE_PRODUCT="Zephyr Shell USB"
|
||||
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
|
||||
# inctease stack size for that thread.
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
|
|
|
@ -349,3 +349,18 @@ tests:
|
|||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_BT_CAP_COMMANDER=n
|
||||
bluetooth.audio_shell.no_lc3:
|
||||
extra_args: CONF_FILE="audio.conf"
|
||||
build_only: true
|
||||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_FPU=n
|
||||
- CONFIG_LIBLC3=n
|
||||
bluetooth.audio_shell.no_usb:
|
||||
extra_args: CONF_FILE="audio.conf"
|
||||
build_only: true
|
||||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_RING_BUFFER=n
|
||||
- CONFIG_USB_DEVICE_STACK=n
|
||||
- CONFIG_USB_DEVICE_AUDIO=n
|
||||
|
|
Loading…
Reference in a new issue