Skip to content

Commit 4340724

Browse files
alexsvennordicjm
authored andcommitted
applications: nrf5340_audio: Split audio frames into 0.5 ms blocks
- This commit modifies the audio datapath to split incoming audio frames into smaller blocks of 0.5 ms each, allowing better support for other frame lengths such as 7.5 ms. The block size is defined as half of the USB block size, which is typically 1 ms. This change involves updating the metadata to reflect the new block size and adjusting the frame accumulation logic accordingly. - OCT-3702 Signed-off-by: Alexander Svensen <alexander.svensen@nordicsemi.no>
1 parent 0f7f9c5 commit 4340724

6 files changed

Lines changed: 114 additions & 52 deletions

File tree

applications/nrf5340_audio/src/audio/audio_datapath.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL);
3838
* - frame: encoded audio packet exchanged with connectivity
3939
*/
4040

41-
#define BLK_PERIOD_US 1000
41+
#define BLK_PERIOD_US 500
4242

4343
/* Total sample FIFO period in microseconds */
4444
#define FIFO_CHANNELS_MAX MAX(CONFIG_AUDIO_INPUT_CHANNELS, CONFIG_AUDIO_OUTPUT_CHANNELS)
@@ -51,7 +51,7 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL);
5151
#define NUM_BLKS(d) ((d) / BLK_PERIOD_US)
5252
/* Single audio block size in number of samples */
5353
/* clang-format off */
54-
#define BLK_SIZE_SAMPLES(r) (((r)*BLK_PERIOD_US) / 1000000)
54+
#define BLK_SIZE_SAMPLES(r) (((r)*BLK_PERIOD_US) / USEC_PER_SEC)
5555
/* clang-format on */
5656
/* Increment sample FIFO index by one block */
5757
#define NEXT_IDX(i) (((i) < (FIFO_NUM_BLKS - 1)) ? ((i) + 1) : 0)
@@ -61,6 +61,7 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL);
6161
#define NUM_BLKS_IN_FRAME NUM_BLKS(CONFIG_AUDIO_FRAME_DURATION_US)
6262
#define BLK_MONO_NUM_SAMPS BLK_SIZE_SAMPLES(CONFIG_AUDIO_SAMPLE_RATE_HZ)
6363
#define BLK_MULTI_CHAN_NUM_SAMPS (BLK_MONO_NUM_SAMPS * CONFIG_AUDIO_OUTPUT_CHANNELS)
64+
6465
/* Number of octets in a single audio block */
6566
#define BLK_MONO_SIZE_OCTETS (BLK_MONO_NUM_SAMPS * CONFIG_AUDIO_BIT_DEPTH_OCTETS)
6667
#define BLK_MULTI_CHAN_SIZE_OCTETS (BLK_MULTI_CHAN_NUM_SAMPS * CONFIG_AUDIO_BIT_DEPTH_OCTETS)
@@ -83,7 +84,7 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL);
8384
#define APLL_FREQ_ADJ(t) (-((t)*1000) / 331)
8485
/* clang-format on */
8586

86-
#define DRIFT_MEAS_PERIOD_US 100000
87+
#define DRIFT_MEAS_PERIOD_US 150000
8788
#define DRIFT_ERR_THRESH_LOCK 16
8889
#define DRIFT_ERR_THRESH_UNLOCK 32
8990
/* To get smaller corrections */
@@ -190,7 +191,7 @@ static int filled_blocks_get(void)
190191
}
191192

192193
static struct audio_metadata i2s_meta = {.data_coding = PCM,
193-
.data_len_us = 1000,
194+
.data_len_us = BLK_PERIOD_US,
194195
.sample_rate_hz = CONFIG_AUDIO_SAMPLE_RATE_HZ,
195196
.bits_per_sample = CONFIG_AUDIO_BIT_DEPTH_BITS,
196197
.carried_bits_per_sample = CONFIG_AUDIO_BIT_DEPTH_BITS,
@@ -776,7 +777,7 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t
776777
/* Update metadata */
777778
struct audio_metadata *meta = net_buf_user_data(i2s_current_frame);
778779

779-
meta->data_len_us += i2s_meta.data_len_us; /* Each block is 1ms */
780+
meta->data_len_us += i2s_meta.data_len_us;
780781
meta->bytes_per_location += i2s_meta.bytes_per_location;
781782
i2s_blocks_in_current_frame++;
782783

@@ -792,7 +793,7 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t
792793
* queue is ready
793794
*/
794795
net_buf_remove_mem(i2s_current_frame, BLK_MULTI_CHAN_SIZE_OCTETS);
795-
meta->data_len_us -= i2s_meta.data_len_us; /* Each block is 1ms */
796+
meta->data_len_us -= i2s_meta.data_len_us;
796797
meta->bytes_per_location -= i2s_meta.bytes_per_location;
797798
i2s_blocks_in_current_frame--;
798799

applications/nrf5340_audio/src/audio/sw_codec_select.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
#include "device_location.h"
2626

2727
#if (CONFIG_SW_CODEC_LC3)
28-
#define LC3_MAX_FRAME_SIZE_MS 10
29-
#define LC3_ENC_MONO_FRAME_SIZE (CONFIG_LC3_BITRATE_MAX * LC3_MAX_FRAME_SIZE_MS / (8 * 1000))
28+
#define LC3_MAX_FRAME_SIZE_US 10000
29+
#define LC3_ENC_MONO_FRAME_SIZE \
30+
(CONFIG_LC3_BITRATE_MAX * LC3_MAX_FRAME_SIZE_US / (8 * USEC_PER_SEC))
3031
#define LC3_PCM_NUM_BYTES_MONO \
31-
(CONFIG_AUDIO_SAMPLE_RATE_HZ * CONFIG_AUDIO_BIT_DEPTH_OCTETS * LC3_MAX_FRAME_SIZE_MS / 1000)
32+
(CONFIG_AUDIO_SAMPLE_RATE_HZ * CONFIG_AUDIO_BIT_DEPTH_OCTETS * LC3_MAX_FRAME_SIZE_US / \
33+
USEC_PER_SEC)
3234
#define LC3_ENC_TIME_US 3000
3335
#define LC3_DEC_TIME_US 1500
3436
#else

applications/nrf5340_audio/src/bluetooth/bt_stream/le_audio.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ int le_audio_bitrate_get(const struct bt_audio_codec_cfg *const codec, uint32_t
206206
return ret;
207207
}
208208

209-
int frames_per_sec = 1000000 / dur_us;
209+
float frames_per_sec = (float)USEC_PER_SEC / dur_us;
210210
uint32_t octets_per_sdu;
211211

212212
ret = le_audio_octets_per_frame_get(codec, &octets_per_sdu);

applications/nrf5340_audio/src/bluetooth/bt_stream/le_audio.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include <audio_defines.h>
2323

2424
#define LE_AUDIO_ZBUS_EVENT_WAIT_TIME K_MSEC(5)
25-
#define LE_AUDIO_SDU_SIZE_OCTETS(bitrate, dur_us) (bitrate / (1000000 / dur_us) / 8)
25+
#define LE_AUDIO_SDU_SIZE_OCTETS(bitrate, dur_us) (bitrate / (USEC_PER_SEC / dur_us) / 8)
2626

2727
#if CONFIG_SAMPLE_RATE_CONVERTER && CONFIG_AUDIO_SAMPLE_RATE_48000_HZ
2828
#define BT_AUDIO_CODEC_CAPABILIY_FREQ \

applications/nrf5340_audio/src/modules/audio_usb.c

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ K_MEM_SLAB_DEFINE_STATIC(usb_rx_slab, ROUND_UP(USB_BLOCK_SIZE_MULTI_CHAN, UDC_BU
3838
static struct k_msgq *audio_q_tx;
3939
static struct k_msgq *audio_q_rx;
4040

41+
/* USB blocks are 1 ms each, but we split them into 0.5 ms blocks for processing,
42+
* hence the dividing by two
43+
*/
4144
NET_BUF_POOL_FIXED_DEFINE(pool_in, USB_BLOCKS,
42-
(USB_BLOCK_SIZE_MULTI_CHAN * CONFIG_FIFO_FRAME_SPLIT_NUM),
45+
(USB_BLOCK_SIZE_MULTI_CHAN * CONFIG_FIFO_FRAME_SPLIT_NUM / 2),
4346
sizeof(struct audio_metadata), NULL);
4447

4548
static uint32_t rx_num_overruns;
@@ -167,6 +170,21 @@ static void *get_recv_buf_cb(const struct device *dev, uint8_t terminal, uint16_
167170
return buf;
168171
}
169172

173+
static void half_buf_add(struct net_buf *frame, void *data, uint16_t size,
174+
uint32_t *blocks_in_frame)
175+
{
176+
const uint32_t half_data_len_us = usb_in_meta.data_len_us / 2;
177+
const uint32_t half_bytes_per_location = usb_in_meta.bytes_per_location / 2;
178+
179+
net_buf_add_mem(frame, data, size);
180+
181+
struct audio_metadata *meta = net_buf_user_data(frame);
182+
183+
meta->data_len_us += half_data_len_us;
184+
meta->bytes_per_location += half_bytes_per_location;
185+
(*blocks_in_frame)++;
186+
}
187+
170188
static void data_recv_cb(const struct device *dev, uint8_t terminal, void *buf, uint16_t size,
171189
void *user_data)
172190
{
@@ -175,23 +193,20 @@ static void data_recv_cb(const struct device *dev, uint8_t terminal, void *buf,
175193
ARG_UNUSED(user_data);
176194

177195
int ret;
178-
/* Frame accumulation for 10ms frames */
179-
static struct net_buf *current_frame;
180-
static uint32_t blocks_in_current_frame;
196+
/* Frame accumulation */
197+
static struct net_buf *frame_current;
198+
static struct net_buf *frame_spillover;
199+
static uint32_t blocks_in_frame_current;
200+
static uint32_t blocks_in_frame_spillover;
181201

182202
if (unlikely(buf == NULL)) {
183203
LOG_ERR("Received NULL buffer");
184204
return;
185205
}
186206

187207
/* Fast exit conditions */
188-
if (unlikely(size == 0 || !playing_state)) {
189-
k_mem_slab_free(&usb_rx_slab, buf);
190-
return;
191-
}
192-
193-
/* Terminal check */
194-
if (unlikely(!(terminal_headset_out_enabled || terminal_headphones_out_enabled))) {
208+
if (unlikely(size == 0 || !playing_state) ||
209+
!(terminal_headset_out_enabled || terminal_headphones_out_enabled)) {
195210
k_mem_slab_free(&usb_rx_slab, buf);
196211
return;
197212
}
@@ -204,52 +219,96 @@ static void data_recv_cb(const struct device *dev, uint8_t terminal, void *buf,
204219
}
205220

206221
/* Allocate new frame if we don't have one */
207-
if (current_frame == NULL) {
208-
/* Check space availability */
209-
if (unlikely(k_msgq_num_free_get(audio_q_rx) == 0 || pool_in.avail_count == 0)) {
210-
goto overrun_cleanup;
222+
if (frame_current == NULL) {
223+
if (frame_spillover != NULL) {
224+
frame_current = frame_spillover;
225+
blocks_in_frame_current = blocks_in_frame_spillover;
226+
frame_spillover = NULL;
227+
blocks_in_frame_spillover = 0;
228+
} else {
229+
/* Check space availability */
230+
if (unlikely(k_msgq_num_free_get(audio_q_rx) == 0 ||
231+
pool_in.avail_count == 0)) {
232+
goto overrun_cleanup;
233+
}
234+
235+
frame_current = net_buf_alloc(&pool_in, K_NO_WAIT);
236+
if (unlikely(frame_current == NULL)) {
237+
LOG_WRN("Out of RX buffers for frame");
238+
goto overrun_cleanup;
239+
}
240+
241+
/* Initialize metadata for the first block */
242+
struct audio_metadata *meta = net_buf_user_data(frame_current);
243+
244+
*meta = usb_in_meta;
245+
meta->data_len_us = 0;
246+
meta->bytes_per_location = 0;
247+
blocks_in_frame_current = 0;
248+
}
249+
}
250+
251+
/* Check if we are about to spill over, if so: allocate spill_over frame */
252+
if ((blocks_in_frame_current + 2) > CONFIG_FIFO_FRAME_SPLIT_NUM) {
253+
const size_t half_size = size / 2;
254+
255+
/* Add half the buffer to current frame */
256+
half_buf_add(frame_current, buf, half_size, &blocks_in_frame_current);
257+
258+
if (frame_spillover != NULL) {
259+
LOG_WRN("Previous spillover frame not consumed, dropping it");
260+
net_buf_unref(frame_spillover);
261+
blocks_in_frame_spillover = 0;
211262
}
212263

213-
current_frame = net_buf_alloc(&pool_in, K_NO_WAIT);
214-
if (unlikely(current_frame == NULL)) {
215-
LOG_WRN("Out of RX buffers for frame");
264+
/* Allocate new frame for spill over data since current frame is full. If allocation
265+
* fails, drop the spill over data to prevent blocking the USB endpoint, but keep
266+
* the current frame to allow it to be sent to the audio system.
267+
*/
268+
frame_spillover = net_buf_alloc(&pool_in, K_NO_WAIT);
269+
if (unlikely(frame_spillover == NULL)) {
270+
LOG_WRN("Out of RX buffers for spill over frame");
216271
goto overrun_cleanup;
217272
}
218273

219274
/* Initialize metadata for the first block */
220-
struct audio_metadata *meta = net_buf_user_data(current_frame);
221-
*meta = usb_in_meta;
222-
meta->data_len_us = 0;
223-
meta->bytes_per_location = 0;
224-
blocks_in_current_frame = 0;
225-
}
226-
227-
/* Add block data directly to current frame */
228-
net_buf_add_mem(current_frame, buf, size);
275+
struct audio_metadata *meta_spill_over = net_buf_user_data(frame_spillover);
229276

230-
/* Update metadata */
231-
struct audio_metadata *meta = net_buf_user_data(current_frame);
277+
*meta_spill_over = usb_in_meta;
278+
meta_spill_over->data_len_us = 0;
279+
meta_spill_over->bytes_per_location = 0;
280+
blocks_in_frame_spillover = 0;
232281

233-
/* Accumulate per-block duration (typically 1 ms) */
234-
meta->data_len_us += usb_in_meta.data_len_us;
235-
meta->bytes_per_location += usb_in_meta.bytes_per_location;
236-
blocks_in_current_frame++;
282+
/* Add half the buffer to spill over frame */
283+
half_buf_add(frame_spillover, (char *)buf + half_size, half_size,
284+
&blocks_in_frame_spillover);
285+
} else {
286+
/* Add block data directly to current frame */
287+
net_buf_add_mem(frame_current, buf, size);
288+
/* Update metadata */
289+
struct audio_metadata *meta = net_buf_user_data(frame_current);
290+
291+
/* Accumulate one full 1 ms USB buffer, which counts as two 0.5 ms blocks. */
292+
meta->data_len_us += usb_in_meta.data_len_us;
293+
meta->bytes_per_location += usb_in_meta.bytes_per_location;
294+
blocks_in_frame_current += 2;
295+
}
237296

238297
/* Release USB buffer */
239298
k_mem_slab_free(&usb_rx_slab, buf);
240299

241300
/* Check if we have a complete frame */
242-
if (blocks_in_current_frame >= CONFIG_FIFO_FRAME_SPLIT_NUM) {
301+
if (blocks_in_frame_current >= CONFIG_FIFO_FRAME_SPLIT_NUM) {
243302
/* Put complete frame into RX queue */
244-
ret = k_msgq_put(audio_q_rx, (void *)&current_frame, K_NO_WAIT);
303+
ret = k_msgq_put(audio_q_rx, (void *)&frame_current, K_NO_WAIT);
245304
if (ret) {
246305
LOG_ERR("Failed to store complete frame");
247-
net_buf_unref(current_frame);
306+
net_buf_unref(frame_current);
248307
}
249308

250309
/* Reset for next frame */
251-
current_frame = NULL;
252-
blocks_in_current_frame = 0;
310+
frame_current = NULL;
311+
blocks_in_frame_current = 0;
253312
}
254313

255314
return;

applications/nrf5340_audio/src/utils/Kconfig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ menu "FIFO"
1212

1313
config FIFO_FRAME_SPLIT_NUM
1414
int "Number of blocks to make up one frame of audio data"
15-
default 10
15+
default 20
1616
help
1717
Easy DMA in I2S requires two buffers to be filled before I2S
1818
transmission will begin. In order to reduce latency, an audio
1919
frame can be split into multiple blocks with this parameter. USB
2020
sends data in 1 ms blocks, so we need the split to match that.
21-
Since we set frame size to 10 ms for USB, 10 is selected as
22-
FRAME_SPLIT_NUM
21+
If we set frame size to 10 ms for USB, 20 is selected as
22+
FIFO_FRAME_SPLIT_NUM
2323

2424
config FIFO_TX_FRAME_COUNT
2525
int "Max number of audio frames in TX slab"

0 commit comments

Comments
 (0)