Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/sound/pcm_read_write.c
39536 views
1
/*-
2
* Copyright (c) 2025 Florian Walpen
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
/*
8
* These tests exercise conversion functions of the sound module, used to read
9
* pcm samples from a buffer, and write pcm samples to a buffer. The test cases
10
* are non-exhaustive, but should detect systematic errors in conversion of the
11
* various sample formats supported. In particular, the test cases establish
12
* correctness independent of the machine's endianness, making them suitable to
13
* check for architecture-specific problems.
14
*/
15
16
#include <sys/types.h>
17
#include <sys/soundcard.h>
18
19
#include <atf-c.h>
20
#include <stdio.h>
21
#include <string.h>
22
23
#include <dev/sound/pcm/sound.h>
24
#include <dev/sound/pcm/pcm.h>
25
#include <dev/sound/pcm/g711.h>
26
27
/* Generic test data, with buffer content matching the sample values. */
28
static struct afmt_test_data {
29
const char *label;
30
uint8_t buffer[4];
31
size_t size;
32
int format;
33
intpcm_t value;
34
_Static_assert((sizeof(intpcm_t) == 4),
35
"Test data assumes 32bit, adjust negative values to new size.");
36
} const afmt_tests[] = {
37
/* 8 bit sample formats. */
38
{"s8_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_S8, 0x00000001},
39
{"s8_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_S8, 0xffffff81},
40
{"u8_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_U8, 0xffffff81},
41
{"u8_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_U8, 0x00000001},
42
43
/* 16 bit sample formats. */
44
{"s16le_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_S16_LE, 0x00000201},
45
{"s16le_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_S16_LE, 0xffff8281},
46
{"s16be_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_S16_BE, 0x00000102},
47
{"s16be_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_S16_BE, 0xffff8182},
48
{"u16le_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_U16_LE, 0xffff8201},
49
{"u16le_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_U16_LE, 0x00000281},
50
{"u16be_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_U16_BE, 0xffff8102},
51
{"u16be_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_U16_BE, 0x00000182},
52
53
/* 24 bit sample formats. */
54
{"s24le_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_S24_LE, 0x00030201},
55
{"s24le_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_S24_LE, 0xff838281},
56
{"s24be_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_S24_BE, 0x00010203},
57
{"s24be_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_S24_BE, 0xff818283},
58
{"u24le_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_U24_LE, 0xff830201},
59
{"u24le_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_U24_LE, 0x00038281},
60
{"u24be_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_U24_BE, 0xff810203},
61
{"u24be_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_U24_BE, 0x00018283},
62
63
/* 32 bit sample formats. */
64
{"s32le_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_S32_LE, 0x04030201},
65
{"s32le_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_S32_LE, 0x84838281},
66
{"s32be_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_S32_BE, 0x01020304},
67
{"s32be_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_S32_BE, 0x81828384},
68
{"u32le_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_U32_LE, 0x84030201},
69
{"u32le_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_U32_LE, 0x04838281},
70
{"u32be_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_U32_BE, 0x81020304},
71
{"u32be_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_U32_BE, 0x01828384},
72
73
/* 32 bit floating point sample formats. */
74
{"f32le_1", {0x00, 0x00, 0x00, 0x3f}, 4, AFMT_F32_LE, 0x40000000},
75
{"f32le_2", {0x00, 0x00, 0x00, 0xbf}, 4, AFMT_F32_LE, 0xc0000000},
76
{"f32be_1", {0x3f, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0x40000000},
77
{"f32be_2", {0xbf, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0xc0000000},
78
79
/* u-law and A-law sample formats. */
80
{"mulaw_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0xffffff87},
81
{"mulaw_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0x00000079},
82
{"alaw_1", {0x2a, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0xffffff83},
83
{"alaw_2", {0xab, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0x00000079}
84
};
85
86
/* Normalize sample values in strictly correct (but slow) c. */
87
static intpcm_t
88
local_normalize(intpcm_t value, int val_bits, int norm_bits)
89
{
90
int32_t divisor;
91
intpcm_t remainder;
92
93
/* Avoid undefined or implementation defined behavior. */
94
if (val_bits < norm_bits)
95
/* Multiply instead of left shift (value may be negative). */
96
return (value * (1 << (norm_bits - val_bits)));
97
else if (val_bits > norm_bits) {
98
divisor = (1 << (val_bits - norm_bits));
99
/* Positive remainder, to discard lowest bits from value. */
100
remainder = value % divisor;
101
remainder = (remainder + divisor) % divisor;
102
/* Divide instead of right shift (value may be negative). */
103
return ((value - remainder) / divisor);
104
}
105
return value;
106
}
107
108
/* Restrict magnitude of sample value to 24bit for 32bit calculations. */
109
static intpcm_t
110
local_calc_limit(intpcm_t value, int val_bits)
111
{
112
/*
113
* When intpcm32_t is defined to be 32bit, calculations for mixing and
114
* volume changes use 32bit integers instead of 64bit. To get some
115
* headroom for calculations, 32bit sample values are restricted to
116
* 24bit magnitude in that case. Also avoid implementation defined
117
* behavior here.
118
*/
119
if (sizeof(intpcm32_t) == (32 / 8) && val_bits == 32)
120
return (local_normalize(value, 32, 24));
121
return value;
122
}
123
124
ATF_TC(pcm_read);
125
ATF_TC_HEAD(pcm_read, tc)
126
{
127
atf_tc_set_md_var(tc, "descr",
128
"Read and verify different pcm sample formats.");
129
}
130
ATF_TC_BODY(pcm_read, tc)
131
{
132
const struct afmt_test_data *test;
133
uint8_t src[4];
134
intpcm_t expected, result;
135
size_t i;
136
137
for (i = 0; i < nitems(afmt_tests); i++) {
138
test = &afmt_tests[i];
139
140
/* Copy byte representation, fill with distinctive pattern. */
141
memset(src, 0x66, sizeof(src));
142
memcpy(src, test->buffer, test->size);
143
144
/* Read sample at format magnitude. */
145
expected = test->value;
146
result = pcm_sample_read(src, test->format);
147
ATF_CHECK_MSG(result == expected,
148
"pcm_read[\"%s\"].value: expected=0x%08x, result=0x%08x",
149
test->label, expected, result);
150
151
/* Read sample at format magnitude, for calculations. */
152
expected = local_calc_limit(test->value, test->size * 8);
153
result = pcm_sample_read_calc(src, test->format);
154
ATF_CHECK_MSG(result == expected,
155
"pcm_read[\"%s\"].calc: expected=0x%08x, result=0x%08x",
156
test->label, expected, result);
157
158
/* Read sample at full 32 bit magnitude. */
159
expected = local_normalize(test->value, test->size * 8, 32);
160
result = pcm_sample_read_norm(src, test->format);
161
ATF_CHECK_MSG(result == expected,
162
"pcm_read[\"%s\"].norm: expected=0x%08x, result=0x%08x",
163
test->label, expected, result);
164
}
165
}
166
167
ATF_TC(pcm_write);
168
ATF_TC_HEAD(pcm_write, tc)
169
{
170
atf_tc_set_md_var(tc, "descr",
171
"Write and verify different pcm sample formats.");
172
}
173
ATF_TC_BODY(pcm_write, tc)
174
{
175
const struct afmt_test_data *test;
176
uint8_t expected[4];
177
uint8_t dst[4];
178
intpcm_t value;
179
size_t i;
180
181
for (i = 0; i < nitems(afmt_tests); i++) {
182
test = &afmt_tests[i];
183
184
/* Write sample of format specific magnitude. */
185
memcpy(expected, test->buffer, sizeof(expected));
186
memset(dst, 0x00, sizeof(dst));
187
value = test->value;
188
pcm_sample_write(dst, value, test->format);
189
ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
190
"pcm_write[\"%s\"].value: "
191
"expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
192
"result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
193
expected[0], expected[1], expected[2], expected[3],
194
dst[0], dst[1], dst[2], dst[3]);
195
196
/* Write sample of format specific, calculation magnitude. */
197
memcpy(expected, test->buffer, sizeof(expected));
198
memset(dst, 0x00, sizeof(dst));
199
value = local_calc_limit(test->value, test->size * 8);
200
if (value != test->value) {
201
/*
202
* 32 bit sample was reduced to 24 bit resolution
203
* for calculation, least significant byte is lost.
204
*/
205
if (test->format & AFMT_BIGENDIAN)
206
expected[3] = 0x00;
207
else
208
expected[0] = 0x00;
209
}
210
pcm_sample_write_calc(dst, value, test->format);
211
ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
212
"pcm_write[\"%s\"].calc: "
213
"expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
214
"result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
215
expected[0], expected[1], expected[2], expected[3],
216
dst[0], dst[1], dst[2], dst[3]);
217
218
/* Write normalized sample of full 32 bit magnitude. */
219
memcpy(expected, test->buffer, sizeof(expected));
220
memset(dst, 0x00, sizeof(dst));
221
value = local_normalize(test->value, test->size * 8, 32);
222
pcm_sample_write_norm(dst, value, test->format);
223
ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
224
"pcm_write[\"%s\"].norm: "
225
"expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
226
"result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
227
expected[0], expected[1], expected[2], expected[3],
228
dst[0], dst[1], dst[2], dst[3]);
229
}
230
}
231
232
ATF_TC(pcm_format_bits);
233
ATF_TC_HEAD(pcm_format_bits, tc)
234
{
235
atf_tc_set_md_var(tc, "descr",
236
"Verify bit width of different pcm sample formats.");
237
}
238
ATF_TC_BODY(pcm_format_bits, tc)
239
{
240
const struct afmt_test_data *test;
241
size_t bits;
242
size_t i;
243
244
for (i = 0; i < nitems(afmt_tests); i++) {
245
test = &afmt_tests[i];
246
247
/* Check bit width determined for given sample format. */
248
bits = AFMT_BIT(test->format);
249
ATF_CHECK_MSG(bits == test->size * 8,
250
"format_bits[%zu].size: expected=%zu, result=%zu",
251
i, test->size * 8, bits);
252
}
253
}
254
255
ATF_TP_ADD_TCS(tp)
256
{
257
ATF_TP_ADD_TC(tp, pcm_read);
258
ATF_TP_ADD_TC(tp, pcm_write);
259
ATF_TP_ADD_TC(tp, pcm_format_bits);
260
261
return atf_no_error();
262
}
263
264