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