Skip to content

Commit e440814

Browse files
gWaceykoffes
authored andcommitted
lib: contin_array: Support multiple channels and data types
Update the library to allow building of multi-channel arrays with 8-, 24- and 32-bit samples and 8-, 16 and 32-bit carrier widths. Twister test cases included. Signed-off-by: Graham Wacey <graham.wacey@nordicsemi.no>
1 parent b05ea3c commit e440814

File tree

10 files changed

+1007
-121
lines changed

10 files changed

+1007
-121
lines changed

include/contin_array.h

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
#ifndef CONTIN_ARRAY_H
88
#define CONTIN_ARRAY_H
99

10-
#include <zephyr/kernel.h>
10+
#include <zephyr/net_buf.h>
11+
#include <zephyr/types.h>
12+
#include <stddef.h>
13+
#include <stdbool.h>
14+
#include <stdint.h>
15+
#include <audio_defines.h>
1116

1217
/**
1318
* @file contin_array.h
@@ -17,17 +22,20 @@
1722
* @brief Basic continuous array.
1823
*/
1924

25+
/** @brief Specifies the maximum number of bits used to carry a sample. */
26+
#define PCM_CONT_MAX_CARRIER_BIT_DEPTH (32)
27+
2028
/** @brief Creates a continuous array from a finite array.
2129
*
22-
* @param pcm_cont Pointer to the destination array.
23-
* @param pcm_cont_size Size of pcm_cont.
24-
* @param pcm_finite Pointer to an array of samples or data.
25-
* @param pcm_finite_size Size of pcm_finite.
26-
* @param finite_pos Variable used internally. Must be set
27-
* to 0 for the first run and not changed.
30+
* @param pcm_cont Pointer to the destination array.
31+
* @param pcm_cont_size Size of pcm_cont.
32+
* @param pcm_finite Pointer to an array of samples or data.
33+
* @param pcm_finite_size Size of pcm_finite.
34+
* @param _finite_pos Variable used internally. Must be set to 0 for the first run and not
35+
* changed.
2836
*
2937
* @note This function serves the purpose of e.g. having a set of audio samples
30-
* stored in pcm_finite. This can then be fetched in smaller pieces into ram and
38+
* stored in pcm_finite. This can then be fetched in smaller pieces into RAM and
3139
* played back in a loop using the results in pcm_cont.
3240
* The function keeps track of the current position in finite_pos,
3341
* so that the function can be called multiple times and maintain the correct
@@ -38,7 +46,70 @@
3846
* @retval -ENXIO On NULL pointer.
3947
*/
4048
int contin_array_create(void *pcm_cont, uint32_t pcm_cont_size, void const *const pcm_finite,
41-
uint32_t pcm_finite_size, uint32_t *const finite_pos);
49+
uint32_t pcm_finite_size, uint32_t *_finite_pos);
50+
51+
/**
52+
* @brief Creates a continuous array in the locations in the net_buf of given in locations, from a
53+
* single channel finite array.
54+
*
55+
* @note This function assumes the finite array is of the same PCM format as the continuous array.
56+
* @note This function serves the purpose of e.g. having a set of audio samples
57+
* stored in pcm_finite. This can then be fetched in smaller pieces into RAM and
58+
* played back in a loop using the results in pcm_cont.
59+
* The function keeps track of the current position in finite_pos,
60+
* so that the function can be called multiple times and maintain the correct
61+
* position in pcm_finite.
62+
*
63+
* @param pcm_contin Pointer to the destination net buf.
64+
* @note The continuous array can be empty. If so, the locations given in locations are filled with
65+
* the finite array. All other valid locations are zeroed.
66+
* @param pcm_finite Pointer to the single channel de-interleaved finite array buffer.
67+
* @param pcm_finite_size Size of the finite array buffer.
68+
* @param locations Location(s) to write the finite array to.
69+
* @note If locations is set to zero (mono) the pcm_contin locations must also be set to zero
70+
* (mono) for the function to operate. If non-zero then all locations must be in the pcm_contin
71+
* array for the function to operate.
72+
* @param _finite_pos Variable used internally. Must be set to 0 for the first run and not
73+
* changed.
74+
*
75+
* @retval 0 If the operation was successful.
76+
* @retval ENXIO On NULL pointer.
77+
* @retval EPERM If any sizes or location is out of range.
78+
* @retval EINVAL If the _finite_pos or locations are out of range.
79+
*/
80+
int contin_array_buf_create(struct net_buf *pcm_contin, void const *const pcm_finite,
81+
uint16_t pcm_finite_size, uint32_t locations,
82+
uint16_t *const _finite_pos);
83+
84+
/**
85+
* @brief Creates a continuous array in the locations in the net_buf as given in locations, from a
86+
* single channel finite array in a net_buf.
87+
*
88+
* @note This function serves the purpose of e.g. having a set of audio samples
89+
* stored in pcm_finite. This can then be fetched in smaller pieces into RAM and
90+
* played back in a loop using the results in pcm_cont.
91+
* The function keeps track of the current position in finite_pos,
92+
* so that the function can be called multiple times and maintain the correct
93+
* position in pcm_finite.
94+
*
95+
* @param pcm_contin Pointer to the destination net buf.
96+
* @note The continuous array can be empty. If so, the locations given in locations are filled with
97+
* the finite array. All other valid locations are zeroed.
98+
* @param pcm_finite Pointer to the single channel de-interleaved finite array net buf.
99+
* @param locations Location(s) to write the finite array to.
100+
* @note If locations is set to zero (mono) the pcm_contin locations must also be set to zero
101+
* (mono) for the function to operate. If non-zero then all locations must be in the pcm_contin
102+
* array to for the function to operate.
103+
* @param _finite_pos Variable used internally. Must be set to 0 for the first run and not
104+
* changed.
105+
*
106+
* @retval 0 If the operation was successful.
107+
* @retval EPERM If any sizes or location is out of range.
108+
* @retval ENXIO On NULL pointer.
109+
* @retval EINVAL If the _finite_pos or locations are out of range.
110+
*/
111+
int contin_array_net_buf_create(struct net_buf *pcm_contin, struct net_buf const *const pcm_finite,
112+
uint32_t locations, uint16_t *const _finite_pos);
42113

43114
/**
44115
* @}

lib/contin_array/contin_array.c

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,25 @@
1212
#include <zephyr/logging/log.h>
1313
LOG_MODULE_REGISTER(contin_array, CONFIG_CONTIN_ARRAY_LOG_LEVEL);
1414

15+
static void copy_samples(uint8_t *pcm_contin, uint32_t pcm_contin_size,
16+
uint8_t const *const pcm_finite, uint32_t pcm_finite_size,
17+
uint16_t *const _finite_pos, uint16_t step, uint8_t carrier_bytes)
18+
{
19+
for (size_t j = 0; j < pcm_contin_size; j += carrier_bytes) {
20+
for (size_t k = 0; k < carrier_bytes; k++) {
21+
*pcm_contin++ = pcm_finite[(*_finite_pos)++];
22+
23+
if (*_finite_pos >= pcm_finite_size) {
24+
*_finite_pos = 0;
25+
}
26+
}
27+
28+
pcm_contin += step;
29+
}
30+
}
31+
1532
int contin_array_create(void *const pcm_cont, uint32_t pcm_cont_size, void const *const pcm_finite,
16-
uint32_t pcm_finite_size, uint32_t *const finite_pos)
33+
uint32_t pcm_finite_size, uint32_t *const _finite_pos)
1734
{
1835
LOG_DBG("pcm_cont_size: %d pcm_finite_size %d", pcm_cont_size, pcm_finite_size);
1936

@@ -22,17 +39,147 @@ int contin_array_create(void *const pcm_cont, uint32_t pcm_cont_size, void const
2239
}
2340

2441
if (!pcm_cont_size || !pcm_finite_size) {
25-
LOG_ERR("size cannot be zero");
42+
LOG_ERR("Size cannot be zero");
2643
return -EPERM;
2744
}
2845

2946
for (uint32_t i = 0; i < pcm_cont_size; i++) {
30-
if (*finite_pos > (pcm_finite_size - 1)) {
31-
*finite_pos = 0;
47+
if (*_finite_pos > (pcm_finite_size - 1)) {
48+
*_finite_pos = 0;
3249
}
33-
((char *)pcm_cont)[i] = ((char *)pcm_finite)[*finite_pos];
34-
(*finite_pos)++;
50+
((char *)pcm_cont)[i] = ((char *)pcm_finite)[*_finite_pos];
51+
(*_finite_pos)++;
3552
}
3653

3754
return 0;
3855
}
56+
57+
int contin_array_buf_create(struct net_buf *pcm_contin, void const *const pcm_finite,
58+
uint16_t pcm_finite_size, uint32_t locations, uint16_t *_finite_pos)
59+
{
60+
struct audio_metadata *meta_contin;
61+
uint8_t num_ch;
62+
uint8_t count_ch;
63+
uint8_t *output;
64+
uint8_t carrier_bytes;
65+
uint16_t step;
66+
uint16_t frame_bytes;
67+
uint16_t finite_start_pos;
68+
uint32_t out_locs;
69+
70+
if (pcm_contin == NULL || pcm_finite == NULL || _finite_pos == NULL) {
71+
LOG_ERR("Invalid parameter");
72+
return -ENXIO;
73+
}
74+
75+
if ((pcm_contin->size == 0) || (pcm_finite_size == 0) ||
76+
(*_finite_pos >= pcm_finite_size)) {
77+
LOG_ERR("Size or finite position out of range");
78+
return -EPERM;
79+
}
80+
81+
meta_contin = net_buf_user_data(pcm_contin);
82+
if (meta_contin == NULL) {
83+
LOG_ERR("No metadata");
84+
return -ENXIO;
85+
}
86+
87+
/* Here the number of common output locations is determined */
88+
if (meta_contin->locations == 0 && locations == 0) {
89+
/* Both are mono buffers and hence have a single output location in common */
90+
out_locs = 0x01;
91+
} else {
92+
out_locs = meta_contin->locations & locations;
93+
}
94+
95+
if (out_locs == 0) {
96+
LOG_ERR("Locations error");
97+
return -EPERM;
98+
}
99+
100+
if ((meta_contin->carried_bits_per_sample == 0) ||
101+
(meta_contin->carried_bits_per_sample > PCM_CONT_MAX_CARRIER_BIT_DEPTH) ||
102+
(meta_contin->carried_bits_per_sample % 8)) {
103+
LOG_ERR("Carrier bits invalid: %d", meta_contin->carried_bits_per_sample);
104+
return -EINVAL;
105+
}
106+
107+
carrier_bytes = meta_contin->carried_bits_per_sample / 8;
108+
109+
num_ch = audio_metadata_num_ch_get(meta_contin);
110+
111+
if (pcm_contin->len == 0) {
112+
memset(pcm_contin->data, 0, meta_contin->bytes_per_location * num_ch);
113+
net_buf_add(pcm_contin, meta_contin->bytes_per_location * num_ch);
114+
}
115+
116+
finite_start_pos = *_finite_pos;
117+
118+
if (meta_contin->interleaved) {
119+
step = carrier_bytes * (num_ch - 1);
120+
frame_bytes = carrier_bytes;
121+
} else {
122+
step = 0;
123+
frame_bytes = meta_contin->bytes_per_location;
124+
}
125+
126+
count_ch = 0;
127+
128+
/* While there are common output locations */
129+
while (out_locs) {
130+
if (out_locs & 0x01) {
131+
output = &((uint8_t *)pcm_contin->data)[frame_bytes * count_ch];
132+
133+
*_finite_pos = finite_start_pos;
134+
135+
copy_samples(output, meta_contin->bytes_per_location, (uint8_t *)pcm_finite,
136+
pcm_finite_size, _finite_pos, step, carrier_bytes);
137+
138+
count_ch++;
139+
}
140+
141+
out_locs >>= 1;
142+
}
143+
144+
return 0;
145+
}
146+
147+
int contin_array_net_buf_create(struct net_buf *pcm_contin, struct net_buf const *const pcm_finite,
148+
uint32_t locations, uint16_t *const _finite_pos)
149+
{
150+
struct audio_metadata *meta_contin;
151+
struct audio_metadata *meta_finite;
152+
153+
if (pcm_contin == NULL || pcm_finite == NULL) {
154+
LOG_ERR("Error in input parameter");
155+
return -ENXIO;
156+
}
157+
158+
if (pcm_finite->len == 0) {
159+
LOG_ERR("Finite buffer size error");
160+
return -EPERM;
161+
}
162+
163+
meta_contin = net_buf_user_data(pcm_contin);
164+
if (meta_contin == NULL) {
165+
LOG_ERR("No metadata");
166+
return -ENXIO;
167+
}
168+
169+
meta_finite = net_buf_user_data(pcm_finite);
170+
if (meta_finite == NULL) {
171+
LOG_ERR("No metadata");
172+
return -ENXIO;
173+
}
174+
175+
if ((meta_contin->sample_rate_hz != meta_finite->sample_rate_hz) ||
176+
(meta_contin->bits_per_sample != meta_finite->bits_per_sample) ||
177+
(meta_contin->carried_bits_per_sample != meta_finite->carried_bits_per_sample) ||
178+
audio_metadata_num_ch_get(meta_finite) > 1) {
179+
LOG_ERR("Sample/Carrier/Finite locations mismatch");
180+
return -EINVAL;
181+
}
182+
183+
return contin_array_buf_create(pcm_contin, (void const *const)pcm_finite->data,
184+
meta_finite->bytes_per_location, locations, _finite_pos);
185+
}

tests/lib/contin_array/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,11 @@ project(contin_array)
1111

1212
FILE(GLOB app_sources src/*.c)
1313
target_sources(app PRIVATE ${app_sources})
14+
15+
target_include_directories(app PRIVATE ./src)
16+
17+
target_compile_options(app
18+
PRIVATE
19+
-DCONFIG_BT_BUF_EVT_RX_COUNT=10
20+
-DCONFIG_BT_BUF_ACL_TX_COUNT=8
21+
)

tests/lib/contin_array/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
CONFIG_ZTEST=y
22
CONFIG_IRQ_OFFLOAD=y
3+
CONFIG_NET_BUF=y
34
CONFIG_CONTIN_ARRAY=y
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/ztest.h>
8+
#include <errno.h>
9+
#include <zephyr/tc_util.h>
10+
#include <contin_array.h>
11+
#include "array_test_data.h"
12+
13+
ZTEST(suite_contin_array, test_simp_arr_loop)
14+
{
15+
const uint32_t NUM_ITERATIONS = 200;
16+
const size_t CONTIN_ARR_SIZE = 97; /* Test with random "uneven" value */
17+
const uint32_t CONTIN_LAST_VAL_IDX = (CONTIN_ARR_SIZE - 1);
18+
const size_t const_arr_size = ARRAY_SIZE(test_arr);
19+
char contin_arr[CONTIN_ARR_SIZE];
20+
int ret;
21+
22+
uint8_t contin_first_val;
23+
uint8_t contin_last_val;
24+
uint32_t finite_pos = 0;
25+
26+
for (int i = 0; i < NUM_ITERATIONS; i++) {
27+
contin_first_val = test_arr[finite_pos];
28+
29+
ret = contin_array_create(contin_arr, CONTIN_ARR_SIZE, test_arr, const_arr_size,
30+
&finite_pos);
31+
zassert_equal(ret, 0, "contin_array_create did not return zero");
32+
33+
if (finite_pos) {
34+
contin_last_val = test_arr[finite_pos - 1];
35+
} else {
36+
contin_last_val = test_arr[const_arr_size - 1];
37+
}
38+
39+
zassert_equal(contin_arr[0], contin_first_val, "First value is not identical");
40+
zassert_equal(contin_arr[CONTIN_LAST_VAL_IDX], contin_last_val,
41+
"Last value is not identical");
42+
}
43+
}
44+
45+
/* Test with const array size being shorter than contin array size */
46+
ZTEST(suite_contin_array, test_simp_arr_loop_short)
47+
{
48+
int ret;
49+
const uint32_t NUM_ITERATIONS = 200;
50+
const size_t CONTIN_ARR_SIZE = 97; /* Test with random "uneven" value */
51+
const uint32_t CONTIN_LAST_VAL_IDX = (CONTIN_ARR_SIZE - 1);
52+
/* Test with random "uneven" value, shorter than CONTIN_ARR_SIZE */
53+
const size_t const_arr_size = 44;
54+
char contin_arr[CONTIN_ARR_SIZE];
55+
uint8_t contin_first_val;
56+
uint8_t contin_last_val;
57+
uint32_t finite_pos = 0;
58+
59+
for (int i = 0; i < NUM_ITERATIONS; i++) {
60+
if (finite_pos > (const_arr_size - 1)) {
61+
contin_first_val = test_arr[0];
62+
} else {
63+
contin_first_val = test_arr[finite_pos];
64+
}
65+
66+
ret = contin_array_create(contin_arr, CONTIN_ARR_SIZE, test_arr, const_arr_size,
67+
&finite_pos);
68+
zassert_equal(ret, 0, "Contin_array_create did not return zero");
69+
70+
if (finite_pos) {
71+
contin_last_val = test_arr[finite_pos - 1];
72+
} else {
73+
contin_last_val = test_arr[const_arr_size - 1];
74+
}
75+
76+
zassert_equal(contin_arr[0], contin_first_val,
77+
"%d: First value is not identical: %d vs %d (%d %d)", i,
78+
contin_arr[0], contin_first_val, finite_pos, const_arr_size);
79+
zassert_equal(contin_arr[CONTIN_LAST_VAL_IDX], contin_last_val,
80+
"Last value is not identical");
81+
}
82+
}

0 commit comments

Comments
 (0)