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