1// SPDX-License-Identifier: GPL-2.0
2//
3// mapper-single.c - a muxer/demuxer for single containers.
4//
5// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6//
7// Licensed under the terms of the GNU General Public License, version 2.
8
9#include "mapper.h"
10#include "misc.h"
11
12struct single_state {
13	void (*align_frames)(void *frame_buf, unsigned int frame_count,
14			     char *buf, unsigned int bytes_per_sample,
15			     unsigned int samples_per_frame);
16	char *buf;
17};
18
19static void align_to_vector(void *frame_buf, unsigned int frame_count,
20			    char *src, unsigned int bytes_per_sample,
21			    unsigned samples_per_frame)
22{
23	char **dst_bufs = frame_buf;
24	char *dst;
25	unsigned int src_pos;
26	unsigned int dst_pos;
27	unsigned int i, j;
28
29	// src: interleaved => dst: a set of interleaved buffers.
30	for (i = 0; i < samples_per_frame; ++i) {
31		dst = dst_bufs[i];
32		for (j = 0; j < frame_count; ++j) {
33			src_pos = bytes_per_sample * (samples_per_frame * j + i);
34			dst_pos = bytes_per_sample * j;
35
36			memcpy(dst + dst_pos, src + src_pos, bytes_per_sample);
37		}
38	}
39}
40
41static void align_from_vector(void *frame_buf, unsigned int frame_count,
42			      char *dst, unsigned int bytes_per_sample,
43			      unsigned int samples_per_frame)
44{
45	char **src_bufs = frame_buf;
46	char *src;
47	unsigned int dst_pos;
48	unsigned int src_pos;
49	unsigned int i, j;
50
51	// src: a set of interleaved buffers => dst:interleaved.
52	for (i = 0; i < samples_per_frame; ++i) {
53		src = src_bufs[i];
54		for (j = 0; j < frame_count; ++j) {
55			src_pos = bytes_per_sample * j;
56			dst_pos = bytes_per_sample * (samples_per_frame * j + i);
57
58			memcpy(dst + dst_pos, src + src_pos, bytes_per_sample);
59		}
60	}
61}
62
63static int single_pre_process(struct mapper_context *mapper,
64			      struct container_context *cntrs,
65			      unsigned int cntr_count ATTRIBUTE_UNUSED)
66{
67	struct single_state *state = mapper->private_data;
68	unsigned int bytes_per_buffer;
69
70	if (cntrs->bytes_per_sample != mapper->bytes_per_sample ||
71	    cntrs->samples_per_frame != mapper->samples_per_frame)
72		return -EINVAL;
73
74	// Decide method to align frames.
75	if (mapper->type == MAPPER_TYPE_DEMUXER) {
76		if (mapper->access == SND_PCM_ACCESS_RW_NONINTERLEAVED ||
77		    mapper->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
78			state->align_frames = align_from_vector;
79		else if (mapper->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
80			 mapper->access == SND_PCM_ACCESS_MMAP_INTERLEAVED)
81			state->align_frames = NULL;
82		else
83			return -EINVAL;
84	} else {
85		if (mapper->access == SND_PCM_ACCESS_RW_NONINTERLEAVED ||
86		    mapper->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
87			state->align_frames = align_to_vector;
88		else if (mapper->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
89			 mapper->access == SND_PCM_ACCESS_MMAP_INTERLEAVED)
90			state->align_frames = NULL;
91		else
92			return -EINVAL;
93	}
94
95	if (state->align_frames) {
96		// Allocate intermediate buffer as the same size as a period.
97		bytes_per_buffer = mapper->bytes_per_sample *
98				   mapper->samples_per_frame *
99				   mapper->frames_per_buffer;
100		state->buf = malloc(bytes_per_buffer);
101		if (state->buf == NULL)
102			return -ENOMEM;
103		memset(state->buf, 0, bytes_per_buffer);
104	}
105
106	return 0;
107}
108
109static int single_muxer_process_frames(struct mapper_context *mapper,
110				       void *frame_buf,
111				       unsigned int *frame_count,
112				       struct container_context *cntrs,
113				       unsigned int cntr_count ATTRIBUTE_UNUSED)
114{
115	struct single_state *state = mapper->private_data;
116	void *src;
117	int err;
118
119	// If need to align PCM frames, process PCM frames to the intermediate
120	// buffer once.
121	if (!state->align_frames) {
122		// The most likely.
123		src = frame_buf;
124	} else {
125		src = state->buf;
126	}
127	err = container_context_process_frames(cntrs, src, frame_count);
128	if (err < 0)
129		return err;
130
131	// Unlikely.
132	if (src != frame_buf && *frame_count > 0)
133		state->align_frames(frame_buf, *frame_count, src,
134				    mapper->bytes_per_sample,
135				    mapper->samples_per_frame);
136
137	return 0;
138}
139
140static int single_demuxer_process_frames(struct mapper_context *mapper,
141					 void *frame_buf,
142					 unsigned int *frame_count,
143					 struct container_context *cntrs,
144					 unsigned int cntr_count ATTRIBUTE_UNUSED)
145{
146	struct single_state *state = mapper->private_data;
147	void *dst;
148
149	// If need to align PCM frames, process PCM frames to the intermediate
150	// buffer once.
151	if (!state->align_frames) {
152		// The most likely.
153		dst = frame_buf;
154	} else {
155		state->align_frames(frame_buf, *frame_count, state->buf,
156				    mapper->bytes_per_sample,
157				    mapper->samples_per_frame);
158		dst = state->buf;
159	}
160
161	return container_context_process_frames(cntrs, dst, frame_count);
162}
163
164static void single_post_process(struct mapper_context *mapper)
165{
166	struct single_state *state = mapper->private_data;
167
168	if (state->buf)
169		free(state->buf);
170
171	state->buf = NULL;
172	state->align_frames = NULL;
173}
174
175const struct mapper_data mapper_muxer_single = {
176	.ops = {
177		.pre_process = single_pre_process,
178		.process_frames = single_muxer_process_frames,
179		.post_process = single_post_process,
180	},
181	.private_size = sizeof(struct single_state),
182};
183
184const struct mapper_data mapper_demuxer_single = {
185	.ops = {
186		.pre_process = single_pre_process,
187		.process_frames = single_demuxer_process_frames,
188		.post_process = single_post_process,
189	},
190	.private_size = sizeof(struct single_state),
191};
192