mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 00:35:42 +01:00
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:
parent
58c83ae239
commit
a5f21d610d
4 changed files with 210 additions and 2 deletions
|
@ -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
191
axfer/mapper-single.c
Normal 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),
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue