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