xref: /freebsd/tests/sys/sound/pcm_read_write.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
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