|
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 |
}
|