xref: /freebsd/tests/sys/sound/pcm_read_write.c (revision 6672831bda883756d7f4598bb4b119f99eb1e7d2)
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 	/* u-law and A-law sample formats. */
74 	{"mulaw_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0xffffff87},
75 	{"mulaw_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0x00000079},
76 	{"alaw_1", {0x2a, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0xffffff83},
77 	{"alaw_2", {0xab, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0x00000079}
78 };
79 
80 /* Normalize sample values in strictly correct (but slow) c. */
81 static intpcm_t
local_normalize(intpcm_t value,int val_bits,int norm_bits)82 local_normalize(intpcm_t value, int val_bits, int norm_bits)
83 {
84 	int32_t divisor;
85 	intpcm_t remainder;
86 
87 	/* Avoid undefined or implementation defined behavior. */
88 	if (val_bits < norm_bits)
89 		/* Multiply instead of left shift (value may be negative). */
90 		return (value * (1 << (norm_bits - val_bits)));
91 	else if (val_bits > norm_bits) {
92 		divisor = (1 << (val_bits - norm_bits));
93 		/* Positive remainder, to discard lowest bits from value. */
94 		remainder = value % divisor;
95 		remainder = (remainder + divisor) % divisor;
96 		/* Divide instead of right shift (value may be negative). */
97 		return ((value - remainder) / divisor);
98 	}
99 	return value;
100 }
101 
102 /* Restrict magnitude of sample value to 24bit for 32bit calculations. */
103 static intpcm_t
local_calc_limit(intpcm_t value,int val_bits)104 local_calc_limit(intpcm_t value, int val_bits)
105 {
106 	/*
107 	 * When intpcm32_t is defined to be 32bit, calculations for mixing and
108 	 * volume changes use 32bit integers instead of 64bit. To get some
109 	 * headroom for calculations, 32bit sample values are restricted to
110 	 * 24bit magnitude in that case. Also avoid implementation defined
111 	 * behavior here.
112 	 */
113 	if (sizeof(intpcm32_t) == (32 / 8) && val_bits == 32)
114 		return (local_normalize(value, 32, 24));
115 	return value;
116 }
117 
118 /* Lookup tables to read u-law and A-law sample formats. */
119 static const uint8_t ulaw_to_u8[G711_TABLE_SIZE] = ULAW_TO_U8;
120 static const uint8_t alaw_to_u8[G711_TABLE_SIZE] = ALAW_TO_U8;
121 
122 /* Helper function to read one sample value from a buffer. */
123 static intpcm_t
local_pcm_read(uint8_t * src,uint32_t format)124 local_pcm_read(uint8_t *src, uint32_t format)
125 {
126 	intpcm_t value;
127 
128 	switch (format) {
129 	case AFMT_S8:
130 		value = _PCM_READ_S8_NE(src);
131 		break;
132 	case AFMT_U8:
133 		value = _PCM_READ_U8_NE(src);
134 		break;
135 	case AFMT_S16_LE:
136 		value = _PCM_READ_S16_LE(src);
137 		break;
138 	case AFMT_S16_BE:
139 		value = _PCM_READ_S16_BE(src);
140 		break;
141 	case AFMT_U16_LE:
142 		value = _PCM_READ_U16_LE(src);
143 		break;
144 	case AFMT_U16_BE:
145 		value = _PCM_READ_U16_BE(src);
146 		break;
147 	case AFMT_S24_LE:
148 		value = _PCM_READ_S24_LE(src);
149 		break;
150 	case AFMT_S24_BE:
151 		value = _PCM_READ_S24_BE(src);
152 		break;
153 	case AFMT_U24_LE:
154 		value = _PCM_READ_U24_LE(src);
155 		break;
156 	case AFMT_U24_BE:
157 		value = _PCM_READ_U24_BE(src);
158 		break;
159 	case AFMT_S32_LE:
160 		value = _PCM_READ_S32_LE(src);
161 		break;
162 	case AFMT_S32_BE:
163 		value = _PCM_READ_S32_BE(src);
164 		break;
165 	case AFMT_U32_LE:
166 		value = _PCM_READ_U32_LE(src);
167 		break;
168 	case AFMT_U32_BE:
169 		value = _PCM_READ_U32_BE(src);
170 		break;
171 	case AFMT_MU_LAW:
172 		value = _G711_TO_INTPCM(ulaw_to_u8, *src);
173 		break;
174 	case AFMT_A_LAW:
175 		value = _G711_TO_INTPCM(alaw_to_u8, *src);
176 		break;
177 	default:
178 		value = 0;
179 	}
180 
181 	return (value);
182 }
183 
184 /* Helper function to read one sample value from a buffer for calculations. */
185 static intpcm_t
local_pcm_read_calc(uint8_t * src,uint32_t format)186 local_pcm_read_calc(uint8_t *src, uint32_t format)
187 {
188 	intpcm_t value;
189 
190 	switch (format) {
191 	case AFMT_S8:
192 		value = PCM_READ_S8_NE(src);
193 		break;
194 	case AFMT_U8:
195 		value = PCM_READ_U8_NE(src);
196 		break;
197 	case AFMT_S16_LE:
198 		value = PCM_READ_S16_LE(src);
199 		break;
200 	case AFMT_S16_BE:
201 		value = PCM_READ_S16_BE(src);
202 		break;
203 	case AFMT_U16_LE:
204 		value = PCM_READ_U16_LE(src);
205 		break;
206 	case AFMT_U16_BE:
207 		value = PCM_READ_U16_BE(src);
208 		break;
209 	case AFMT_S24_LE:
210 		value = PCM_READ_S24_LE(src);
211 		break;
212 	case AFMT_S24_BE:
213 		value = PCM_READ_S24_BE(src);
214 		break;
215 	case AFMT_U24_LE:
216 		value = PCM_READ_U24_LE(src);
217 		break;
218 	case AFMT_U24_BE:
219 		value = PCM_READ_U24_BE(src);
220 		break;
221 	case AFMT_S32_LE:
222 		value = PCM_READ_S32_LE(src);
223 		break;
224 	case AFMT_S32_BE:
225 		value = PCM_READ_S32_BE(src);
226 		break;
227 	case AFMT_U32_LE:
228 		value = PCM_READ_U32_LE(src);
229 		break;
230 	case AFMT_U32_BE:
231 		value = PCM_READ_U32_BE(src);
232 		break;
233 	case AFMT_MU_LAW:
234 		value = _G711_TO_INTPCM(ulaw_to_u8, *src);
235 		break;
236 	case AFMT_A_LAW:
237 		value = _G711_TO_INTPCM(alaw_to_u8, *src);
238 		break;
239 	default:
240 		value = 0;
241 	}
242 
243 	return (value);
244 }
245 
246 /* Helper function to read one normalized sample from a buffer. */
247 static intpcm_t
local_pcm_read_norm(uint8_t * src,uint32_t format)248 local_pcm_read_norm(uint8_t *src, uint32_t format)
249 {
250 	intpcm_t value;
251 
252 	value = local_pcm_read(src, format);
253 	value <<= (32 - AFMT_BIT(format));
254 	return (value);
255 }
256 
257 /* Lookup tables to write u-law and A-law sample formats. */
258 static const uint8_t u8_to_ulaw[G711_TABLE_SIZE] = U8_TO_ULAW;
259 static const uint8_t u8_to_alaw[G711_TABLE_SIZE] = U8_TO_ALAW;
260 
261 /* Helper function to write one sample value to a buffer. */
262 static void
local_pcm_write(uint8_t * dst,intpcm_t value,uint32_t format)263 local_pcm_write(uint8_t *dst, intpcm_t value, uint32_t format)
264 {
265 	switch (format) {
266 	case AFMT_S8:
267 		_PCM_WRITE_S8_NE(dst, value);
268 		break;
269 	case AFMT_U8:
270 		_PCM_WRITE_U8_NE(dst, value);
271 		break;
272 	case AFMT_S16_LE:
273 		_PCM_WRITE_S16_LE(dst, value);
274 		break;
275 	case AFMT_S16_BE:
276 		_PCM_WRITE_S16_BE(dst, value);
277 		break;
278 	case AFMT_U16_LE:
279 		_PCM_WRITE_U16_LE(dst, value);
280 		break;
281 	case AFMT_U16_BE:
282 		_PCM_WRITE_U16_BE(dst, value);
283 		break;
284 	case AFMT_S24_LE:
285 		_PCM_WRITE_S24_LE(dst, value);
286 		break;
287 	case AFMT_S24_BE:
288 		_PCM_WRITE_S24_BE(dst, value);
289 		break;
290 	case AFMT_U24_LE:
291 		_PCM_WRITE_U24_LE(dst, value);
292 		break;
293 	case AFMT_U24_BE:
294 		_PCM_WRITE_U24_BE(dst, value);
295 		break;
296 	case AFMT_S32_LE:
297 		_PCM_WRITE_S32_LE(dst, value);
298 		break;
299 	case AFMT_S32_BE:
300 		_PCM_WRITE_S32_BE(dst, value);
301 		break;
302 	case AFMT_U32_LE:
303 		_PCM_WRITE_U32_LE(dst, value);
304 		break;
305 	case AFMT_U32_BE:
306 		_PCM_WRITE_U32_BE(dst, value);
307 		break;
308 	case AFMT_MU_LAW:
309 		*dst = _INTPCM_TO_G711(u8_to_ulaw, value);
310 		break;
311 	case AFMT_A_LAW:
312 		*dst = _INTPCM_TO_G711(u8_to_alaw, value);
313 		break;
314 	default:
315 		value = 0;
316 	}
317 }
318 
319 /* Helper function to write one calculation sample value to a buffer. */
320 static void
local_pcm_write_calc(uint8_t * dst,intpcm_t value,uint32_t format)321 local_pcm_write_calc(uint8_t *dst, intpcm_t value, uint32_t format)
322 {
323 	switch (format) {
324 	case AFMT_S8:
325 		PCM_WRITE_S8_NE(dst, value);
326 		break;
327 	case AFMT_U8:
328 		PCM_WRITE_U8_NE(dst, value);
329 		break;
330 	case AFMT_S16_LE:
331 		PCM_WRITE_S16_LE(dst, value);
332 		break;
333 	case AFMT_S16_BE:
334 		PCM_WRITE_S16_BE(dst, value);
335 		break;
336 	case AFMT_U16_LE:
337 		PCM_WRITE_U16_LE(dst, value);
338 		break;
339 	case AFMT_U16_BE:
340 		PCM_WRITE_U16_BE(dst, value);
341 		break;
342 	case AFMT_S24_LE:
343 		PCM_WRITE_S24_LE(dst, value);
344 		break;
345 	case AFMT_S24_BE:
346 		PCM_WRITE_S24_BE(dst, value);
347 		break;
348 	case AFMT_U24_LE:
349 		PCM_WRITE_U24_LE(dst, value);
350 		break;
351 	case AFMT_U24_BE:
352 		PCM_WRITE_U24_BE(dst, value);
353 		break;
354 	case AFMT_S32_LE:
355 		PCM_WRITE_S32_LE(dst, value);
356 		break;
357 	case AFMT_S32_BE:
358 		PCM_WRITE_S32_BE(dst, value);
359 		break;
360 	case AFMT_U32_LE:
361 		PCM_WRITE_U32_LE(dst, value);
362 		break;
363 	case AFMT_U32_BE:
364 		PCM_WRITE_U32_BE(dst, value);
365 		break;
366 	case AFMT_MU_LAW:
367 		*dst = _INTPCM_TO_G711(u8_to_ulaw, value);
368 		break;
369 	case AFMT_A_LAW:
370 		*dst = _INTPCM_TO_G711(u8_to_alaw, value);
371 		break;
372 	default:
373 		value = 0;
374 	}
375 }
376 
377 /* Helper function to write one normalized sample to a buffer. */
378 static void
local_pcm_write_norm(uint8_t * dst,intpcm_t value,uint32_t format)379 local_pcm_write_norm(uint8_t *dst, intpcm_t value, uint32_t format)
380 {
381 	local_pcm_write(dst, value >> (32 - AFMT_BIT(format)), format);
382 }
383 
384 ATF_TC(pcm_read);
ATF_TC_HEAD(pcm_read,tc)385 ATF_TC_HEAD(pcm_read, tc)
386 {
387 	atf_tc_set_md_var(tc, "descr",
388 	    "Read and verify different pcm sample formats.");
389 }
ATF_TC_BODY(pcm_read,tc)390 ATF_TC_BODY(pcm_read, tc)
391 {
392 	const struct afmt_test_data *test;
393 	uint8_t src[4];
394 	intpcm_t expected, result;
395 	size_t i;
396 
397 	for (i = 0; i < nitems(afmt_tests); i++) {
398 		test = &afmt_tests[i];
399 
400 		/* Copy byte representation, fill with distinctive pattern. */
401 		memset(src, 0x66, sizeof(src));
402 		memcpy(src, test->buffer, test->size);
403 
404 		/* Read sample at format magnitude. */
405 		expected = test->value;
406 		result = local_pcm_read(src, test->format);
407 		ATF_CHECK_MSG(result == expected,
408 		    "pcm_read[\"%s\"].value: expected=0x%08x, result=0x%08x",
409 		    test->label, expected, result);
410 
411 		/* Read sample at format magnitude, for calculations. */
412 		expected = local_calc_limit(test->value, test->size * 8);
413 		result = local_pcm_read_calc(src, test->format);
414 		ATF_CHECK_MSG(result == expected,
415 		    "pcm_read[\"%s\"].calc: expected=0x%08x, result=0x%08x",
416 		    test->label, expected, result);
417 
418 		/* Read sample at full 32 bit magnitude. */
419 		expected = local_normalize(test->value, test->size * 8, 32);
420 		result = local_pcm_read_norm(src, test->format);
421 		ATF_CHECK_MSG(result == expected,
422 		    "pcm_read[\"%s\"].norm: expected=0x%08x, result=0x%08x",
423 		    test->label, expected, result);
424 	}
425 }
426 
427 ATF_TC(pcm_write);
ATF_TC_HEAD(pcm_write,tc)428 ATF_TC_HEAD(pcm_write, tc)
429 {
430 	atf_tc_set_md_var(tc, "descr",
431 	    "Write and verify different pcm sample formats.");
432 }
ATF_TC_BODY(pcm_write,tc)433 ATF_TC_BODY(pcm_write, tc)
434 {
435 	const struct afmt_test_data *test;
436 	uint8_t expected[4];
437 	uint8_t dst[4];
438 	intpcm_t value;
439 	size_t i;
440 
441 	for (i = 0; i < nitems(afmt_tests); i++) {
442 		test = &afmt_tests[i];
443 
444 		/* Write sample of format specific magnitude. */
445 		memcpy(expected, test->buffer, sizeof(expected));
446 		memset(dst, 0x00, sizeof(dst));
447 		value = test->value;
448 		local_pcm_write(dst, value, test->format);
449 		ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
450 		    "pcm_write[\"%s\"].value: "
451 		    "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
452 		    "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
453 		    expected[0], expected[1], expected[2], expected[3],
454 		    dst[0], dst[1], dst[2], dst[3]);
455 
456 		/* Write sample of format specific, calculation magnitude. */
457 		memcpy(expected, test->buffer, sizeof(expected));
458 		memset(dst, 0x00, sizeof(dst));
459 		value = local_calc_limit(test->value, test->size * 8);
460 		if (value != test->value) {
461 			/*
462 			 * 32 bit sample was reduced to 24 bit resolution
463 			 * for calculation, least significant byte is lost.
464 			 */
465 			if (test->format & AFMT_BIGENDIAN)
466 				expected[3] = 0x00;
467 			else
468 				expected[0] = 0x00;
469 		}
470 		local_pcm_write_calc(dst, value, test->format);
471 		ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
472 		    "pcm_write[\"%s\"].calc: "
473 		    "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
474 		    "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
475 		    expected[0], expected[1], expected[2], expected[3],
476 		    dst[0], dst[1], dst[2], dst[3]);
477 
478 		/* Write normalized sample of full 32 bit magnitude. */
479 		memcpy(expected, test->buffer, sizeof(expected));
480 		memset(dst, 0x00, sizeof(dst));
481 		value = local_normalize(test->value, test->size * 8, 32);
482 		local_pcm_write_norm(dst, value, test->format);
483 		ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
484 		    "pcm_write[\"%s\"].norm: "
485 		    "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
486 		    "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
487 		    expected[0], expected[1], expected[2], expected[3],
488 		    dst[0], dst[1], dst[2], dst[3]);
489 	}
490 }
491 
492 ATF_TC(pcm_format_bits);
ATF_TC_HEAD(pcm_format_bits,tc)493 ATF_TC_HEAD(pcm_format_bits, tc)
494 {
495 	atf_tc_set_md_var(tc, "descr",
496 	    "Verify bit width of different pcm sample formats.");
497 }
ATF_TC_BODY(pcm_format_bits,tc)498 ATF_TC_BODY(pcm_format_bits, tc)
499 {
500 	const struct afmt_test_data *test;
501 	size_t bits;
502 	size_t i;
503 
504 	for (i = 0; i < nitems(afmt_tests); i++) {
505 		test = &afmt_tests[i];
506 
507 		/* Check bit width determined for given sample format. */
508 		bits = AFMT_BIT(test->format);
509 		ATF_CHECK_MSG(bits == test->size * 8,
510 		    "format_bits[%zu].size: expected=%zu, result=%zu",
511 		    i, test->size * 8, bits);
512 	}
513 }
514 
ATF_TP_ADD_TCS(tp)515 ATF_TP_ADD_TCS(tp)
516 {
517 	ATF_TP_ADD_TC(tp, pcm_read);
518 	ATF_TP_ADD_TC(tp, pcm_write);
519 	ATF_TP_ADD_TC(tp, pcm_format_bits);
520 
521 	return atf_no_error();
522 }
523