axfer: add support for a mapper for single target

In usual use case of aplay, single file is used to playback or capture data
frames.

This commit adds support of single type mapper for this use case. All of
supported file format can include data frame with interleaved alignment,
thus this mapper have a functionality to convert from several types of
data frame alignment to interleaved alignment or vise versa. When
handling non-interleaved buffer, a caller should use an array of buffer
for each of channels with non-interleaved data frames.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2018-11-13 15:41:22 +09:00 committed by Takashi Iwai
parent 58c83ae239
commit a5f21d610d
4 changed files with 210 additions and 2 deletions

View file

@ -32,4 +32,5 @@ axfer_SOURCES = \
container-voc.c \
container-raw.c \
mapper.h \
mapper.c
mapper.c \
mapper-single.c

191
axfer/mapper-single.c Normal file
View file

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0
//
// mapper-single.c - a muxer/demuxer for single containers.
//
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
//
// Licensed under the terms of the GNU General Public License, version 2.
#include "mapper.h"
#include "misc.h"
struct single_state {
void (*align_frames)(void *frame_buf, unsigned int frame_count,
char *buf, unsigned int bytes_per_sample,
unsigned int samples_per_frame);
char *buf;
};
static void align_to_vector(void *frame_buf, unsigned int frame_count,
char *src, unsigned int bytes_per_sample,
unsigned samples_per_frame)
{
char **dst_bufs = frame_buf;
char *dst;
unsigned int src_pos;
unsigned int dst_pos;
int i, j;
// src: interleaved => dst: a set of interleaved buffers.
for (i = 0; i < samples_per_frame; ++i) {
dst = dst_bufs[i];
for (j = 0; j < frame_count; ++j) {
src_pos = bytes_per_sample * (samples_per_frame * j + i);
dst_pos = bytes_per_sample * j;
memcpy(dst + dst_pos, src + src_pos, bytes_per_sample);
}
}
}
static void align_from_vector(void *frame_buf, unsigned int frame_count,
char *dst, unsigned int bytes_per_sample,
unsigned int samples_per_frame)
{
char **src_bufs = frame_buf;
char *src;
unsigned int dst_pos;
unsigned int src_pos;
int i, j;
// src: a set of interleaved buffers => dst:interleaved.
for (i = 0; i < samples_per_frame; ++i) {
src = src_bufs[i];
for (j = 0; j < frame_count; ++j) {
src_pos = bytes_per_sample * j;
dst_pos = bytes_per_sample * (samples_per_frame * j + i);
memcpy(dst + dst_pos, src + src_pos, bytes_per_sample);
}
}
}
static int single_pre_process(struct mapper_context *mapper,
struct container_context *cntrs,
unsigned int cntr_count)
{
struct single_state *state = mapper->private_data;
unsigned int bytes_per_buffer;
if (cntrs->bytes_per_sample != mapper->bytes_per_sample ||
cntrs->samples_per_frame != mapper->samples_per_frame)
return -EINVAL;
// Decide method to align frames.
if (mapper->type == MAPPER_TYPE_DEMUXER) {
if (mapper->access == SND_PCM_ACCESS_RW_NONINTERLEAVED ||
mapper->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
state->align_frames = align_from_vector;
else if (mapper->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
mapper->access == SND_PCM_ACCESS_MMAP_INTERLEAVED)
state->align_frames = NULL;
else
return -EINVAL;
} else {
if (mapper->access == SND_PCM_ACCESS_RW_NONINTERLEAVED ||
mapper->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
state->align_frames = align_to_vector;
else if (mapper->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
mapper->access == SND_PCM_ACCESS_MMAP_INTERLEAVED)
state->align_frames = NULL;
else
return -EINVAL;
}
if (state->align_frames) {
// Allocate intermediate buffer as the same size as a period.
bytes_per_buffer = mapper->bytes_per_sample *
mapper->samples_per_frame *
mapper->frames_per_buffer;
state->buf = malloc(bytes_per_buffer);
if (state->buf == NULL)
return -ENOMEM;
memset(state->buf, 0, bytes_per_buffer);
}
return 0;
}
static int single_muxer_process_frames(struct mapper_context *mapper,
void *frame_buf,
unsigned int *frame_count,
struct container_context *cntrs,
unsigned int cntr_count)
{
struct single_state *state = mapper->private_data;
void *src;
int err;
// If need to align PCM frames, process PCM frames to the intermediate
// buffer once.
if (!state->align_frames) {
// The most likely.
src = frame_buf;
} else {
src = state->buf;
}
err = container_context_process_frames(cntrs, src, frame_count);
if (err < 0)
return err;
// Unlikely.
if (src != frame_buf && *frame_count > 0)
state->align_frames(frame_buf, *frame_count, src,
mapper->bytes_per_sample,
mapper->samples_per_frame);
return 0;
}
static int single_demuxer_process_frames(struct mapper_context *mapper,
void *frame_buf,
unsigned int *frame_count,
struct container_context *cntrs,
unsigned int cntr_count)
{
struct single_state *state = mapper->private_data;
void *dst;
// If need to align PCM frames, process PCM frames to the intermediate
// buffer once.
if (!state->align_frames) {
// The most likely.
dst = frame_buf;
} else {
state->align_frames(frame_buf, *frame_count, state->buf,
mapper->bytes_per_sample,
mapper->samples_per_frame);
dst = state->buf;
}
return container_context_process_frames(cntrs, dst, frame_count);
}
static void single_post_process(struct mapper_context *mapper)
{
struct single_state *state = mapper->private_data;
if (state->buf)
free(state->buf);
state->buf = NULL;
state->align_frames = NULL;
}
const struct mapper_data mapper_muxer_single = {
.ops = {
.pre_process = single_pre_process,
.process_frames = single_muxer_process_frames,
.post_process = single_post_process,
},
.private_size = sizeof(struct single_state),
};
const struct mapper_data mapper_demuxer_single = {
.ops = {
.pre_process = single_pre_process,
.process_frames = single_demuxer_process_frames,
.post_process = single_post_process,
},
.private_size = sizeof(struct single_state),
};

View file

@ -18,7 +18,7 @@ static const char *const mapper_type_labels[] = {
};
static const char *const mapper_target_labels[] = {
[MAPPER_TARGET_COUNT] = "",
[MAPPER_TARGET_SINGLE] = "single",
};
int mapper_context_init(struct mapper_context *mapper,
@ -35,6 +35,18 @@ int mapper_context_init(struct mapper_context *mapper,
memset(mapper, 0, sizeof(*mapper));
if (type == MAPPER_TYPE_MUXER) {
if (cntr_count == 1) {
data = &mapper_muxer_single;
mapper->target = MAPPER_TARGET_SINGLE;
}
} else {
if (cntr_count == 1) {
data = &mapper_demuxer_single;
mapper->target = MAPPER_TARGET_SINGLE;
}
}
mapper->ops = &data->ops;
mapper->type = type;

View file

@ -19,6 +19,7 @@ enum mapper_type {
};
enum mapper_target {
MAPPER_TARGET_SINGLE = 0,
MAPPER_TARGET_COUNT,
};
@ -76,4 +77,7 @@ struct mapper_data {
unsigned int private_size;
};
extern const struct mapper_data mapper_muxer_single;
extern const struct mapper_data mapper_demuxer_single;
#endif