mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-09 18:15:41 +01:00
a5f21d610d
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>
191 lines
5.1 KiB
C
191 lines
5.1 KiB
C
// 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),
|
|
};
|