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