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 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 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 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 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 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 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 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 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); 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 } 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); 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 } 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); 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 } 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 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