1 // SPDX-License-Identifier: GPL-2.0
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 
14 static const char *const xfer_type_labels[] = {
15 	[XFER_TYPE_LIBASOUND] = "libasound",
16 #if WITH_FFADO
17 	[XFER_TYPE_LIBFFADO] = "libffado",
18 #endif
19 };
20 
xfer_type_from_label(const char * label)21 enum xfer_type xfer_type_from_label(const char *label)
22 {
23 	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 
xfer_label_from_type(enum xfer_type type)33 const char *xfer_label_from_type(enum xfer_type type)
34 {
35 	return xfer_type_labels[type];
36 }
37 
xfer_context_init(struct xfer_context * xfer,enum xfer_type type,snd_pcm_stream_t direction,int argc,char * const * argv)38 int 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 	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 
xfer_context_destroy(struct xfer_context * xfer)85 void xfer_context_destroy(struct xfer_context *xfer)
86 {
87 	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 
xfer_context_pre_process(struct xfer_context * xfer,snd_pcm_format_t * format,unsigned int * samples_per_frame,unsigned int * frames_per_second,snd_pcm_access_t * access,snd_pcm_uframes_t * frames_per_buffer)114 int 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 
xfer_context_process_frames(struct xfer_context * xfer,struct mapper_context * mapper,struct container_context * cntrs,unsigned int * frame_count)225 int 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 
xfer_context_pause(struct xfer_context * xfer,bool enable)241 void 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 
xfer_context_post_process(struct xfer_context * xfer)251 void 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