Blame axfer/xfer.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// xfer.c - receiver/transmiter of data frames.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "xfer.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
Packit Service a9274b
static const char *const xfer_type_labels[] = {
Packit Service a9274b
	[XFER_TYPE_LIBASOUND] = "libasound",
Packit Service a9274b
#if WITH_FFADO
Packit Service a9274b
	[XFER_TYPE_LIBFFADO] = "libffado",
Packit Service a9274b
#endif
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
enum xfer_type xfer_type_from_label(const char *label)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(xfer_type_labels); ++i) {
Packit Service a9274b
		if (!strcmp(xfer_type_labels[i], label))
Packit Service a9274b
			return i;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return XFER_TYPE_UNSUPPORTED;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
const char *xfer_label_from_type(enum xfer_type type)
Packit Service a9274b
{
Packit Service a9274b
	return xfer_type_labels[type];
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int xfer_context_init(struct xfer_context *xfer, enum xfer_type type,
Packit Service a9274b
		      snd_pcm_stream_t direction, int argc, char *const *argv)
Packit Service a9274b
{
Packit Service a9274b
	struct {
Packit Service a9274b
		enum xfer_type type;
Packit Service a9274b
		const struct xfer_data *data;
Packit Service a9274b
	} *entry, entries[] = {
Packit Service a9274b
		{XFER_TYPE_LIBASOUND, &xfer_libasound},
Packit Service a9274b
#if WITH_FFADO
Packit Service a9274b
		{XFER_TYPE_LIBFFADO, &xfer_libffado},
Packit Service a9274b
#endif
Packit Service a9274b
	};
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
	assert(direction >= SND_PCM_STREAM_PLAYBACK);
Packit Service a9274b
	assert(direction <= SND_PCM_STREAM_CAPTURE);
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
Packit Service a9274b
		if (entries[i].type == type)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
	if (i == ARRAY_SIZE(entries))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	entry = &entries[i];
Packit Service a9274b
Packit Service a9274b
	xfer->direction = direction;
Packit Service a9274b
	xfer->type = type;
Packit Service a9274b
	xfer->ops = &entry->data->ops;
Packit Service a9274b
Packit Service a9274b
	xfer->private_data = malloc(entry->data->private_size);
Packit Service a9274b
	if (xfer->private_data == NULL)
Packit Service a9274b
		return -ENOMEM;
Packit Service a9274b
	memset(xfer->private_data, 0, entry->data->private_size);
Packit Service a9274b
Packit Service a9274b
	err = xfer->ops->init(xfer, direction);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	err = xfer_options_parse_args(xfer, entry->data, argc, argv);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	return xfer->ops->validate_opts(xfer);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void xfer_context_destroy(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
Packit Service a9274b
	if (!xfer->ops)
Packit Service a9274b
		return;
Packit Service a9274b
Packit Service a9274b
	if (xfer->ops->destroy)
Packit Service a9274b
		xfer->ops->destroy(xfer);
Packit Service a9274b
	if (xfer->private_data)
Packit Service a9274b
		free(xfer->private_data);
Packit Service a9274b
Packit Service a9274b
	if (xfer->paths) {
Packit Service a9274b
		for (i = 0; i < xfer->path_count; ++i)
Packit Service a9274b
			free(xfer->paths[i]);
Packit Service a9274b
		free(xfer->paths);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	xfer->paths = NULL;
Packit Service a9274b
Packit Service a9274b
	free(xfer->sample_format_literal);
Packit Service a9274b
	xfer->sample_format_literal = NULL;
Packit Service a9274b
Packit Service a9274b
	free(xfer->cntr_format_literal);
Packit Service a9274b
	xfer->cntr_format_literal = NULL;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int xfer_context_pre_process(struct xfer_context *xfer,
Packit Service a9274b
			     snd_pcm_format_t *format,
Packit Service a9274b
			     unsigned int *samples_per_frame,
Packit Service a9274b
			     unsigned int *frames_per_second,
Packit Service a9274b
			     snd_pcm_access_t *access,
Packit Service a9274b
			     snd_pcm_uframes_t *frames_per_buffer)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
	assert(format);
Packit Service a9274b
	assert(samples_per_frame);
Packit Service a9274b
	assert(frames_per_second);
Packit Service a9274b
	assert(access);
Packit Service a9274b
	assert(frames_per_buffer);
Packit Service a9274b
Packit Service a9274b
	if (!xfer->ops)
Packit Service a9274b
		return -ENXIO;
Packit Service a9274b
Packit Service a9274b
	if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
Packit Service a9274b
		// For capture direction, use values in options if given.
Packit Service a9274b
		if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN)
Packit Service a9274b
			*format = xfer->sample_format;
Packit Service a9274b
		if (xfer->samples_per_frame > 0)
Packit Service a9274b
			*samples_per_frame = xfer->samples_per_frame;
Packit Service a9274b
		if (xfer->frames_per_second > 0)
Packit Service a9274b
			*frames_per_second = xfer->frames_per_second;
Packit Service a9274b
	} else if (xfer->direction == SND_PCM_STREAM_PLAYBACK) {
Packit Service a9274b
		// For playback direction, check values in given options so that
Packit Service a9274b
		// they don't mismatch to parameters from media container.
Packit Service a9274b
		if (*format != xfer->sample_format) {
Packit Service a9274b
			// Not initial value.
Packit Service a9274b
			if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN) {
Packit Service a9274b
				fprintf(stderr,
Packit Service a9274b
					"Sample format mismatch: %s is given "
Packit Service a9274b
					"but %s by files\n",
Packit Service a9274b
					snd_pcm_format_name(xfer->sample_format),
Packit Service a9274b
					snd_pcm_format_name(*format));
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		if (*samples_per_frame != xfer->samples_per_frame) {
Packit Service a9274b
			// Not initial value.
Packit Service a9274b
			if (xfer->samples_per_frame > 0) {
Packit Service a9274b
				fprintf(stderr,
Packit Service a9274b
					"The number of channels mismatch: %u "
Packit Service a9274b
					"is given but %u by files\n",
Packit Service a9274b
					xfer->samples_per_frame,
Packit Service a9274b
					*samples_per_frame);
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		if (*frames_per_second != xfer->frames_per_second) {
Packit Service a9274b
			// Not initial value.
Packit Service a9274b
			if (xfer->frames_per_second != 8000) {
Packit Service a9274b
				fprintf(stderr,
Packit Service a9274b
					"Sampling rate mismatch: %u is given "
Packit Service a9274b
					"but %u by files\n",
Packit Service a9274b
					xfer->frames_per_second,
Packit Service a9274b
					*frames_per_second);
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	err = xfer->ops->pre_process(xfer, format, samples_per_frame,
Packit Service a9274b
				     frames_per_second, access,
Packit Service a9274b
				     frames_per_buffer);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	assert(*format >= SND_PCM_FORMAT_S8);
Packit Service a9274b
	assert(*format <= SND_PCM_FORMAT_LAST);
Packit Service a9274b
	assert(*samples_per_frame > 0);
Packit Service a9274b
	assert(*frames_per_second > 0);
Packit Service a9274b
	assert(*access >= SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit Service a9274b
	assert(*access <= SND_PCM_ACCESS_LAST);
Packit Service a9274b
	assert(*frames_per_buffer > 0);
Packit Service a9274b
Packit Service a9274b
	xfer->sample_format = *format;
Packit Service a9274b
	xfer->samples_per_frame = *samples_per_frame;
Packit Service a9274b
	xfer->frames_per_second = *frames_per_second;
Packit Service a9274b
Packit Service a9274b
	if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
Packit Service a9274b
		err = xfer_options_fixup_paths(xfer);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (xfer->verbose > 1) {
Packit Service a9274b
		fprintf(stderr, "Transfer: %s\n",
Packit Service a9274b
			xfer_type_labels[xfer->type]);
Packit Service a9274b
		fprintf(stderr, "  access: %s\n",
Packit Service a9274b
			snd_pcm_access_name(*access));
Packit Service a9274b
		fprintf(stderr, "  sample format: %s\n",
Packit Service a9274b
			snd_pcm_format_name(*format));
Packit Service a9274b
		fprintf(stderr, "  bytes/sample: %u\n",
Packit Service a9274b
		       snd_pcm_format_physical_width(*format) / 8);
Packit Service a9274b
		fprintf(stderr, "  samples/frame: %u\n",
Packit Service a9274b
			*samples_per_frame);
Packit Service a9274b
		fprintf(stderr, "  frames/second: %u\n",
Packit Service a9274b
			*frames_per_second);
Packit Service a9274b
		fprintf(stderr, "  frames/buffer: %lu\n",
Packit Service a9274b
			*frames_per_buffer);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int xfer_context_process_frames(struct xfer_context *xfer,
Packit Service a9274b
				struct mapper_context *mapper,
Packit Service a9274b
				struct container_context *cntrs,
Packit Service a9274b
				unsigned int *frame_count)
Packit Service a9274b
{
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
	assert(mapper);
Packit Service a9274b
	assert(cntrs);
Packit Service a9274b
	assert(frame_count);
Packit Service a9274b
Packit Service a9274b
	if (!xfer->ops)
Packit Service a9274b
		return -ENXIO;
Packit Service a9274b
Packit Service a9274b
	return xfer->ops->process_frames(xfer, frame_count, mapper, cntrs);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void xfer_context_pause(struct xfer_context *xfer, bool enable)
Packit Service a9274b
{
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
Packit Service a9274b
	if (!xfer->ops)
Packit Service a9274b
		return;
Packit Service a9274b
Packit Service a9274b
	xfer->ops->pause(xfer, enable);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void xfer_context_post_process(struct xfer_context *xfer)
Packit Service a9274b
{
Packit Service a9274b
	assert(xfer);
Packit Service a9274b
Packit Service a9274b
	if (!xfer->ops)
Packit Service a9274b
		return;
Packit Service a9274b
Packit Service a9274b
	xfer->ops->post_process(xfer);
Packit Service a9274b
}