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