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