1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
6
7 #include <sof/audio/pcm_converter.h>
8 #include <sof/audio/format.h>
9 #include <sof/common.h>
10 #include <sof/audio/buffer.h>
11 #include <ipc/stream.h>
12 #include <ipc/stream.h>
13
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <stddef.h>
17 #include <setjmp.h>
18 #include <math.h>
19 #include <stdint.h>
20 #include <cmocka.h>
21
22 #ifdef HAVE_MALLOC_H
23 #include <malloc.h>
24 #else
25 #include <stdlib.h>
26 #endif
27
28 #include "../../util.h"
29
30 /* used during float assertions */
31 #define EPSILON 0.01f
32
33 /* base number to check combinations for each data format */
34 #define PCM_TEST_INT_NUMBERS \
35 -0, 0, 0, 0, -1, 1, -2, 2, \
36 -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -25, 25, -57, 57, -100, 100
37 #define PCM_TEST_FLOAT_NUMBERS \
38 -0, 0, 0.1, -0.1f, -0.8f, 0.8f, -1.9f, 1.9f, \
39 -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -25, 25, -57, 57, -100, 100
40
41 /* generate biggest signed value on a 24 bits number */
42 #define INT24_MAX ((1 << (24 - 1)) - 1)
43
44 /* generate smallest signed value on a 24 bits number */
45 #define INT24_MIN (-INT24_MAX - 1)
46
47 /* conversion ration */
48 const float ratio16 = 1.f / (1u << 15);
49 const float ratio24 = 1.f / (1u << 23);
50 const float ratio32 = 1.f / (1u << 31);
51
52 /*
53 * Function design to help debugging conversion between fixed and float numbers.
54 *
55 * Usage:
56 * add declaration of this function for example to pcm_converter_generic.c
57 * And feel free to use pcm_float_print_values in functions like
58 * _pcm_convert_i_to_f or _pcm_convert_f_to_i during debugging
59 *
60 * param it int number to show
61 * param fl pointer to float number to show
62 * param ex expected number
63 */
pcm_float_print_values(int32_t it,int32_t * fl,double ep,const char * func_name)64 static void pcm_float_print_values(int32_t it, int32_t *fl, double ep,
65 const char *func_name)
66 {
67 const int32_t mantissa_mask = (1 << 23) - 1;
68 int32_t ex = ((*fl >> 23) & 0xFF) - 127;
69 int32_t mt = *fl & mantissa_mask;
70 float mantissa_value = mt * (1.0f / mantissa_mask) + 1.f;
71
72 print_message("%s: 0x%08Xf => %.3f * 2**%3d = %10.3e f <=> %7d d \t(expected: %10.3e)\n",
73 func_name, *fl, mantissa_value, ex, *((float *)fl), it,
74 ep);
75 }
76
_test_pcm_convert(enum sof_ipc_frame frm_in,enum sof_ipc_frame frm_out,int samples,const void * data)77 static struct comp_buffer *_test_pcm_convert(enum sof_ipc_frame frm_in, enum sof_ipc_frame frm_out,
78 int samples, const void *data)
79 {
80 /* create buffers - output buffer may be too big for test */
81 struct comp_buffer *source;
82 struct comp_buffer *sink;
83 pcm_converter_func fun;
84 const uint8_t fillval = 0xAB;
85 const int inbytes = samples * get_sample_bytes(frm_in);
86 const int outbytes = (samples + 1) * get_sample_bytes(frm_out);
87
88 /* create buffers */
89 source = create_test_source(NULL, 0, frm_in, 1, inbytes);
90 sink = create_test_sink(NULL, 0, frm_out, 1, outbytes);
91
92 /* fill source */
93 memcpy_s(source->stream.w_ptr, source->stream.size, data, inbytes);
94 audio_stream_produce(&source->stream, inbytes);
95
96 /* fill sink memory - to validate last value */
97 memset(sink->stream.w_ptr, fillval, outbytes);
98
99 /* run conversion */
100 fun = pcm_get_conversion_function(frm_in, frm_out);
101 assert_non_null(fun);
102 fun(&source->stream, 0, &sink->stream, 0, samples);
103
104 /* assert last value in sink is untouched */
105 assert_int_equal(((uint8_t *)sink->stream.w_ptr)[outbytes - 1], fillval);
106
107 /* free source and return sink */
108 free_test_source(source);
109 return sink;
110 }
111
mask_array(int32_t mask,int32_t * parray,int cnt)112 static void mask_array(int32_t mask, int32_t *parray, int cnt)
113 {
114 assert_non_null(parray);
115 while (cnt--)
116 parray[cnt] &= mask;
117 }
118
scale_array(float ratio,float * parray,int cnt)119 static void scale_array(float ratio, float *parray, int cnt)
120 {
121 assert_non_null(parray);
122 while (cnt--)
123 parray[cnt] *= ratio;
124 }
125
126 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S16LE
test_pcm_convert_s16_to_f(void ** state)127 static void test_pcm_convert_s16_to_f(void **state)
128 {
129 typedef int16_t Tin;
130 typedef float Tout;
131 static const Tin source_buf[] = {
132 PCM_TEST_INT_NUMBERS,
133 INT16_MIN + 1, INT16_MIN,
134 INT16_MAX - 1, INT16_MAX,
135 };
136 static Tout expected_buf[] = {
137 PCM_TEST_INT_NUMBERS,
138 INT16_MIN + 1, INT16_MIN,
139 INT16_MAX - 1, INT16_MAX,
140 };
141
142 struct comp_buffer *sink;
143 int i, N = ARRAY_SIZE(source_buf);
144 Tout *read_val;
145
146 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
147 scale_array(ratio16, expected_buf, N);
148
149 /* run test */
150 sink = _test_pcm_convert(SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_FLOAT,
151 N, source_buf);
152
153 /* check results */
154 for (i = 0; i < N; ++i) {
155 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
156 print_message("%2d/%02d ", i + 1, N);
157 pcm_float_print_values(source_buf[i], (int32_t *)read_val,
158 expected_buf[i], __func__);
159 assert_float_equal(*read_val, expected_buf[i], EPSILON);
160 }
161
162 /* free memory */
163 free_test_sink(sink);
164 }
165
test_pcm_convert_f_to_s16(void ** state)166 static void test_pcm_convert_f_to_s16(void **state)
167 {
168 typedef float Tin;
169 typedef int16_t Tout;
170 static Tin source_buf[] = {
171 PCM_TEST_FLOAT_NUMBERS,
172 INT16_MIN + 1, INT16_MIN,
173 INT16_MAX - 1, INT16_MAX,
174 };
175 static const Tout expected_buf[] = {
176 PCM_TEST_INT_NUMBERS,
177 INT16_MIN + 1, INT16_MIN,
178 INT16_MAX - 1, INT16_MAX,
179 };
180
181 struct comp_buffer *sink;
182 int i, N = ARRAY_SIZE(source_buf);
183 Tout *read_val;
184
185 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
186 scale_array(ratio16, source_buf, N);
187
188 /* run test */
189 sink = _test_pcm_convert(SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_S16_LE,
190 N, source_buf);
191
192 /* check results */
193 for (i = 0; i < N; ++i) {
194 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
195 print_message("%2d/%02d ", i + 1, N);
196 pcm_float_print_values(*read_val, (int32_t *)&source_buf[i],
197 (float)expected_buf[i], __func__);
198 assert_int_equal(*read_val, expected_buf[i]);
199 }
200
201 /* free memory */
202 free_test_sink(sink);
203 }
204 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S16LE */
205
206 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S24LE
test_pcm_convert_s24_in_s32_to_f(void ** state)207 static void test_pcm_convert_s24_in_s32_to_f(void **state)
208 {
209 typedef int32_t Tin;
210 typedef float Tout;
211 static const Tin source_buf[] = {
212 PCM_TEST_INT_NUMBERS,
213 INT16_MIN + 1, INT16_MIN,
214 INT16_MAX - 1, INT16_MAX,
215 INT24_MIN + 1, INT24_MIN,
216 INT24_MAX - 1, INT24_MAX,
217 };
218 static Tout expected_buf[] = {
219 PCM_TEST_INT_NUMBERS,
220 INT16_MIN + 1, INT16_MIN,
221 INT16_MAX - 1, INT16_MAX,
222 INT24_MIN + 1, INT24_MIN,
223 INT24_MAX - 1, INT24_MAX,
224 };
225
226 struct comp_buffer *sink;
227 int i, N = ARRAY_SIZE(source_buf);
228 Tout *read_val;
229
230 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
231 scale_array(ratio24, expected_buf, N);
232
233 /* run test */
234 sink = _test_pcm_convert(SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_FLOAT,
235 N, source_buf);
236
237 /* check results */
238 for (i = 0; i < N; ++i) {
239 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
240 print_message("%2d/%02d ", i + 1, N);
241 pcm_float_print_values(source_buf[i], (int32_t *)read_val,
242 expected_buf[i], __func__);
243 assert_float_equal(*read_val, expected_buf[i], EPSILON);
244 }
245
246 /* free memory */
247 free_test_sink(sink);
248 }
249
test_pcm_convert_s24_to_f(void ** state)250 static void test_pcm_convert_s24_to_f(void **state)
251 {
252 typedef int32_t Tin;
253 typedef float Tout;
254 static Tin source_buf[] = {
255 PCM_TEST_INT_NUMBERS,
256 INT16_MIN + 1, INT16_MIN,
257 INT16_MAX - 1, INT16_MAX,
258 INT24_MIN + 1, INT24_MIN,
259 INT24_MAX - 1, INT24_MAX,
260 };
261 static Tout expected_buf[] = {
262 PCM_TEST_INT_NUMBERS,
263 INT16_MIN + 1, INT16_MIN,
264 INT16_MAX - 1, INT16_MAX,
265 INT24_MIN + 1, INT24_MIN,
266 INT24_MAX - 1, INT24_MAX,
267 };
268
269 struct comp_buffer *sink;
270 int i, N = ARRAY_SIZE(source_buf);
271 Tout *read_val;
272
273 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
274 mask_array(MASK(24, 0), source_buf, N);
275 scale_array(ratio24, expected_buf, N);
276
277 /* run test */
278 sink = _test_pcm_convert(SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_FLOAT,
279 N, source_buf);
280
281 /* check results */
282 for (i = 0; i < N; ++i) {
283 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
284 print_message("%2d/%02d ", i + 1, N);
285 pcm_float_print_values(sign_extend_s24(source_buf[i]),
286 (int32_t *)read_val, expected_buf[i],
287 __func__);
288 assert_float_equal(*read_val, expected_buf[i], EPSILON);
289 }
290
291 /* free memory */
292 free_test_sink(sink);
293 }
294
test_pcm_convert_f_to_s24(void ** state)295 static void test_pcm_convert_f_to_s24(void **state)
296 {
297 typedef float Tin;
298 typedef int32_t Tout;
299 static Tin source_buf[] = {
300 PCM_TEST_FLOAT_NUMBERS,
301 INT16_MIN + 1, INT16_MIN,
302 INT16_MAX - 1, INT16_MAX,
303 INT24_MIN + 1, INT24_MIN,
304 INT24_MAX - 1, INT24_MAX,
305 INT24_MIN - 1, INT24_MAX + 1,
306 };
307 static const Tout expected_buf[] = {
308 PCM_TEST_INT_NUMBERS,
309 INT16_MIN + 1, INT16_MIN,
310 INT16_MAX - 1, INT16_MAX,
311 INT24_MIN + 1, INT24_MIN,
312 INT24_MAX - 1, INT24_MAX,
313 INT24_MIN, INT24_MAX,
314 };
315
316 struct comp_buffer *sink;
317 int i, N = ARRAY_SIZE(source_buf);
318 Tout *read_val;
319
320 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
321 scale_array(ratio24, source_buf, N);
322
323 /* run test */
324 sink = _test_pcm_convert(SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_S24_4LE,
325 N, source_buf);
326
327 /* check results */
328 for (i = 0; i < N; ++i) {
329 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
330 print_message("%2d/%02d ", i + 1, N);
331 pcm_float_print_values(*read_val, (int32_t *)&source_buf[i],
332 (float)expected_buf[i], __func__);
333 assert_int_equal(*read_val, expected_buf[i]);
334 }
335
336 /* free memory */
337 free_test_sink(sink);
338 }
339 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S24LE */
340
341 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S32LE
342 #define S24TOS32MULT (1 << (32 - 24))
test_pcm_convert_s32_to_f(void ** state)343 static void test_pcm_convert_s32_to_f(void **state)
344 {
345 typedef int32_t Tin;
346 typedef float Tout;
347
348 static const Tin source_buf[] = {
349 PCM_TEST_INT_NUMBERS,
350 INT16_MIN + 1, INT16_MIN,
351 INT16_MAX - 1, INT16_MAX,
352 INT24_MIN + 1, INT24_MIN,
353 INT24_MAX - 1, INT24_MAX,
354 INT32_MIN + 1, INT32_MIN,
355 INT32_MAX - 1, INT32_MAX,
356 };
357 static Tout expected_buf[] = {
358 PCM_TEST_INT_NUMBERS,
359 INT16_MIN + 1, INT16_MIN,
360 INT16_MAX - 1, INT16_MAX,
361 INT24_MIN + 1, INT24_MIN,
362 INT24_MAX - 1, INT24_MAX,
363 INT24_MIN*S24TOS32MULT, INT24_MIN * S24TOS32MULT,
364 INT24_MAX*S24TOS32MULT, INT24_MAX * S24TOS32MULT,
365 };
366
367 struct comp_buffer *sink;
368 int i, N = ARRAY_SIZE(source_buf);
369 Tout *read_val;
370
371 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
372 scale_array(ratio32, expected_buf, N);
373
374 /* run test */
375 sink = _test_pcm_convert(SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_FLOAT,
376 N, source_buf);
377
378 /* check results */
379 for (i = 0; i < N; ++i) {
380 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
381 print_message("%2d/%02d ", i + 1, N);
382 pcm_float_print_values(source_buf[i], (int32_t *)read_val,
383 expected_buf[i], __func__);
384 assert_float_equal(*read_val, expected_buf[i], EPSILON);
385 }
386
387 /* free memory */
388 free_test_sink(sink);
389 }
390
test_pcm_convert_f_to_s32(void ** state)391 static void test_pcm_convert_f_to_s32(void **state)
392 {
393 typedef float Tin;
394 typedef int32_t Tout;
395 static Tin source_buf[] = {
396 PCM_TEST_FLOAT_NUMBERS,
397 INT16_MIN + 1, INT16_MIN,
398 INT16_MAX - 1, INT16_MAX,
399 INT24_MIN + 1, INT24_MIN,
400 INT24_MAX - 1, INT24_MAX,
401 };
402 static const Tout expected_buf[] = {
403 PCM_TEST_INT_NUMBERS,
404 INT16_MIN + 1, INT16_MIN,
405 INT16_MAX - 1, INT16_MAX,
406 INT24_MIN + 1, INT24_MIN,
407 INT24_MAX - 1, INT24_MAX,
408 };
409
410 struct comp_buffer *sink;
411 int i, N = ARRAY_SIZE(source_buf);
412 Tout *read_val;
413
414 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
415 scale_array(ratio32, source_buf, N);
416
417 /* run test */
418 sink = _test_pcm_convert(SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_S32_LE,
419 N, source_buf);
420
421 /* check results */
422 for (i = 0; i < N; ++i) {
423 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
424 print_message("%2d/%02d ", i + 1, N);
425 pcm_float_print_values(*read_val, (int32_t *)&source_buf[i],
426 (float)expected_buf[i], __func__);
427 assert_int_equal(*read_val, expected_buf[i]);
428 }
429
430 /* free memory */
431 free_test_sink(sink);
432 }
433
test_pcm_convert_f_to_s32_big_neg(void ** state)434 static void test_pcm_convert_f_to_s32_big_neg(void **state)
435 {
436 typedef float Tin;
437 typedef int32_t Tout;
438 static Tin source_buf[] = {
439 INT24_MIN - 127, INT24_MIN - 128, /* 24B mantissa trimming */
440 INT32_MIN + 1, INT32_MIN, /* 24B mantissa trimming */
441 INT32_MIN * 2.f, INT32_MIN * 10.f,
442 -INFINITY
443 };
444 static const Tout expected_buf[] = {
445 INT24_MIN - 127, INT24_MIN - 128,
446 INT32_MIN + 1, INT32_MIN,
447 INT32_MIN, INT32_MIN,
448 INT32_MIN
449 };
450
451 struct comp_buffer *sink;
452 int i, N = ARRAY_SIZE(source_buf);
453 Tout *read_val;
454
455 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
456 scale_array(ratio32, source_buf, N);
457
458 /* run test */
459 sink = _test_pcm_convert(SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_S32_LE,
460 N, source_buf);
461
462 /* check results */
463 for (i = 0; i < N; ++i) {
464 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
465 print_message("%2d/%02d ", i + 1, N);
466 pcm_float_print_values(*read_val, (int32_t *)&source_buf[i],
467 (float)expected_buf[i], __func__);
468 /* assert as float because of possible rounding effects */
469 assert_float_equal(*read_val, expected_buf[i], 1);
470 }
471
472 /* free memory */
473 free_test_sink(sink);
474 }
475
test_pcm_convert_f_to_s32_big_pos(void ** state)476 static void test_pcm_convert_f_to_s32_big_pos(void **state)
477 {
478 typedef float Tin;
479 typedef int32_t Tout;
480 static Tin source_buf[] = {
481 INT24_MAX + 127, INT24_MAX + 128,
482 INT32_MAX - 255, INT32_MAX - 127,
483 INT32_MAX - 1, INT32_MAX,
484 INT32_MAX * 2.f, INT32_MAX * 10.f,
485 INFINITY,
486 };
487 /* remember about 24B mantissa trimming */
488 static const Tout expected_buf[] = {
489 INT24_MAX + 127, INT24_MAX + 128,
490 INT32_MAX - 254, INT32_MAX - 126,
491 INT32_MAX - 1, INT32_MAX,
492 INT32_MAX, INT32_MAX,
493 INT32_MAX,
494 };
495
496 struct comp_buffer *sink;
497 int i, N = ARRAY_SIZE(source_buf);
498 Tout *read_val;
499
500 assert_int_equal(ARRAY_SIZE(source_buf), ARRAY_SIZE(expected_buf));
501 scale_array(ratio32, source_buf, N);
502
503 /* run test */
504 sink = _test_pcm_convert(SOF_IPC_FRAME_FLOAT, SOF_IPC_FRAME_S32_LE,
505 N, source_buf);
506
507 /* check results */
508 for (i = 0; i < N; ++i) {
509 read_val = audio_stream_read_frag(&sink->stream, i, sizeof(Tout));
510 print_message("%2d/%02d ", i + 1, N);
511 pcm_float_print_values(*read_val, (int32_t *)&source_buf[i],
512 (float)expected_buf[i], __func__);
513 /* assert as float because of possible rounding effects */
514 assert_float_equal(*read_val, expected_buf[i], 1);
515 }
516
517 /* free memory */
518 free_test_sink(sink);
519 }
520 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S32LE */
521
main(void)522 int main(void)
523 {
524 const struct CMUnitTest tests[] = {
525 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S16LE
526 cmocka_unit_test(test_pcm_convert_s16_to_f),
527 cmocka_unit_test(test_pcm_convert_f_to_s16),
528 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S16LE */
529 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S24LE
530 cmocka_unit_test(test_pcm_convert_s24_to_f),
531 cmocka_unit_test(test_pcm_convert_s24_in_s32_to_f),
532 cmocka_unit_test(test_pcm_convert_f_to_s24),
533 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S24LE */
534 #if CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S32LE
535 cmocka_unit_test(test_pcm_convert_s32_to_f),
536 cmocka_unit_test(test_pcm_convert_f_to_s32),
537 cmocka_unit_test(test_pcm_convert_f_to_s32_big_neg),
538 cmocka_unit_test(test_pcm_convert_f_to_s32_big_pos),
539 #endif /* CONFIG_FORMAT_FLOAT && CONFIG_FORMAT_S32LE */
540 };
541
542 cmocka_set_message_output(CM_OUTPUT_TAP);
543
544 /* log number of converting functions for current configuration */
545 print_message("%s start tests, count(pcm_func_map)=%zu\n",
546 __FILE__, pcm_func_count);
547
548 return cmocka_run_group_tests(tests, NULL, NULL);
549 }
550