xref: /third_party/alsa-utils/axfer/xfer.c (revision c72fcc34)
1
2//
3// xfer.c - receiver/transmiter of data frames.
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 "xfer.h"
10#include "misc.h"
11
12#include <stdio.h>
13
14static const char *const xfer_type_labels[] = {
15	[XFER_TYPE_LIBASOUND] = "libasound",
16#if WITH_FFADO
17	[XFER_TYPE_LIBFFADO] = "libffado",
18#endif
19};
20
21enum xfer_type xfer_type_from_label(const char *label)
22{
23	unsigned int i;
24
25	for (i = 0; i < ARRAY_SIZE(xfer_type_labels); ++i) {
26		if (!strcmp(xfer_type_labels[i], label))
27			return i;
28	}
29
30	return XFER_TYPE_UNSUPPORTED;
31}
32
33const char *xfer_label_from_type(enum xfer_type type)
34{
35	return xfer_type_labels[type];
36}
37
38int xfer_context_init(struct xfer_context *xfer, enum xfer_type type,
39		      snd_pcm_stream_t direction, int argc, char *const *argv)
40{
41	struct {
42		enum xfer_type type;
43		const struct xfer_data *data;
44	} *entry, entries[] = {
45		{XFER_TYPE_LIBASOUND, &xfer_libasound},
46#if WITH_FFADO
47		{XFER_TYPE_LIBFFADO, &xfer_libffado},
48#endif
49	};
50	unsigned int i;
51	int err;
52
53	assert(xfer);
54	assert(direction >= SND_PCM_STREAM_PLAYBACK);
55	assert(direction <= SND_PCM_STREAM_CAPTURE);
56
57	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
58		if (entries[i].type == type)
59			break;
60	}
61	if (i == ARRAY_SIZE(entries))
62		return -EINVAL;
63	entry = &entries[i];
64
65	xfer->direction = direction;
66	xfer->type = type;
67	xfer->ops = &entry->data->ops;
68
69	xfer->private_data = malloc(entry->data->private_size);
70	if (xfer->private_data == NULL)
71		return -ENOMEM;
72	memset(xfer->private_data, 0, entry->data->private_size);
73
74	err = xfer->ops->init(xfer, direction);
75	if (err < 0)
76		return err;
77
78	err = xfer_options_parse_args(xfer, entry->data, argc, argv);
79	if (err < 0)
80		return err;
81
82	return xfer->ops->validate_opts(xfer);
83}
84
85void xfer_context_destroy(struct xfer_context *xfer)
86{
87	unsigned int i;
88
89	assert(xfer);
90
91	if (!xfer->ops)
92		return;
93
94	if (xfer->ops->destroy)
95		xfer->ops->destroy(xfer);
96	if (xfer->private_data)
97		free(xfer->private_data);
98
99	if (xfer->paths) {
100		for (i = 0; i < xfer->path_count; ++i)
101			free(xfer->paths[i]);
102		free(xfer->paths);
103	}
104
105	xfer->paths = NULL;
106
107	free(xfer->sample_format_literal);
108	xfer->sample_format_literal = NULL;
109
110	free(xfer->cntr_format_literal);
111	xfer->cntr_format_literal = NULL;
112}
113
114int xfer_context_pre_process(struct xfer_context *xfer,
115			     snd_pcm_format_t *format,
116			     unsigned int *samples_per_frame,
117			     unsigned int *frames_per_second,
118			     snd_pcm_access_t *access,
119			     snd_pcm_uframes_t *frames_per_buffer)
120{
121	int err;
122
123	assert(xfer);
124	assert(format);
125	assert(samples_per_frame);
126	assert(frames_per_second);
127	assert(access);
128	assert(frames_per_buffer);
129
130	if (!xfer->ops)
131		return -ENXIO;
132
133	if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
134		// For capture direction, use values in options if given.
135		if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN)
136			*format = xfer->sample_format;
137		if (xfer->samples_per_frame > 0)
138			*samples_per_frame = xfer->samples_per_frame;
139		if (xfer->frames_per_second > 0)
140			*frames_per_second = xfer->frames_per_second;
141	} else if (xfer->direction == SND_PCM_STREAM_PLAYBACK) {
142		// For playback direction, check values in given options so that
143		// they don't mismatch to parameters from media container.
144		if (*format != xfer->sample_format) {
145			// Not initial value.
146			if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN) {
147				fprintf(stderr,
148					"Sample format mismatch: %s is given "
149					"but %s by files\n",
150					snd_pcm_format_name(xfer->sample_format),
151					snd_pcm_format_name(*format));
152				return -EINVAL;
153			}
154		}
155
156		if (*samples_per_frame != xfer->samples_per_frame) {
157			// Not initial value.
158			if (xfer->samples_per_frame > 0) {
159				fprintf(stderr,
160					"The number of channels mismatch: %u "
161					"is given but %u by files\n",
162					xfer->samples_per_frame,
163					*samples_per_frame);
164				return -EINVAL;
165			}
166		}
167
168		if (*frames_per_second != xfer->frames_per_second) {
169			// Not initial value.
170			if (xfer->frames_per_second != 8000) {
171				fprintf(stderr,
172					"Sampling rate mismatch: %u is given "
173					"but %u by files\n",
174					xfer->frames_per_second,
175					*frames_per_second);
176				return -EINVAL;
177			}
178		}
179	}
180
181	err = xfer->ops->pre_process(xfer, format, samples_per_frame,
182				     frames_per_second, access,
183				     frames_per_buffer);
184	if (err < 0)
185		return err;
186
187	assert(*format >= SND_PCM_FORMAT_S8);
188	assert(*format <= SND_PCM_FORMAT_LAST);
189	assert(*samples_per_frame > 0);
190	assert(*frames_per_second > 0);
191	assert(*access >= SND_PCM_ACCESS_MMAP_INTERLEAVED);
192	assert(*access <= SND_PCM_ACCESS_LAST);
193	assert(*frames_per_buffer > 0);
194
195	xfer->sample_format = *format;
196	xfer->samples_per_frame = *samples_per_frame;
197	xfer->frames_per_second = *frames_per_second;
198
199	if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
200		err = xfer_options_fixup_paths(xfer);
201		if (err < 0)
202			return err;
203	}
204
205	if (xfer->verbose > 1) {
206		fprintf(stderr, "Transfer: %s\n",
207			xfer_type_labels[xfer->type]);
208		fprintf(stderr, "  access: %s\n",
209			snd_pcm_access_name(*access));
210		fprintf(stderr, "  sample format: %s\n",
211			snd_pcm_format_name(*format));
212		fprintf(stderr, "  bytes/sample: %u\n",
213		       snd_pcm_format_physical_width(*format) / 8);
214		fprintf(stderr, "  samples/frame: %u\n",
215			*samples_per_frame);
216		fprintf(stderr, "  frames/second: %u\n",
217			*frames_per_second);
218		fprintf(stderr, "  frames/buffer: %lu\n",
219			*frames_per_buffer);
220	}
221
222	return 0;
223}
224
225int xfer_context_process_frames(struct xfer_context *xfer,
226				struct mapper_context *mapper,
227				struct container_context *cntrs,
228				unsigned int *frame_count)
229{
230	assert(xfer);
231	assert(mapper);
232	assert(cntrs);
233	assert(frame_count);
234
235	if (!xfer->ops)
236		return -ENXIO;
237
238	return xfer->ops->process_frames(xfer, frame_count, mapper, cntrs);
239}
240
241void xfer_context_pause(struct xfer_context *xfer, bool enable)
242{
243	assert(xfer);
244
245	if (!xfer->ops)
246		return;
247
248	xfer->ops->pause(xfer, enable);
249}
250
251void xfer_context_post_process(struct xfer_context *xfer)
252{
253	assert(xfer);
254
255	if (!xfer->ops)
256		return;
257
258	xfer->ops->post_process(xfer);
259}
260