1// SPDX-License-Identifier: GPL-2.0
2//
3// container-io.c - a unit test for parser/builder of supported containers.
4//
5// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6//
7// Licensed under the terms of the GNU General Public License, version 2.
8
9#include <aconfig.h>
10#ifdef HAVE_MEMFD_CREATE
11#define _GNU_SOURCE
12#endif
13
14#include "../container.h"
15#include "../misc.h"
16
17#include "generator.h"
18
19#ifdef HAVE_MEMFD_CREATE
20#include <sys/mman.h>
21#endif
22
23#include <stdlib.h>
24#include <unistd.h>
25#include <stdbool.h>
26
27#include <assert.h>
28
29struct container_trial {
30	enum container_format format;
31
32	struct container_context cntr;
33	bool verbose;
34};
35
36static void test_builder(struct container_context *cntr, int fd,
37			 enum container_format format,
38			 snd_pcm_access_t access,
39			 snd_pcm_format_t sample_format,
40			 unsigned int samples_per_frame,
41			 unsigned int frames_per_second,
42			 void *frame_buffer, unsigned int frame_count,
43			 bool verbose)
44{
45	snd_pcm_format_t sample;
46	unsigned int channels;
47	unsigned int rate;
48	uint64_t max_frame_count;
49	unsigned int handled_frame_count;
50	uint64_t total_frame_count;
51	int err;
52
53	err = container_builder_init(cntr, fd, format, verbose);
54	assert(err == 0);
55
56	sample = sample_format;
57	channels = samples_per_frame;
58	rate = frames_per_second;
59	max_frame_count = 0;
60	err = container_context_pre_process(cntr, &sample, &channels, &rate,
61					    &max_frame_count);
62	assert(err == 0);
63	assert(sample == sample_format);
64	assert(channels == samples_per_frame);
65	assert(rate == frames_per_second);
66	assert(max_frame_count > 0);
67
68	handled_frame_count = frame_count;
69	err = container_context_process_frames(cntr, frame_buffer,
70					       &handled_frame_count);
71	assert(err == 0);
72	assert(handled_frame_count > 0);
73	assert(handled_frame_count <= frame_count);
74
75	total_frame_count = 0;
76	err = container_context_post_process(cntr, &total_frame_count);
77	assert(err == 0);
78	assert(total_frame_count == frame_count);
79
80	container_context_destroy(cntr);
81}
82
83static void test_parser(struct container_context *cntr, int fd,
84			enum container_format format,
85		        snd_pcm_access_t access, snd_pcm_format_t sample_format,
86		        unsigned int samples_per_frame,
87		        unsigned int frames_per_second,
88		        void *frame_buffer, unsigned int frame_count,
89			bool verbose)
90{
91	snd_pcm_format_t sample;
92	unsigned int channels;
93	unsigned int rate;
94	uint64_t total_frame_count;
95	unsigned int handled_frame_count;
96	int err;
97
98	err = container_parser_init(cntr, fd, verbose);
99	assert(err == 0);
100
101	sample = sample_format;
102	channels = samples_per_frame;
103	rate = frames_per_second;
104	total_frame_count = 0;
105	err = container_context_pre_process(cntr, &sample, &channels, &rate,
106					    &total_frame_count);
107	assert(err == 0);
108	assert(sample == sample_format);
109	assert(channels == samples_per_frame);
110	assert(rate == frames_per_second);
111	assert(total_frame_count == frame_count);
112
113	handled_frame_count = total_frame_count;
114	err = container_context_process_frames(cntr, frame_buffer,
115					       &handled_frame_count);
116	assert(err == 0);
117	assert(handled_frame_count == frame_count);
118
119	total_frame_count = 0;
120	err = container_context_post_process(cntr, &total_frame_count);
121	assert(err == 0);
122	assert(total_frame_count == handled_frame_count);
123
124	container_context_destroy(cntr);
125}
126
127static int callback(struct test_generator *gen, snd_pcm_access_t access,
128		    snd_pcm_format_t sample_format,
129		    unsigned int samples_per_frame, void *frame_buffer,
130		    unsigned int frame_count)
131{
132	static const unsigned int entries[] = {
133		[0] = 44100,
134		[1] = 48000,
135		[2] = 88200,
136		[3] = 96000,
137		[4] = 176400,
138		[5] = 192000,
139	};
140	struct container_trial *trial = gen->private_data;
141	unsigned int frames_per_second;
142	const char *const name = "hoge";
143	unsigned int size;
144	void *buf;
145	int i;
146	int err = 0;
147
148	size = frame_count * samples_per_frame *
149			snd_pcm_format_physical_width(sample_format) / 8;
150	buf = malloc(size);
151	if (buf == NULL)
152		return -ENOMEM;
153
154	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
155		int fd;
156		off_t pos;
157
158		frames_per_second = entries[i];
159
160#ifdef HAVE_MEMFD_CREATE
161		fd = memfd_create(name, 0);
162#else
163		fd = open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
164#endif
165		if (fd < 0) {
166			err = -errno;
167			break;
168		}
169
170		test_builder(&trial->cntr, fd, trial->format, access,
171			     sample_format, samples_per_frame,
172			     frames_per_second, frame_buffer, frame_count,
173			     trial->verbose);
174
175		pos = lseek(fd, 0, SEEK_SET);
176		if (pos < 0) {
177			err = -errno;
178			break;
179		}
180
181		test_parser(&trial->cntr, fd, trial->format, access,
182			    sample_format, samples_per_frame, frames_per_second,
183			    buf, frame_count, trial->verbose);
184
185		err = memcmp(buf, frame_buffer, size);
186		assert(err == 0);
187
188		close(fd);
189	}
190
191	free(buf);
192
193	return err;
194}
195
196int main(int argc, const char *argv[])
197{
198	static const uint64_t sample_format_masks[] = {
199		[CONTAINER_FORMAT_RIFF_WAVE] =
200			(1ull << SND_PCM_FORMAT_U8) |
201			(1ull << SND_PCM_FORMAT_S16_LE) |
202			(1ull << SND_PCM_FORMAT_S16_BE) |
203			(1ull << SND_PCM_FORMAT_S24_LE) |
204			(1ull << SND_PCM_FORMAT_S24_BE) |
205			(1ull << SND_PCM_FORMAT_S32_LE) |
206			(1ull << SND_PCM_FORMAT_S32_BE) |
207			(1ull << SND_PCM_FORMAT_FLOAT_LE) |
208			(1ull << SND_PCM_FORMAT_FLOAT_BE) |
209			(1ull << SND_PCM_FORMAT_FLOAT64_LE) |
210			(1ull << SND_PCM_FORMAT_FLOAT64_BE) |
211			(1ull << SND_PCM_FORMAT_MU_LAW) |
212			(1ull << SND_PCM_FORMAT_A_LAW) |
213			(1ull << SND_PCM_FORMAT_S24_3LE) |
214			(1ull << SND_PCM_FORMAT_S24_3BE) |
215			(1ull << SND_PCM_FORMAT_S20_3LE) |
216			(1ull << SND_PCM_FORMAT_S20_3BE) |
217			(1ull << SND_PCM_FORMAT_S18_3LE) |
218			(1ull << SND_PCM_FORMAT_S18_3BE),
219		[CONTAINER_FORMAT_AU] =
220			(1ull << SND_PCM_FORMAT_S8) |
221			(1ull << SND_PCM_FORMAT_S16_BE) |
222			(1ull << SND_PCM_FORMAT_S32_BE) |
223			(1ull << SND_PCM_FORMAT_FLOAT_BE) |
224			(1ull << SND_PCM_FORMAT_FLOAT64_BE) |
225			(1ull << SND_PCM_FORMAT_MU_LAW) |
226			(1ull << SND_PCM_FORMAT_A_LAW),
227		[CONTAINER_FORMAT_VOC] =
228			(1ull << SND_PCM_FORMAT_U8) |
229			(1ull << SND_PCM_FORMAT_S16_LE) |
230			(1ull << SND_PCM_FORMAT_MU_LAW) |
231			(1ull << SND_PCM_FORMAT_A_LAW),
232		[CONTAINER_FORMAT_RAW] =
233			(1ull << SND_PCM_FORMAT_S8) |
234			(1ull << SND_PCM_FORMAT_U8) |
235			(1ull << SND_PCM_FORMAT_S16_LE) |
236			(1ull << SND_PCM_FORMAT_S16_BE) |
237			(1ull << SND_PCM_FORMAT_U16_LE) |
238			(1ull << SND_PCM_FORMAT_U16_BE) |
239			(1ull << SND_PCM_FORMAT_S24_LE) |
240			(1ull << SND_PCM_FORMAT_S24_BE) |
241			(1ull << SND_PCM_FORMAT_U24_LE) |
242			(1ull << SND_PCM_FORMAT_U24_BE) |
243			(1ull << SND_PCM_FORMAT_S32_LE) |
244			(1ull << SND_PCM_FORMAT_S32_BE) |
245			(1ull << SND_PCM_FORMAT_U32_LE) |
246			(1ull << SND_PCM_FORMAT_U32_BE) |
247			(1ull << SND_PCM_FORMAT_FLOAT_LE) |
248			(1ull << SND_PCM_FORMAT_FLOAT_BE) |
249			(1ull << SND_PCM_FORMAT_FLOAT64_LE) |
250			(1ull << SND_PCM_FORMAT_FLOAT64_BE) |
251			(1ull << SND_PCM_FORMAT_IEC958_SUBFRAME_LE) |
252			(1ull << SND_PCM_FORMAT_IEC958_SUBFRAME_BE) |
253			(1ull << SND_PCM_FORMAT_MU_LAW) |
254			(1ull << SND_PCM_FORMAT_A_LAW) |
255			(1ull << SND_PCM_FORMAT_S24_3LE) |
256			(1ull << SND_PCM_FORMAT_S24_3BE) |
257			(1ull << SND_PCM_FORMAT_U24_3LE) |
258			(1ull << SND_PCM_FORMAT_U24_3BE) |
259			(1ull << SND_PCM_FORMAT_S20_3LE) |
260			(1ull << SND_PCM_FORMAT_S20_3BE) |
261			(1ull << SND_PCM_FORMAT_U20_3LE) |
262			(1ull << SND_PCM_FORMAT_U20_3BE) |
263			(1ull << SND_PCM_FORMAT_S18_3LE) |
264			(1ull << SND_PCM_FORMAT_S18_3BE) |
265			(1ull << SND_PCM_FORMAT_U18_3LE) |
266			(1ull << SND_PCM_FORMAT_U18_3BE) |
267			(1ull << SND_PCM_FORMAT_DSD_U8) |
268			(1ull << SND_PCM_FORMAT_DSD_U16_LE) |
269			(1ull << SND_PCM_FORMAT_DSD_U32_LE) |
270			(1ull << SND_PCM_FORMAT_DSD_U16_BE) |
271			(1ull << SND_PCM_FORMAT_DSD_U32_BE),
272	};
273	static const uint64_t access_mask =
274		(1ull << SND_PCM_ACCESS_MMAP_INTERLEAVED) |
275		(1ull << SND_PCM_ACCESS_RW_INTERLEAVED);
276	struct test_generator gen = {0};
277	struct container_trial *trial;
278	int i;
279	int begin;
280	int end;
281	bool verbose;
282	int err;
283
284	if (argc > 1) {
285		char *term;
286		begin = strtol(argv[1], &term, 10);
287		if (errno || *term != '\0')
288			return EXIT_FAILURE;
289		if (begin < CONTAINER_FORMAT_RIFF_WAVE &&
290		    begin > CONTAINER_FORMAT_RAW)
291			return -EXIT_FAILURE;
292		end = begin + 1;
293		verbose = true;
294	} else {
295		begin = CONTAINER_FORMAT_RIFF_WAVE;
296		end = CONTAINER_FORMAT_RAW + 1;
297		verbose = false;
298	}
299
300	for (i = begin; i < end; ++i) {
301		err = generator_context_init(&gen, access_mask,
302					     sample_format_masks[i],
303					     1, 32, 23, 3000, 512,
304					     sizeof(struct container_trial));
305		if (err >= 0) {
306			trial = gen.private_data;
307			trial->format = i;
308			trial->verbose = verbose;
309			err = generator_context_run(&gen, callback);
310		}
311
312		generator_context_destroy(&gen);
313
314		if (err < 0)
315			break;
316	}
317
318	if (err < 0) {
319		printf("%s\n", strerror(-err));
320		return EXIT_FAILURE;
321	}
322
323	return EXIT_SUCCESS;
324}
325