1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file pcm/pcm_route.c
3d5ac70f0Sopenharmony_ci * \ingroup PCM_Plugins
4d5ac70f0Sopenharmony_ci * \brief PCM Route & Volume Plugin Interface
5d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org>
6d5ac70f0Sopenharmony_ci * \date 2000-2001
7d5ac70f0Sopenharmony_ci */
8d5ac70f0Sopenharmony_ci/*
9d5ac70f0Sopenharmony_ci *  PCM - Route & Volume Plugin
10d5ac70f0Sopenharmony_ci *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11d5ac70f0Sopenharmony_ci *
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
14d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
15d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
16d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
17d5ac70f0Sopenharmony_ci *
18d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
19d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
22d5ac70f0Sopenharmony_ci *
23d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
24d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
25d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26d5ac70f0Sopenharmony_ci *
27d5ac70f0Sopenharmony_ci */
28d5ac70f0Sopenharmony_ci
29d5ac70f0Sopenharmony_ci#include "pcm_local.h"
30d5ac70f0Sopenharmony_ci#include "pcm_plugin.h"
31d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
32d5ac70f0Sopenharmony_ci#include "bswap.h"
33d5ac70f0Sopenharmony_ci#include <math.h>
34d5ac70f0Sopenharmony_ci
35d5ac70f0Sopenharmony_ci#ifndef PIC
36d5ac70f0Sopenharmony_ci/* entry for static linking */
37d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_route = "";
38d5ac70f0Sopenharmony_ci#endif
39d5ac70f0Sopenharmony_ci
40d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
41d5ac70f0Sopenharmony_ci
42d5ac70f0Sopenharmony_ci/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
43d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
44d5ac70f0Sopenharmony_ci#define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
45d5ac70f0Sopenharmony_ci#elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
46d5ac70f0Sopenharmony_ci#define div(a) a >>= 4
47d5ac70f0Sopenharmony_ci#else
48d5ac70f0Sopenharmony_ci#error "Add some code here"
49d5ac70f0Sopenharmony_ci#endif
50d5ac70f0Sopenharmony_ci
51d5ac70f0Sopenharmony_citypedef struct {
52d5ac70f0Sopenharmony_ci	int channel;
53d5ac70f0Sopenharmony_ci	int as_int;
54d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
55d5ac70f0Sopenharmony_ci	float as_float;
56d5ac70f0Sopenharmony_ci#endif
57d5ac70f0Sopenharmony_ci} snd_pcm_route_ttable_src_t;
58d5ac70f0Sopenharmony_ci
59d5ac70f0Sopenharmony_citypedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
60d5ac70f0Sopenharmony_ci
61d5ac70f0Sopenharmony_citypedef struct {
62d5ac70f0Sopenharmony_ci	enum {UINT64, FLOAT} sum_idx;
63d5ac70f0Sopenharmony_ci	unsigned int get_idx;
64d5ac70f0Sopenharmony_ci	unsigned int put_idx;
65d5ac70f0Sopenharmony_ci	unsigned int conv_idx;
66d5ac70f0Sopenharmony_ci	int use_getput;
67d5ac70f0Sopenharmony_ci	unsigned int src_size;
68d5ac70f0Sopenharmony_ci	snd_pcm_format_t dst_sfmt;
69d5ac70f0Sopenharmony_ci	unsigned int nsrcs;
70d5ac70f0Sopenharmony_ci	unsigned int ndsts;
71d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_dst_t *dsts;
72d5ac70f0Sopenharmony_ci} snd_pcm_route_params_t;
73d5ac70f0Sopenharmony_ci
74d5ac70f0Sopenharmony_ci
75d5ac70f0Sopenharmony_citypedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
76d5ac70f0Sopenharmony_ci			snd_pcm_uframes_t dst_offset,
77d5ac70f0Sopenharmony_ci			const snd_pcm_channel_area_t *src_areas,
78d5ac70f0Sopenharmony_ci			snd_pcm_uframes_t src_offset,
79d5ac70f0Sopenharmony_ci			unsigned int src_channels,
80d5ac70f0Sopenharmony_ci			snd_pcm_uframes_t frames,
81d5ac70f0Sopenharmony_ci			const snd_pcm_route_ttable_dst_t *ttable,
82d5ac70f0Sopenharmony_ci			const snd_pcm_route_params_t *params);
83d5ac70f0Sopenharmony_ci
84d5ac70f0Sopenharmony_cistruct snd_pcm_route_ttable_dst {
85d5ac70f0Sopenharmony_ci	int att;	/* Attenuated */
86d5ac70f0Sopenharmony_ci	unsigned int nsrcs;
87d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_src_t* srcs;
88d5ac70f0Sopenharmony_ci	route_f func;
89d5ac70f0Sopenharmony_ci};
90d5ac70f0Sopenharmony_ci
91d5ac70f0Sopenharmony_citypedef union {
92d5ac70f0Sopenharmony_ci	int32_t as_sint32;
93d5ac70f0Sopenharmony_ci	int64_t as_sint64;
94d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
95d5ac70f0Sopenharmony_ci	float as_float;
96d5ac70f0Sopenharmony_ci#endif
97d5ac70f0Sopenharmony_ci} sum_t;
98d5ac70f0Sopenharmony_ci
99d5ac70f0Sopenharmony_citypedef struct {
100d5ac70f0Sopenharmony_ci	/* This field need to be the first */
101d5ac70f0Sopenharmony_ci	snd_pcm_plugin_t plug;
102d5ac70f0Sopenharmony_ci	snd_pcm_format_t sformat;
103d5ac70f0Sopenharmony_ci	int schannels;
104d5ac70f0Sopenharmony_ci	snd_pcm_route_params_t params;
105d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *chmap;
106d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **chmap_override;
107d5ac70f0Sopenharmony_ci} snd_pcm_route_t;
108d5ac70f0Sopenharmony_ci
109d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
110d5ac70f0Sopenharmony_ci
111d5ac70f0Sopenharmony_cistatic void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
112d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t dst_offset,
113d5ac70f0Sopenharmony_ci					const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
114d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
115d5ac70f0Sopenharmony_ci					unsigned int src_channels ATTRIBUTE_UNUSED,
116d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t frames,
117d5ac70f0Sopenharmony_ci					const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
118d5ac70f0Sopenharmony_ci					const snd_pcm_route_params_t *params)
119d5ac70f0Sopenharmony_ci{
120d5ac70f0Sopenharmony_ci	snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
121d5ac70f0Sopenharmony_ci}
122d5ac70f0Sopenharmony_ci
123d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
124d5ac70f0Sopenharmony_ci
125d5ac70f0Sopenharmony_cistatic void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
126d5ac70f0Sopenharmony_ci				       snd_pcm_uframes_t dst_offset,
127d5ac70f0Sopenharmony_ci				       const snd_pcm_channel_area_t *src_areas,
128d5ac70f0Sopenharmony_ci				       snd_pcm_uframes_t src_offset,
129d5ac70f0Sopenharmony_ci				       unsigned int src_channels,
130d5ac70f0Sopenharmony_ci				       snd_pcm_uframes_t frames,
131d5ac70f0Sopenharmony_ci				       const snd_pcm_route_ttable_dst_t* ttable,
132d5ac70f0Sopenharmony_ci				       const snd_pcm_route_params_t *params)
133d5ac70f0Sopenharmony_ci{
134d5ac70f0Sopenharmony_ci#define CONV_LABELS
135d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
136d5ac70f0Sopenharmony_ci#undef CONV_LABELS
137d5ac70f0Sopenharmony_ci	void *conv;
138d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *src_area = 0;
139d5ac70f0Sopenharmony_ci	unsigned int srcidx;
140d5ac70f0Sopenharmony_ci	const char *src;
141d5ac70f0Sopenharmony_ci	char *dst;
142d5ac70f0Sopenharmony_ci	int src_step, dst_step;
143d5ac70f0Sopenharmony_ci	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
144d5ac70f0Sopenharmony_ci		unsigned int channel = ttable->srcs[srcidx].channel;
145d5ac70f0Sopenharmony_ci		if (channel >= src_channels)
146d5ac70f0Sopenharmony_ci			continue;
147d5ac70f0Sopenharmony_ci		src_area = &src_areas[channel];
148d5ac70f0Sopenharmony_ci		if (src_area->addr != NULL)
149d5ac70f0Sopenharmony_ci			break;
150d5ac70f0Sopenharmony_ci	}
151d5ac70f0Sopenharmony_ci	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
152d5ac70f0Sopenharmony_ci		snd_pcm_route_convert1_zero(dst_area, dst_offset,
153d5ac70f0Sopenharmony_ci					    src_areas, src_offset,
154d5ac70f0Sopenharmony_ci					    src_channels,
155d5ac70f0Sopenharmony_ci					    frames, ttable, params);
156d5ac70f0Sopenharmony_ci		return;
157d5ac70f0Sopenharmony_ci	}
158d5ac70f0Sopenharmony_ci
159d5ac70f0Sopenharmony_ci	conv = conv_labels[params->conv_idx];
160d5ac70f0Sopenharmony_ci	src = snd_pcm_channel_area_addr(src_area, src_offset);
161d5ac70f0Sopenharmony_ci	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
162d5ac70f0Sopenharmony_ci	src_step = snd_pcm_channel_area_step(src_area);
163d5ac70f0Sopenharmony_ci	dst_step = snd_pcm_channel_area_step(dst_area);
164d5ac70f0Sopenharmony_ci	while (frames-- > 0) {
165d5ac70f0Sopenharmony_ci		goto *conv;
166d5ac70f0Sopenharmony_ci#define CONV_END after
167d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
168d5ac70f0Sopenharmony_ci#undef CONV_END
169d5ac70f0Sopenharmony_ci	after:
170d5ac70f0Sopenharmony_ci		src += src_step;
171d5ac70f0Sopenharmony_ci		dst += dst_step;
172d5ac70f0Sopenharmony_ci	}
173d5ac70f0Sopenharmony_ci}
174d5ac70f0Sopenharmony_ci
175d5ac70f0Sopenharmony_cistatic void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
176d5ac70f0Sopenharmony_ci					      snd_pcm_uframes_t dst_offset,
177d5ac70f0Sopenharmony_ci					      const snd_pcm_channel_area_t *src_areas,
178d5ac70f0Sopenharmony_ci					      snd_pcm_uframes_t src_offset,
179d5ac70f0Sopenharmony_ci					      unsigned int src_channels,
180d5ac70f0Sopenharmony_ci					      snd_pcm_uframes_t frames,
181d5ac70f0Sopenharmony_ci					      const snd_pcm_route_ttable_dst_t* ttable,
182d5ac70f0Sopenharmony_ci					      const snd_pcm_route_params_t *params)
183d5ac70f0Sopenharmony_ci{
184d5ac70f0Sopenharmony_ci#define CONV24_LABELS
185d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
186d5ac70f0Sopenharmony_ci#undef CONV24_LABELS
187d5ac70f0Sopenharmony_ci	void *get, *put;
188d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *src_area = 0;
189d5ac70f0Sopenharmony_ci	unsigned int srcidx;
190d5ac70f0Sopenharmony_ci	const char *src;
191d5ac70f0Sopenharmony_ci	char *dst;
192d5ac70f0Sopenharmony_ci	int src_step, dst_step;
193d5ac70f0Sopenharmony_ci	uint32_t sample = 0;
194d5ac70f0Sopenharmony_ci	for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
195d5ac70f0Sopenharmony_ci		unsigned int channel = ttable->srcs[srcidx].channel;
196d5ac70f0Sopenharmony_ci		if (channel >= src_channels)
197d5ac70f0Sopenharmony_ci			continue;
198d5ac70f0Sopenharmony_ci		src_area = &src_areas[channel];
199d5ac70f0Sopenharmony_ci		if (src_area->addr != NULL)
200d5ac70f0Sopenharmony_ci			break;
201d5ac70f0Sopenharmony_ci	}
202d5ac70f0Sopenharmony_ci	if (srcidx == ttable->nsrcs || srcidx == src_channels) {
203d5ac70f0Sopenharmony_ci		snd_pcm_route_convert1_zero(dst_area, dst_offset,
204d5ac70f0Sopenharmony_ci					    src_areas, src_offset,
205d5ac70f0Sopenharmony_ci					    src_channels,
206d5ac70f0Sopenharmony_ci					    frames, ttable, params);
207d5ac70f0Sopenharmony_ci		return;
208d5ac70f0Sopenharmony_ci	}
209d5ac70f0Sopenharmony_ci
210d5ac70f0Sopenharmony_ci	get = get32_labels[params->get_idx];
211d5ac70f0Sopenharmony_ci	put = put32_labels[params->put_idx];
212d5ac70f0Sopenharmony_ci	src = snd_pcm_channel_area_addr(src_area, src_offset);
213d5ac70f0Sopenharmony_ci	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
214d5ac70f0Sopenharmony_ci	src_step = snd_pcm_channel_area_step(src_area);
215d5ac70f0Sopenharmony_ci	dst_step = snd_pcm_channel_area_step(dst_area);
216d5ac70f0Sopenharmony_ci	while (frames-- > 0) {
217d5ac70f0Sopenharmony_ci		goto *get;
218d5ac70f0Sopenharmony_ci#define CONV24_END after
219d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
220d5ac70f0Sopenharmony_ci#undef CONV24_END
221d5ac70f0Sopenharmony_ci	after:
222d5ac70f0Sopenharmony_ci		src += src_step;
223d5ac70f0Sopenharmony_ci		dst += dst_step;
224d5ac70f0Sopenharmony_ci	}
225d5ac70f0Sopenharmony_ci}
226d5ac70f0Sopenharmony_ci
227d5ac70f0Sopenharmony_cistatic void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
228d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t dst_offset,
229d5ac70f0Sopenharmony_ci					const snd_pcm_channel_area_t *src_areas,
230d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t src_offset,
231d5ac70f0Sopenharmony_ci					unsigned int src_channels,
232d5ac70f0Sopenharmony_ci					snd_pcm_uframes_t frames,
233d5ac70f0Sopenharmony_ci					const snd_pcm_route_ttable_dst_t* ttable,
234d5ac70f0Sopenharmony_ci					const snd_pcm_route_params_t *params)
235d5ac70f0Sopenharmony_ci{
236d5ac70f0Sopenharmony_ci#define GET32_LABELS
237d5ac70f0Sopenharmony_ci#define PUT32_LABELS
238d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
239d5ac70f0Sopenharmony_ci#undef GET32_LABELS
240d5ac70f0Sopenharmony_ci#undef PUT32_LABELS
241d5ac70f0Sopenharmony_ci	static void *const zero_labels[2] = {
242d5ac70f0Sopenharmony_ci		&&zero_int64,
243d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
244d5ac70f0Sopenharmony_ci		&&zero_float
245d5ac70f0Sopenharmony_ci#endif
246d5ac70f0Sopenharmony_ci	};
247d5ac70f0Sopenharmony_ci	/* sum_type att */
248d5ac70f0Sopenharmony_ci	static void *const add_labels[2 * 2] = {
249d5ac70f0Sopenharmony_ci		&&add_int64_noatt, &&add_int64_att,
250d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
251d5ac70f0Sopenharmony_ci		&&add_float_noatt, &&add_float_att
252d5ac70f0Sopenharmony_ci#endif
253d5ac70f0Sopenharmony_ci	};
254d5ac70f0Sopenharmony_ci	/* sum_type att */
255d5ac70f0Sopenharmony_ci	static void *const norm_labels[2 * 2] = {
256d5ac70f0Sopenharmony_ci		&&norm_int64_noatt,
257d5ac70f0Sopenharmony_ci		&&norm_int64_att,
258d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
259d5ac70f0Sopenharmony_ci		&&norm_float,
260d5ac70f0Sopenharmony_ci		&&norm_float,
261d5ac70f0Sopenharmony_ci#endif
262d5ac70f0Sopenharmony_ci	};
263d5ac70f0Sopenharmony_ci	void *zero, *get32, *add, *norm, *put32;
264d5ac70f0Sopenharmony_ci	int nsrcs = ttable->nsrcs;
265d5ac70f0Sopenharmony_ci	char *dst;
266d5ac70f0Sopenharmony_ci	int dst_step;
267d5ac70f0Sopenharmony_ci	const char *srcs[nsrcs];
268d5ac70f0Sopenharmony_ci	int src_steps[nsrcs];
269d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_src_t src_tt[nsrcs];
270d5ac70f0Sopenharmony_ci	int32_t sample = 0;
271d5ac70f0Sopenharmony_ci	int srcidx, srcidx1 = 0;
272d5ac70f0Sopenharmony_ci	for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
273d5ac70f0Sopenharmony_ci		const snd_pcm_channel_area_t *src_area;
274d5ac70f0Sopenharmony_ci		unsigned int channel = ttable->srcs[srcidx].channel;
275d5ac70f0Sopenharmony_ci		if (channel >= src_channels)
276d5ac70f0Sopenharmony_ci			continue;
277d5ac70f0Sopenharmony_ci		src_area = &src_areas[channel];
278d5ac70f0Sopenharmony_ci		srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
279d5ac70f0Sopenharmony_ci		src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
280d5ac70f0Sopenharmony_ci		src_tt[srcidx1] = ttable->srcs[srcidx];
281d5ac70f0Sopenharmony_ci		srcidx1++;
282d5ac70f0Sopenharmony_ci	}
283d5ac70f0Sopenharmony_ci	nsrcs = srcidx1;
284d5ac70f0Sopenharmony_ci	if (nsrcs == 0) {
285d5ac70f0Sopenharmony_ci		snd_pcm_route_convert1_zero(dst_area, dst_offset,
286d5ac70f0Sopenharmony_ci					    src_areas, src_offset,
287d5ac70f0Sopenharmony_ci					    src_channels,
288d5ac70f0Sopenharmony_ci					    frames, ttable, params);
289d5ac70f0Sopenharmony_ci		return;
290d5ac70f0Sopenharmony_ci	} else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
291d5ac70f0Sopenharmony_ci		if (params->use_getput)
292d5ac70f0Sopenharmony_ci			snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
293d5ac70f0Sopenharmony_ci							  src_areas, src_offset,
294d5ac70f0Sopenharmony_ci							  src_channels,
295d5ac70f0Sopenharmony_ci							  frames, ttable, params);
296d5ac70f0Sopenharmony_ci		else
297d5ac70f0Sopenharmony_ci			snd_pcm_route_convert1_one(dst_area, dst_offset,
298d5ac70f0Sopenharmony_ci						   src_areas, src_offset,
299d5ac70f0Sopenharmony_ci						   src_channels,
300d5ac70f0Sopenharmony_ci						   frames, ttable, params);
301d5ac70f0Sopenharmony_ci		return;
302d5ac70f0Sopenharmony_ci	}
303d5ac70f0Sopenharmony_ci
304d5ac70f0Sopenharmony_ci	zero = zero_labels[params->sum_idx];
305d5ac70f0Sopenharmony_ci	get32 = get32_labels[params->get_idx];
306d5ac70f0Sopenharmony_ci	add = add_labels[params->sum_idx * 2 + ttable->att];
307d5ac70f0Sopenharmony_ci	norm = norm_labels[params->sum_idx * 2 + ttable->att];
308d5ac70f0Sopenharmony_ci	put32 = put32_labels[params->put_idx];
309d5ac70f0Sopenharmony_ci	dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
310d5ac70f0Sopenharmony_ci	dst_step = snd_pcm_channel_area_step(dst_area);
311d5ac70f0Sopenharmony_ci
312d5ac70f0Sopenharmony_ci	while (frames-- > 0) {
313d5ac70f0Sopenharmony_ci		snd_pcm_route_ttable_src_t *ttp = src_tt;
314d5ac70f0Sopenharmony_ci		sum_t sum;
315d5ac70f0Sopenharmony_ci
316d5ac70f0Sopenharmony_ci		/* Zero sum */
317d5ac70f0Sopenharmony_ci		goto *zero;
318d5ac70f0Sopenharmony_ci	zero_int64:
319d5ac70f0Sopenharmony_ci		sum.as_sint64 = 0;
320d5ac70f0Sopenharmony_ci		goto zero_end;
321d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
322d5ac70f0Sopenharmony_ci	zero_float:
323d5ac70f0Sopenharmony_ci		sum.as_float = 0.0;
324d5ac70f0Sopenharmony_ci		goto zero_end;
325d5ac70f0Sopenharmony_ci#endif
326d5ac70f0Sopenharmony_ci	zero_end:
327d5ac70f0Sopenharmony_ci		for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
328d5ac70f0Sopenharmony_ci			const char *src = srcs[srcidx];
329d5ac70f0Sopenharmony_ci
330d5ac70f0Sopenharmony_ci			/* Get sample */
331d5ac70f0Sopenharmony_ci			goto *get32;
332d5ac70f0Sopenharmony_ci#define GET32_END after_get
333d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
334d5ac70f0Sopenharmony_ci#undef GET32_END
335d5ac70f0Sopenharmony_ci		after_get:
336d5ac70f0Sopenharmony_ci
337d5ac70f0Sopenharmony_ci			/* Sum */
338d5ac70f0Sopenharmony_ci			goto *add;
339d5ac70f0Sopenharmony_ci		add_int64_att:
340d5ac70f0Sopenharmony_ci			sum.as_sint64 += (int64_t) sample * ttp->as_int;
341d5ac70f0Sopenharmony_ci			goto after_sum;
342d5ac70f0Sopenharmony_ci		add_int64_noatt:
343d5ac70f0Sopenharmony_ci			if (ttp->as_int)
344d5ac70f0Sopenharmony_ci				sum.as_sint64 += sample;
345d5ac70f0Sopenharmony_ci			goto after_sum;
346d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
347d5ac70f0Sopenharmony_ci		add_float_att:
348d5ac70f0Sopenharmony_ci			sum.as_float += sample * ttp->as_float;
349d5ac70f0Sopenharmony_ci			goto after_sum;
350d5ac70f0Sopenharmony_ci		add_float_noatt:
351d5ac70f0Sopenharmony_ci			if (ttp->as_int)
352d5ac70f0Sopenharmony_ci				sum.as_float += sample;
353d5ac70f0Sopenharmony_ci			goto after_sum;
354d5ac70f0Sopenharmony_ci#endif
355d5ac70f0Sopenharmony_ci		after_sum:
356d5ac70f0Sopenharmony_ci			srcs[srcidx] += src_steps[srcidx];
357d5ac70f0Sopenharmony_ci			ttp++;
358d5ac70f0Sopenharmony_ci		}
359d5ac70f0Sopenharmony_ci
360d5ac70f0Sopenharmony_ci		/* Normalization */
361d5ac70f0Sopenharmony_ci		goto *norm;
362d5ac70f0Sopenharmony_ci	norm_int64_att:
363d5ac70f0Sopenharmony_ci		div(sum.as_sint64);
364d5ac70f0Sopenharmony_ci		/* fallthru */
365d5ac70f0Sopenharmony_ci	norm_int64_noatt:
366d5ac70f0Sopenharmony_ci		if (sum.as_sint64 > (int64_t)0x7fffffff)
367d5ac70f0Sopenharmony_ci			sample = 0x7fffffff;	/* maximum positive value */
368d5ac70f0Sopenharmony_ci		else if (sum.as_sint64 < -(int64_t)0x80000000)
369d5ac70f0Sopenharmony_ci			sample = 0x80000000;	/* maximum negative value */
370d5ac70f0Sopenharmony_ci		else
371d5ac70f0Sopenharmony_ci			sample = sum.as_sint64;
372d5ac70f0Sopenharmony_ci		goto after_norm;
373d5ac70f0Sopenharmony_ci
374d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
375d5ac70f0Sopenharmony_ci	norm_float:
376d5ac70f0Sopenharmony_ci		sum.as_float = rint(sum.as_float);
377d5ac70f0Sopenharmony_ci		if (sum.as_float > (int64_t)0x7fffffff)
378d5ac70f0Sopenharmony_ci			sample = 0x7fffffff;	/* maximum positive value */
379d5ac70f0Sopenharmony_ci		else if (sum.as_float < -(int64_t)0x80000000)
380d5ac70f0Sopenharmony_ci			sample = 0x80000000;	/* maximum negative value */
381d5ac70f0Sopenharmony_ci		else
382d5ac70f0Sopenharmony_ci			sample = sum.as_float;
383d5ac70f0Sopenharmony_ci		goto after_norm;
384d5ac70f0Sopenharmony_ci#endif
385d5ac70f0Sopenharmony_ci	after_norm:
386d5ac70f0Sopenharmony_ci
387d5ac70f0Sopenharmony_ci		/* Put sample */
388d5ac70f0Sopenharmony_ci		goto *put32;
389d5ac70f0Sopenharmony_ci#define PUT32_END after_put32
390d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
391d5ac70f0Sopenharmony_ci#undef PUT32_END
392d5ac70f0Sopenharmony_ci	after_put32:
393d5ac70f0Sopenharmony_ci
394d5ac70f0Sopenharmony_ci		dst += dst_step;
395d5ac70f0Sopenharmony_ci	}
396d5ac70f0Sopenharmony_ci}
397d5ac70f0Sopenharmony_ci
398d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
399d5ac70f0Sopenharmony_ci
400d5ac70f0Sopenharmony_cistatic void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
401d5ac70f0Sopenharmony_ci				  snd_pcm_uframes_t dst_offset,
402d5ac70f0Sopenharmony_ci				  const snd_pcm_channel_area_t *src_areas,
403d5ac70f0Sopenharmony_ci				  snd_pcm_uframes_t src_offset,
404d5ac70f0Sopenharmony_ci				  unsigned int src_channels,
405d5ac70f0Sopenharmony_ci				  unsigned int dst_channels,
406d5ac70f0Sopenharmony_ci				  snd_pcm_uframes_t frames,
407d5ac70f0Sopenharmony_ci				  snd_pcm_route_params_t *params)
408d5ac70f0Sopenharmony_ci{
409d5ac70f0Sopenharmony_ci	unsigned int dst_channel;
410d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_dst_t *dstp;
411d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *dst_area;
412d5ac70f0Sopenharmony_ci
413d5ac70f0Sopenharmony_ci	dstp = params->dsts;
414d5ac70f0Sopenharmony_ci	dst_area = dst_areas;
415d5ac70f0Sopenharmony_ci	for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
416d5ac70f0Sopenharmony_ci		if (dst_channel >= params->ndsts)
417d5ac70f0Sopenharmony_ci			snd_pcm_route_convert1_zero(dst_area, dst_offset,
418d5ac70f0Sopenharmony_ci						    src_areas, src_offset,
419d5ac70f0Sopenharmony_ci						    src_channels,
420d5ac70f0Sopenharmony_ci						    frames, dstp, params);
421d5ac70f0Sopenharmony_ci		else
422d5ac70f0Sopenharmony_ci			dstp->func(dst_area, dst_offset,
423d5ac70f0Sopenharmony_ci				   src_areas, src_offset,
424d5ac70f0Sopenharmony_ci				   src_channels,
425d5ac70f0Sopenharmony_ci				   frames, dstp, params);
426d5ac70f0Sopenharmony_ci		dstp++;
427d5ac70f0Sopenharmony_ci		dst_area++;
428d5ac70f0Sopenharmony_ci	}
429d5ac70f0Sopenharmony_ci}
430d5ac70f0Sopenharmony_ci
431d5ac70f0Sopenharmony_cistatic int snd_pcm_route_close(snd_pcm_t *pcm)
432d5ac70f0Sopenharmony_ci{
433d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
434d5ac70f0Sopenharmony_ci	snd_pcm_route_params_t *params = &route->params;
435d5ac70f0Sopenharmony_ci	unsigned int dst_channel;
436d5ac70f0Sopenharmony_ci
437d5ac70f0Sopenharmony_ci	if (params->dsts) {
438d5ac70f0Sopenharmony_ci		for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
439d5ac70f0Sopenharmony_ci			free(params->dsts[dst_channel].srcs);
440d5ac70f0Sopenharmony_ci		}
441d5ac70f0Sopenharmony_ci		free(params->dsts);
442d5ac70f0Sopenharmony_ci	}
443d5ac70f0Sopenharmony_ci	free(route->chmap);
444d5ac70f0Sopenharmony_ci	snd_pcm_free_chmaps(route->chmap_override);
445d5ac70f0Sopenharmony_ci	return snd_pcm_generic_close(pcm);
446d5ac70f0Sopenharmony_ci}
447d5ac70f0Sopenharmony_ci
448d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
449d5ac70f0Sopenharmony_ci{
450d5ac70f0Sopenharmony_ci	int err;
451d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
452d5ac70f0Sopenharmony_ci	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
453d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
454d5ac70f0Sopenharmony_ci					 &access_mask);
455d5ac70f0Sopenharmony_ci	if (err < 0)
456d5ac70f0Sopenharmony_ci		return err;
457d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
458d5ac70f0Sopenharmony_ci					 &format_mask);
459d5ac70f0Sopenharmony_ci	if (err < 0)
460d5ac70f0Sopenharmony_ci		return err;
461d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
462d5ac70f0Sopenharmony_ci	if (err < 0)
463d5ac70f0Sopenharmony_ci		return err;
464d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
465d5ac70f0Sopenharmony_ci	if (err < 0)
466d5ac70f0Sopenharmony_ci		return err;
467d5ac70f0Sopenharmony_ci	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
468d5ac70f0Sopenharmony_ci	return 0;
469d5ac70f0Sopenharmony_ci}
470d5ac70f0Sopenharmony_ci
471d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
472d5ac70f0Sopenharmony_ci{
473d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
474d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
475d5ac70f0Sopenharmony_ci	_snd_pcm_hw_params_any(sparams);
476d5ac70f0Sopenharmony_ci	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
477d5ac70f0Sopenharmony_ci				   &saccess_mask);
478d5ac70f0Sopenharmony_ci	if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
479d5ac70f0Sopenharmony_ci		_snd_pcm_hw_params_set_format(sparams, route->sformat);
480d5ac70f0Sopenharmony_ci		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
481d5ac70f0Sopenharmony_ci	}
482d5ac70f0Sopenharmony_ci	if (route->schannels >= 0) {
483d5ac70f0Sopenharmony_ci		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
484d5ac70f0Sopenharmony_ci				      (unsigned int) route->schannels, 0);
485d5ac70f0Sopenharmony_ci	}
486d5ac70f0Sopenharmony_ci	return 0;
487d5ac70f0Sopenharmony_ci}
488d5ac70f0Sopenharmony_ci
489d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
490d5ac70f0Sopenharmony_ci					    snd_pcm_hw_params_t *sparams)
491d5ac70f0Sopenharmony_ci{
492d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
493d5ac70f0Sopenharmony_ci	int err;
494d5ac70f0Sopenharmony_ci	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
495d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIODS |
496d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
497d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_TIME |
498d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
499d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_TIME |
500d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_TICK_TIME);
501d5ac70f0Sopenharmony_ci	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
502d5ac70f0Sopenharmony_ci		links |= (SND_PCM_HW_PARBIT_FORMAT |
503d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SUBFORMAT |
504d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
505d5ac70f0Sopenharmony_ci	if (route->schannels < 0)
506d5ac70f0Sopenharmony_ci		links |= SND_PCM_HW_PARBIT_CHANNELS;
507d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_refine(sparams, links, params);
508d5ac70f0Sopenharmony_ci	if (err < 0)
509d5ac70f0Sopenharmony_ci		return err;
510d5ac70f0Sopenharmony_ci	return 0;
511d5ac70f0Sopenharmony_ci}
512d5ac70f0Sopenharmony_ci
513d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
514d5ac70f0Sopenharmony_ci					    snd_pcm_hw_params_t *sparams)
515d5ac70f0Sopenharmony_ci{
516d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
517d5ac70f0Sopenharmony_ci	int err;
518d5ac70f0Sopenharmony_ci	unsigned int links = (SND_PCM_HW_PARBIT_RATE |
519d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIODS |
520d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
521d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_TIME |
522d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
523d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_TIME |
524d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_TICK_TIME);
525d5ac70f0Sopenharmony_ci	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
526d5ac70f0Sopenharmony_ci		links |= (SND_PCM_HW_PARBIT_FORMAT |
527d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SUBFORMAT |
528d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
529d5ac70f0Sopenharmony_ci	if (route->schannels < 0)
530d5ac70f0Sopenharmony_ci		links |= SND_PCM_HW_PARBIT_CHANNELS;
531d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_refine(params, links, sparams);
532d5ac70f0Sopenharmony_ci	if (err < 0)
533d5ac70f0Sopenharmony_ci		return err;
534d5ac70f0Sopenharmony_ci	return 0;
535d5ac70f0Sopenharmony_ci}
536d5ac70f0Sopenharmony_ci
537d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
538d5ac70f0Sopenharmony_ci{
539d5ac70f0Sopenharmony_ci	return snd_pcm_hw_refine_slave(pcm, params,
540d5ac70f0Sopenharmony_ci				       snd_pcm_route_hw_refine_cprepare,
541d5ac70f0Sopenharmony_ci				       snd_pcm_route_hw_refine_cchange,
542d5ac70f0Sopenharmony_ci				       snd_pcm_route_hw_refine_sprepare,
543d5ac70f0Sopenharmony_ci				       snd_pcm_route_hw_refine_schange,
544d5ac70f0Sopenharmony_ci				       snd_pcm_generic_hw_refine);
545d5ac70f0Sopenharmony_ci}
546d5ac70f0Sopenharmony_ci
547d5ac70f0Sopenharmony_cistatic int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
548d5ac70f0Sopenharmony_ci{
549d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
550d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = route->plug.gen.slave;
551d5ac70f0Sopenharmony_ci	snd_pcm_format_t src_format, dst_format;
552d5ac70f0Sopenharmony_ci	int err = snd_pcm_hw_params_slave(pcm, params,
553d5ac70f0Sopenharmony_ci					  snd_pcm_route_hw_refine_cchange,
554d5ac70f0Sopenharmony_ci					  snd_pcm_route_hw_refine_sprepare,
555d5ac70f0Sopenharmony_ci					  snd_pcm_route_hw_refine_schange,
556d5ac70f0Sopenharmony_ci					  snd_pcm_generic_hw_params);
557d5ac70f0Sopenharmony_ci	if (err < 0)
558d5ac70f0Sopenharmony_ci		return err;
559d5ac70f0Sopenharmony_ci
560d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
561d5ac70f0Sopenharmony_ci		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
562d5ac70f0Sopenharmony_ci		dst_format = slave->format;
563d5ac70f0Sopenharmony_ci	} else {
564d5ac70f0Sopenharmony_ci		src_format = slave->format;
565d5ac70f0Sopenharmony_ci		err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
566d5ac70f0Sopenharmony_ci	}
567d5ac70f0Sopenharmony_ci	if (err < 0)
568d5ac70f0Sopenharmony_ci		return err;
569d5ac70f0Sopenharmony_ci	/* 3 bytes or 20-bit formats? */
570d5ac70f0Sopenharmony_ci	route->params.use_getput =
571d5ac70f0Sopenharmony_ci		(snd_pcm_format_physical_width(src_format) + 7) / 8 == 3 ||
572d5ac70f0Sopenharmony_ci		(snd_pcm_format_physical_width(dst_format) + 7) / 8 == 3 ||
573d5ac70f0Sopenharmony_ci		snd_pcm_format_width(src_format) == 20 ||
574d5ac70f0Sopenharmony_ci		snd_pcm_format_width(dst_format) == 20;
575d5ac70f0Sopenharmony_ci	route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32);
576d5ac70f0Sopenharmony_ci	route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, dst_format);
577d5ac70f0Sopenharmony_ci	route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
578d5ac70f0Sopenharmony_ci	route->params.src_size = snd_pcm_format_width(src_format) / 8;
579d5ac70f0Sopenharmony_ci	route->params.dst_sfmt = dst_format;
580d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
581d5ac70f0Sopenharmony_ci	route->params.sum_idx = FLOAT;
582d5ac70f0Sopenharmony_ci#else
583d5ac70f0Sopenharmony_ci	route->params.sum_idx = UINT64;
584d5ac70f0Sopenharmony_ci#endif
585d5ac70f0Sopenharmony_ci	return 0;
586d5ac70f0Sopenharmony_ci}
587d5ac70f0Sopenharmony_ci
588d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t
589d5ac70f0Sopenharmony_cisnd_pcm_route_write_areas(snd_pcm_t *pcm,
590d5ac70f0Sopenharmony_ci			  const snd_pcm_channel_area_t *areas,
591d5ac70f0Sopenharmony_ci			  snd_pcm_uframes_t offset,
592d5ac70f0Sopenharmony_ci			  snd_pcm_uframes_t size,
593d5ac70f0Sopenharmony_ci			  const snd_pcm_channel_area_t *slave_areas,
594d5ac70f0Sopenharmony_ci			  snd_pcm_uframes_t slave_offset,
595d5ac70f0Sopenharmony_ci			  snd_pcm_uframes_t *slave_sizep)
596d5ac70f0Sopenharmony_ci{
597d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
598d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = route->plug.gen.slave;
599d5ac70f0Sopenharmony_ci	if (size > *slave_sizep)
600d5ac70f0Sopenharmony_ci		size = *slave_sizep;
601d5ac70f0Sopenharmony_ci	snd_pcm_route_convert(slave_areas, slave_offset,
602d5ac70f0Sopenharmony_ci			      areas, offset,
603d5ac70f0Sopenharmony_ci			      pcm->channels,
604d5ac70f0Sopenharmony_ci			      slave->channels,
605d5ac70f0Sopenharmony_ci			      size, &route->params);
606d5ac70f0Sopenharmony_ci	*slave_sizep = size;
607d5ac70f0Sopenharmony_ci	return size;
608d5ac70f0Sopenharmony_ci}
609d5ac70f0Sopenharmony_ci
610d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t
611d5ac70f0Sopenharmony_cisnd_pcm_route_read_areas(snd_pcm_t *pcm,
612d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *areas,
613d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t offset,
614d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t size,
615d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *slave_areas,
616d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t slave_offset,
617d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t *slave_sizep)
618d5ac70f0Sopenharmony_ci{
619d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
620d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = route->plug.gen.slave;
621d5ac70f0Sopenharmony_ci	if (size > *slave_sizep)
622d5ac70f0Sopenharmony_ci		size = *slave_sizep;
623d5ac70f0Sopenharmony_ci	snd_pcm_route_convert(areas, offset,
624d5ac70f0Sopenharmony_ci			      slave_areas, slave_offset,
625d5ac70f0Sopenharmony_ci			      slave->channels,
626d5ac70f0Sopenharmony_ci			      pcm->channels,
627d5ac70f0Sopenharmony_ci			      size, &route->params);
628d5ac70f0Sopenharmony_ci	*slave_sizep = size;
629d5ac70f0Sopenharmony_ci	return size;
630d5ac70f0Sopenharmony_ci}
631d5ac70f0Sopenharmony_ci
632d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
633d5ac70f0Sopenharmony_ci{
634d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
635d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *map, *slave_map;
636d5ac70f0Sopenharmony_ci	unsigned int src, dst, nsrcs;
637d5ac70f0Sopenharmony_ci
638d5ac70f0Sopenharmony_ci	if (route->chmap_override)
639d5ac70f0Sopenharmony_ci		return _snd_pcm_choose_fixed_chmap(pcm, route->chmap_override);
640d5ac70f0Sopenharmony_ci
641d5ac70f0Sopenharmony_ci	slave_map = snd_pcm_generic_get_chmap(pcm);
642d5ac70f0Sopenharmony_ci	if (!slave_map)
643d5ac70f0Sopenharmony_ci		return NULL;
644d5ac70f0Sopenharmony_ci	nsrcs = route->params.nsrcs;
645d5ac70f0Sopenharmony_ci	map = calloc(4, nsrcs + 1);
646d5ac70f0Sopenharmony_ci	if (!map) {
647d5ac70f0Sopenharmony_ci		free(slave_map);
648d5ac70f0Sopenharmony_ci		return NULL;
649d5ac70f0Sopenharmony_ci	}
650d5ac70f0Sopenharmony_ci	map->channels = nsrcs;
651d5ac70f0Sopenharmony_ci	for (src = 0; src < nsrcs; src++)
652d5ac70f0Sopenharmony_ci		map->pos[src] = SND_CHMAP_NA;
653d5ac70f0Sopenharmony_ci	for (dst = 0; dst < route->params.ndsts; dst++) {
654d5ac70f0Sopenharmony_ci		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
655d5ac70f0Sopenharmony_ci		for (src = 0; src < d->nsrcs; src++) {
656d5ac70f0Sopenharmony_ci			unsigned int c = d->srcs[src].channel;
657d5ac70f0Sopenharmony_ci			if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
658d5ac70f0Sopenharmony_ci				map->pos[c] = slave_map->pos[dst];
659d5ac70f0Sopenharmony_ci		}
660d5ac70f0Sopenharmony_ci	}
661d5ac70f0Sopenharmony_ci	free(slave_map);
662d5ac70f0Sopenharmony_ci	return map;
663d5ac70f0Sopenharmony_ci}
664d5ac70f0Sopenharmony_ci
665d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
666d5ac70f0Sopenharmony_ci{
667d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
668d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **maps;
669d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *map;
670d5ac70f0Sopenharmony_ci
671d5ac70f0Sopenharmony_ci	if (route->chmap_override)
672d5ac70f0Sopenharmony_ci		return _snd_pcm_copy_chmap_query(route->chmap_override);
673d5ac70f0Sopenharmony_ci
674d5ac70f0Sopenharmony_ci	map = snd_pcm_route_get_chmap(pcm);
675d5ac70f0Sopenharmony_ci	if (!map)
676d5ac70f0Sopenharmony_ci		return NULL;
677d5ac70f0Sopenharmony_ci	maps = _snd_pcm_make_single_query_chmaps(map);
678d5ac70f0Sopenharmony_ci	free(map);
679d5ac70f0Sopenharmony_ci	return maps;
680d5ac70f0Sopenharmony_ci}
681d5ac70f0Sopenharmony_ci
682d5ac70f0Sopenharmony_cistatic void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
683d5ac70f0Sopenharmony_ci{
684d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
685d5ac70f0Sopenharmony_ci	unsigned int dst;
686d5ac70f0Sopenharmony_ci	if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
687d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Route conversion PCM\n");
688d5ac70f0Sopenharmony_ci	else
689d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Route conversion PCM (sformat=%s)\n",
690d5ac70f0Sopenharmony_ci			snd_pcm_format_name(route->sformat));
691d5ac70f0Sopenharmony_ci	snd_output_puts(out, "  Transformation table:\n");
692d5ac70f0Sopenharmony_ci	for (dst = 0; dst < route->params.ndsts; dst++) {
693d5ac70f0Sopenharmony_ci		snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
694d5ac70f0Sopenharmony_ci		unsigned int src;
695d5ac70f0Sopenharmony_ci		snd_output_printf(out, "    %d <- ", dst);
696d5ac70f0Sopenharmony_ci		if (d->nsrcs == 0) {
697d5ac70f0Sopenharmony_ci			snd_output_printf(out, "none\n");
698d5ac70f0Sopenharmony_ci			continue;
699d5ac70f0Sopenharmony_ci		}
700d5ac70f0Sopenharmony_ci		src = 0;
701d5ac70f0Sopenharmony_ci		while (1) {
702d5ac70f0Sopenharmony_ci			snd_pcm_route_ttable_src_t *s = &d->srcs[src];
703d5ac70f0Sopenharmony_ci			if (d->att)
704d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
705d5ac70f0Sopenharmony_ci				snd_output_printf(out, "%d*%g", s->channel, s->as_float);
706d5ac70f0Sopenharmony_ci#else
707d5ac70f0Sopenharmony_ci				snd_output_printf(out, "%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
708d5ac70f0Sopenharmony_ci#endif
709d5ac70f0Sopenharmony_ci			else
710d5ac70f0Sopenharmony_ci				snd_output_printf(out, "%d", s->channel);
711d5ac70f0Sopenharmony_ci			src++;
712d5ac70f0Sopenharmony_ci			if (src == d->nsrcs)
713d5ac70f0Sopenharmony_ci				break;
714d5ac70f0Sopenharmony_ci			snd_output_puts(out, " + ");
715d5ac70f0Sopenharmony_ci		}
716d5ac70f0Sopenharmony_ci		snd_output_putc(out, '\n');
717d5ac70f0Sopenharmony_ci	}
718d5ac70f0Sopenharmony_ci	if (pcm->setup) {
719d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Its setup is:\n");
720d5ac70f0Sopenharmony_ci		snd_pcm_dump_setup(pcm, out);
721d5ac70f0Sopenharmony_ci	}
722d5ac70f0Sopenharmony_ci	snd_output_printf(out, "Slave: ");
723d5ac70f0Sopenharmony_ci	snd_pcm_dump(route->plug.gen.slave, out);
724d5ac70f0Sopenharmony_ci}
725d5ac70f0Sopenharmony_ci
726d5ac70f0Sopenharmony_ci/*
727d5ac70f0Sopenharmony_ci * Converts a string to an array of channel indices:
728d5ac70f0Sopenharmony_ci * - Given a number, the result is an array with one element,
729d5ac70f0Sopenharmony_ci *   containing that number
730d5ac70f0Sopenharmony_ci * - Given a channel name (e g "FL") and a chmap,
731d5ac70f0Sopenharmony_ci *   it will look this up in the chmap and return all matches
732d5ac70f0Sopenharmony_ci * - Given a channel name and no chmap, the result is an array with one element,
733d5ac70f0Sopenharmony_ci     containing alsa standard channel map. Note that this might be a negative
734d5ac70f0Sopenharmony_ci     number in case of "UNKNOWN", "NA" or "MONO".
735d5ac70f0Sopenharmony_ci * Return value is number of matches written.
736d5ac70f0Sopenharmony_ci */
737d5ac70f0Sopenharmony_cistatic int strtochannel(const char *id, snd_pcm_chmap_t *chmap,
738d5ac70f0Sopenharmony_ci			 long *channel, int channel_size)
739d5ac70f0Sopenharmony_ci{
740d5ac70f0Sopenharmony_ci	int ch;
741d5ac70f0Sopenharmony_ci	if (safe_strtol(id, channel) >= 0)
742d5ac70f0Sopenharmony_ci		return 1;
743d5ac70f0Sopenharmony_ci
744d5ac70f0Sopenharmony_ci	ch = (int) snd_pcm_chmap_from_string(id);
745d5ac70f0Sopenharmony_ci	if (ch == -1)
746d5ac70f0Sopenharmony_ci		return -EINVAL;
747d5ac70f0Sopenharmony_ci
748d5ac70f0Sopenharmony_ci	if (chmap) {
749d5ac70f0Sopenharmony_ci		int i, r = 0;
750d5ac70f0Sopenharmony_ci		/* Start with highest channel to simplify implementation of
751d5ac70f0Sopenharmony_ci		   determine ttable size */
752d5ac70f0Sopenharmony_ci		for (i = chmap->channels - 1; i >= 0; i--) {
753d5ac70f0Sopenharmony_ci			if ((int) chmap->pos[i] != ch)
754d5ac70f0Sopenharmony_ci				continue;
755d5ac70f0Sopenharmony_ci			if (r >= channel_size)
756d5ac70f0Sopenharmony_ci				continue;
757d5ac70f0Sopenharmony_ci			channel[r++] = i;
758d5ac70f0Sopenharmony_ci		}
759d5ac70f0Sopenharmony_ci		return r;
760d5ac70f0Sopenharmony_ci	}
761d5ac70f0Sopenharmony_ci	else {
762d5ac70f0Sopenharmony_ci		/* Assume ALSA standard channel mapping */
763d5ac70f0Sopenharmony_ci		*channel = ch - SND_CHMAP_FL;
764d5ac70f0Sopenharmony_ci		return 1;
765d5ac70f0Sopenharmony_ci	}
766d5ac70f0Sopenharmony_ci}
767d5ac70f0Sopenharmony_ci
768d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
769d5ac70f0Sopenharmony_ci#define MAX_CHMAP_CHANNELS 256
770d5ac70f0Sopenharmony_ci#endif
771d5ac70f0Sopenharmony_ci
772d5ac70f0Sopenharmony_cistatic int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap)
773d5ac70f0Sopenharmony_ci{
774d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, inext;
775d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *chmap;
776d5ac70f0Sopenharmony_ci
777d5ac70f0Sopenharmony_ci	assert(tt && tt_chmap);
778d5ac70f0Sopenharmony_ci	chmap = malloc(sizeof(snd_pcm_chmap_t) +
779d5ac70f0Sopenharmony_ci		       MAX_CHMAP_CHANNELS * sizeof(unsigned int));
780d5ac70f0Sopenharmony_ci
781d5ac70f0Sopenharmony_ci	chmap->channels = 0;
782d5ac70f0Sopenharmony_ci	snd_config_for_each(i, inext, tt) {
783d5ac70f0Sopenharmony_ci		const char *id;
784d5ac70f0Sopenharmony_ci		snd_config_iterator_t j, jnext;
785d5ac70f0Sopenharmony_ci		snd_config_t *in = snd_config_iterator_entry(i);
786d5ac70f0Sopenharmony_ci
787d5ac70f0Sopenharmony_ci		if (snd_config_get_id(in, &id) < 0)
788d5ac70f0Sopenharmony_ci			continue;
789d5ac70f0Sopenharmony_ci		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
790d5ac70f0Sopenharmony_ci			goto err;
791d5ac70f0Sopenharmony_ci		snd_config_for_each(j, jnext, in) {
792d5ac70f0Sopenharmony_ci			int ch, k, found;
793d5ac70f0Sopenharmony_ci			long schannel;
794d5ac70f0Sopenharmony_ci			snd_config_t *jnode = snd_config_iterator_entry(j);
795d5ac70f0Sopenharmony_ci			if (snd_config_get_id(jnode, &id) < 0)
796d5ac70f0Sopenharmony_ci				continue;
797d5ac70f0Sopenharmony_ci			if (safe_strtol(id, &schannel) >= 0)
798d5ac70f0Sopenharmony_ci				continue;
799d5ac70f0Sopenharmony_ci			ch = (int) snd_pcm_chmap_from_string(id);
800d5ac70f0Sopenharmony_ci			if (ch == -1)
801d5ac70f0Sopenharmony_ci				goto err;
802d5ac70f0Sopenharmony_ci
803d5ac70f0Sopenharmony_ci			found = 0;
804d5ac70f0Sopenharmony_ci			for (k = 0; k < (int) chmap->channels; k++)
805d5ac70f0Sopenharmony_ci				if (ch == (int) chmap->pos[k]) {
806d5ac70f0Sopenharmony_ci					found = 1;
807d5ac70f0Sopenharmony_ci					break;
808d5ac70f0Sopenharmony_ci				}
809d5ac70f0Sopenharmony_ci			if (found)
810d5ac70f0Sopenharmony_ci				continue;
811d5ac70f0Sopenharmony_ci
812d5ac70f0Sopenharmony_ci			if (chmap->channels >= MAX_CHMAP_CHANNELS) {
813d5ac70f0Sopenharmony_ci				SNDERR("Too many channels in ttable chmap");
814d5ac70f0Sopenharmony_ci				goto err;
815d5ac70f0Sopenharmony_ci			}
816d5ac70f0Sopenharmony_ci			chmap->pos[chmap->channels++] = ch;
817d5ac70f0Sopenharmony_ci		}
818d5ac70f0Sopenharmony_ci	}
819d5ac70f0Sopenharmony_ci
820d5ac70f0Sopenharmony_ci	if (chmap->channels == 0) {
821d5ac70f0Sopenharmony_ci		free(chmap);
822d5ac70f0Sopenharmony_ci		chmap = NULL;
823d5ac70f0Sopenharmony_ci	}
824d5ac70f0Sopenharmony_ci	*tt_chmap = chmap;
825d5ac70f0Sopenharmony_ci	return 0;
826d5ac70f0Sopenharmony_ci
827d5ac70f0Sopenharmony_cierr:
828d5ac70f0Sopenharmony_ci	*tt_chmap = NULL;
829d5ac70f0Sopenharmony_ci	free(chmap);
830d5ac70f0Sopenharmony_ci	return -EINVAL;
831d5ac70f0Sopenharmony_ci}
832d5ac70f0Sopenharmony_ci
833d5ac70f0Sopenharmony_cistatic int find_matching_chmap(snd_pcm_chmap_query_t **chmaps,
834d5ac70f0Sopenharmony_ci			       snd_pcm_chmap_t *tt_chmap,
835d5ac70f0Sopenharmony_ci			       snd_pcm_chmap_t **found_chmap, int *schannels)
836d5ac70f0Sopenharmony_ci{
837d5ac70f0Sopenharmony_ci	int i;
838d5ac70f0Sopenharmony_ci
839d5ac70f0Sopenharmony_ci	*found_chmap = NULL;
840d5ac70f0Sopenharmony_ci
841d5ac70f0Sopenharmony_ci	if (chmaps == NULL)
842d5ac70f0Sopenharmony_ci		return 0; /* chmap API not supported for this slave */
843d5ac70f0Sopenharmony_ci
844d5ac70f0Sopenharmony_ci	for (i = 0; chmaps[i]; i++) {
845d5ac70f0Sopenharmony_ci		unsigned int j, k;
846d5ac70f0Sopenharmony_ci		int match = 1;
847d5ac70f0Sopenharmony_ci		snd_pcm_chmap_t *c = &chmaps[i]->map;
848d5ac70f0Sopenharmony_ci		if (*schannels >= 0 && (int) c->channels != *schannels)
849d5ac70f0Sopenharmony_ci			continue;
850d5ac70f0Sopenharmony_ci
851d5ac70f0Sopenharmony_ci		for (j = 0; j < tt_chmap->channels; j++) {
852d5ac70f0Sopenharmony_ci			int found = 0;
853d5ac70f0Sopenharmony_ci			unsigned int ch = tt_chmap->pos[j];
854d5ac70f0Sopenharmony_ci			for (k = 0; k < c->channels; k++)
855d5ac70f0Sopenharmony_ci				if (c->pos[k] == ch) {
856d5ac70f0Sopenharmony_ci					found = 1;
857d5ac70f0Sopenharmony_ci					break;
858d5ac70f0Sopenharmony_ci				}
859d5ac70f0Sopenharmony_ci			if (!found) {
860d5ac70f0Sopenharmony_ci				match = 0;
861d5ac70f0Sopenharmony_ci				break;
862d5ac70f0Sopenharmony_ci			}
863d5ac70f0Sopenharmony_ci		}
864d5ac70f0Sopenharmony_ci
865d5ac70f0Sopenharmony_ci		if (match) {
866d5ac70f0Sopenharmony_ci			int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int);
867d5ac70f0Sopenharmony_ci			*found_chmap = malloc(size);
868d5ac70f0Sopenharmony_ci			if (!*found_chmap) {
869d5ac70f0Sopenharmony_ci				return -ENOMEM;
870d5ac70f0Sopenharmony_ci			}
871d5ac70f0Sopenharmony_ci			memcpy(*found_chmap, c, size);
872d5ac70f0Sopenharmony_ci			*schannels = c->channels;
873d5ac70f0Sopenharmony_ci			break;
874d5ac70f0Sopenharmony_ci		}
875d5ac70f0Sopenharmony_ci	}
876d5ac70f0Sopenharmony_ci
877d5ac70f0Sopenharmony_ci	if (*found_chmap == NULL) {
878d5ac70f0Sopenharmony_ci		SNDERR("Found no matching channel map");
879d5ac70f0Sopenharmony_ci		return -EINVAL;
880d5ac70f0Sopenharmony_ci	}
881d5ac70f0Sopenharmony_ci	return 0;
882d5ac70f0Sopenharmony_ci}
883d5ac70f0Sopenharmony_ci
884d5ac70f0Sopenharmony_cistatic int route_chmap_init(snd_pcm_t *pcm)
885d5ac70f0Sopenharmony_ci{
886d5ac70f0Sopenharmony_ci	int set_map = 0;
887d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *current;
888d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route = pcm->private_data;
889d5ac70f0Sopenharmony_ci	if (!route->chmap)
890d5ac70f0Sopenharmony_ci		return 0;
891d5ac70f0Sopenharmony_ci	if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
892d5ac70f0Sopenharmony_ci		return 0;
893d5ac70f0Sopenharmony_ci
894d5ac70f0Sopenharmony_ci	/* Check if we really need to set the chmap or not.
895d5ac70f0Sopenharmony_ci	   This is important in case set_chmap is not implemented. */
896d5ac70f0Sopenharmony_ci	current = snd_pcm_get_chmap(route->plug.gen.slave);
897d5ac70f0Sopenharmony_ci	if (!current)
898d5ac70f0Sopenharmony_ci		return -ENOSYS;
899d5ac70f0Sopenharmony_ci	if (current->channels != route->chmap->channels)
900d5ac70f0Sopenharmony_ci		set_map = 1;
901d5ac70f0Sopenharmony_ci	else
902d5ac70f0Sopenharmony_ci		set_map = memcmp(current->pos, route->chmap->pos,
903d5ac70f0Sopenharmony_ci				 current->channels);
904d5ac70f0Sopenharmony_ci	free(current);
905d5ac70f0Sopenharmony_ci	if (!set_map)
906d5ac70f0Sopenharmony_ci		return 0;
907d5ac70f0Sopenharmony_ci
908d5ac70f0Sopenharmony_ci	return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
909d5ac70f0Sopenharmony_ci}
910d5ac70f0Sopenharmony_ci
911d5ac70f0Sopenharmony_ci
912d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_route_ops = {
913d5ac70f0Sopenharmony_ci	.close = snd_pcm_route_close,
914d5ac70f0Sopenharmony_ci	.info = snd_pcm_generic_info,
915d5ac70f0Sopenharmony_ci	.hw_refine = snd_pcm_route_hw_refine,
916d5ac70f0Sopenharmony_ci	.hw_params = snd_pcm_route_hw_params,
917d5ac70f0Sopenharmony_ci	.hw_free = snd_pcm_generic_hw_free,
918d5ac70f0Sopenharmony_ci	.sw_params = snd_pcm_generic_sw_params,
919d5ac70f0Sopenharmony_ci	.channel_info = snd_pcm_generic_channel_info,
920d5ac70f0Sopenharmony_ci	.dump = snd_pcm_route_dump,
921d5ac70f0Sopenharmony_ci	.nonblock = snd_pcm_generic_nonblock,
922d5ac70f0Sopenharmony_ci	.async = snd_pcm_generic_async,
923d5ac70f0Sopenharmony_ci	.mmap = snd_pcm_generic_mmap,
924d5ac70f0Sopenharmony_ci	.munmap = snd_pcm_generic_munmap,
925d5ac70f0Sopenharmony_ci	.query_chmaps = snd_pcm_route_query_chmaps,
926d5ac70f0Sopenharmony_ci	.get_chmap = snd_pcm_route_get_chmap,
927d5ac70f0Sopenharmony_ci	.set_chmap = NULL, /* NYI */
928d5ac70f0Sopenharmony_ci};
929d5ac70f0Sopenharmony_ci
930d5ac70f0Sopenharmony_cistatic int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
931d5ac70f0Sopenharmony_ci			     unsigned int tt_ssize,
932d5ac70f0Sopenharmony_ci			     snd_pcm_route_ttable_entry_t *ttable,
933d5ac70f0Sopenharmony_ci			     unsigned int tt_cused, unsigned int tt_sused)
934d5ac70f0Sopenharmony_ci{
935d5ac70f0Sopenharmony_ci	unsigned int src_channel, dst_channel;
936d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_dst_t *dptr;
937d5ac70f0Sopenharmony_ci	unsigned int sused, dused, smul, dmul;
938d5ac70f0Sopenharmony_ci	if (stream == SND_PCM_STREAM_PLAYBACK) {
939d5ac70f0Sopenharmony_ci		sused = tt_cused;
940d5ac70f0Sopenharmony_ci		dused = tt_sused;
941d5ac70f0Sopenharmony_ci		smul = tt_ssize;
942d5ac70f0Sopenharmony_ci		dmul = 1;
943d5ac70f0Sopenharmony_ci	} else {
944d5ac70f0Sopenharmony_ci		sused = tt_sused;
945d5ac70f0Sopenharmony_ci		dused = tt_cused;
946d5ac70f0Sopenharmony_ci		smul = 1;
947d5ac70f0Sopenharmony_ci		dmul = tt_ssize;
948d5ac70f0Sopenharmony_ci	}
949d5ac70f0Sopenharmony_ci	params->ndsts = dused;
950d5ac70f0Sopenharmony_ci	params->nsrcs = sused;
951d5ac70f0Sopenharmony_ci	dptr = calloc(dused, sizeof(*params->dsts));
952d5ac70f0Sopenharmony_ci	if (!dptr)
953d5ac70f0Sopenharmony_ci		return -ENOMEM;
954d5ac70f0Sopenharmony_ci	params->dsts = dptr;
955d5ac70f0Sopenharmony_ci	for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
956d5ac70f0Sopenharmony_ci		snd_pcm_route_ttable_entry_t t = 0;
957d5ac70f0Sopenharmony_ci		int att = 0;
958d5ac70f0Sopenharmony_ci		int nsrcs = 0;
959d5ac70f0Sopenharmony_ci		snd_pcm_route_ttable_src_t srcs[sused];
960d5ac70f0Sopenharmony_ci		for (src_channel = 0; src_channel < sused; ++src_channel) {
961d5ac70f0Sopenharmony_ci			snd_pcm_route_ttable_entry_t v;
962d5ac70f0Sopenharmony_ci			v = ttable[src_channel * smul + dst_channel * dmul];
963d5ac70f0Sopenharmony_ci			if (v != 0) {
964d5ac70f0Sopenharmony_ci				srcs[nsrcs].channel = src_channel;
965d5ac70f0Sopenharmony_ci#if SND_PCM_PLUGIN_ROUTE_FLOAT
966d5ac70f0Sopenharmony_ci				/* Also in user space for non attenuated */
967d5ac70f0Sopenharmony_ci				srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
968d5ac70f0Sopenharmony_ci				srcs[nsrcs].as_float = v;
969d5ac70f0Sopenharmony_ci#else
970d5ac70f0Sopenharmony_ci				assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
971d5ac70f0Sopenharmony_ci				srcs[nsrcs].as_int = v;
972d5ac70f0Sopenharmony_ci#endif
973d5ac70f0Sopenharmony_ci				if (v != SND_PCM_PLUGIN_ROUTE_FULL)
974d5ac70f0Sopenharmony_ci					att = 1;
975d5ac70f0Sopenharmony_ci				t += v;
976d5ac70f0Sopenharmony_ci				nsrcs++;
977d5ac70f0Sopenharmony_ci			}
978d5ac70f0Sopenharmony_ci		}
979d5ac70f0Sopenharmony_ci#if 0
980d5ac70f0Sopenharmony_ci		assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
981d5ac70f0Sopenharmony_ci#endif
982d5ac70f0Sopenharmony_ci		dptr->att = att;
983d5ac70f0Sopenharmony_ci		dptr->nsrcs = nsrcs;
984d5ac70f0Sopenharmony_ci		if (nsrcs == 0)
985d5ac70f0Sopenharmony_ci			dptr->func = snd_pcm_route_convert1_zero;
986d5ac70f0Sopenharmony_ci		else
987d5ac70f0Sopenharmony_ci			dptr->func = snd_pcm_route_convert1_many;
988d5ac70f0Sopenharmony_ci		if (nsrcs > 0) {
989d5ac70f0Sopenharmony_ci			dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
990d5ac70f0Sopenharmony_ci			if (!dptr->srcs)
991d5ac70f0Sopenharmony_ci				return -ENOMEM;
992d5ac70f0Sopenharmony_ci			memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
993d5ac70f0Sopenharmony_ci		} else
994d5ac70f0Sopenharmony_ci			dptr->srcs = 0;
995d5ac70f0Sopenharmony_ci		dptr++;
996d5ac70f0Sopenharmony_ci	}
997d5ac70f0Sopenharmony_ci	return 0;
998d5ac70f0Sopenharmony_ci}
999d5ac70f0Sopenharmony_ci
1000d5ac70f0Sopenharmony_ci/**
1001d5ac70f0Sopenharmony_ci * \brief Creates a new Route & Volume PCM
1002d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1003d5ac70f0Sopenharmony_ci * \param name Name of PCM
1004d5ac70f0Sopenharmony_ci * \param sformat Slave format
1005d5ac70f0Sopenharmony_ci * \param schannels Slave channels
1006d5ac70f0Sopenharmony_ci * \param ttable Attenuation table
1007d5ac70f0Sopenharmony_ci * \param tt_ssize Attenuation table - slave size
1008d5ac70f0Sopenharmony_ci * \param tt_cused Attenuation table - client used count
1009d5ac70f0Sopenharmony_ci * \param tt_sused Attenuation table - slave used count
1010d5ac70f0Sopenharmony_ci * \param slave Slave PCM handle
1011d5ac70f0Sopenharmony_ci * \param close_slave When set, the slave PCM handle is closed with copy PCM
1012d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1013d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1014d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1015d5ac70f0Sopenharmony_ci *          changed in future.
1016d5ac70f0Sopenharmony_ci */
1017d5ac70f0Sopenharmony_ciint snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1018d5ac70f0Sopenharmony_ci		       snd_pcm_format_t sformat, int schannels,
1019d5ac70f0Sopenharmony_ci		       snd_pcm_route_ttable_entry_t *ttable,
1020d5ac70f0Sopenharmony_ci		       unsigned int tt_ssize,
1021d5ac70f0Sopenharmony_ci		       unsigned int tt_cused, unsigned int tt_sused,
1022d5ac70f0Sopenharmony_ci		       snd_pcm_t *slave, int close_slave)
1023d5ac70f0Sopenharmony_ci{
1024d5ac70f0Sopenharmony_ci	snd_pcm_t *pcm;
1025d5ac70f0Sopenharmony_ci	snd_pcm_route_t *route;
1026d5ac70f0Sopenharmony_ci	int err;
1027d5ac70f0Sopenharmony_ci	assert(pcmp && slave && ttable);
1028d5ac70f0Sopenharmony_ci	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1029d5ac70f0Sopenharmony_ci	    snd_pcm_format_linear(sformat) != 1)
1030d5ac70f0Sopenharmony_ci		return -EINVAL;
1031d5ac70f0Sopenharmony_ci	route = calloc(1, sizeof(snd_pcm_route_t));
1032d5ac70f0Sopenharmony_ci	if (!route) {
1033d5ac70f0Sopenharmony_ci		return -ENOMEM;
1034d5ac70f0Sopenharmony_ci	}
1035d5ac70f0Sopenharmony_ci	snd_pcm_plugin_init(&route->plug);
1036d5ac70f0Sopenharmony_ci	route->sformat = sformat;
1037d5ac70f0Sopenharmony_ci	route->schannels = schannels;
1038d5ac70f0Sopenharmony_ci	route->plug.read = snd_pcm_route_read_areas;
1039d5ac70f0Sopenharmony_ci	route->plug.write = snd_pcm_route_write_areas;
1040d5ac70f0Sopenharmony_ci	route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
1041d5ac70f0Sopenharmony_ci	route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
1042d5ac70f0Sopenharmony_ci	route->plug.gen.slave = slave;
1043d5ac70f0Sopenharmony_ci	route->plug.gen.close_slave = close_slave;
1044d5ac70f0Sopenharmony_ci	route->plug.init = route_chmap_init;
1045d5ac70f0Sopenharmony_ci
1046d5ac70f0Sopenharmony_ci	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
1047d5ac70f0Sopenharmony_ci	if (err < 0) {
1048d5ac70f0Sopenharmony_ci		free(route);
1049d5ac70f0Sopenharmony_ci		return err;
1050d5ac70f0Sopenharmony_ci	}
1051d5ac70f0Sopenharmony_ci	pcm->ops = &snd_pcm_route_ops;
1052d5ac70f0Sopenharmony_ci	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
1053d5ac70f0Sopenharmony_ci	pcm->private_data = route;
1054d5ac70f0Sopenharmony_ci	pcm->poll_fd = slave->poll_fd;
1055d5ac70f0Sopenharmony_ci	pcm->poll_events = slave->poll_events;
1056d5ac70f0Sopenharmony_ci	pcm->tstamp_type = slave->tstamp_type;
1057d5ac70f0Sopenharmony_ci	snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
1058d5ac70f0Sopenharmony_ci	snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
1059d5ac70f0Sopenharmony_ci	err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
1060d5ac70f0Sopenharmony_ci	if (err < 0) {
1061d5ac70f0Sopenharmony_ci		snd_pcm_close(pcm);
1062d5ac70f0Sopenharmony_ci		return err;
1063d5ac70f0Sopenharmony_ci	}
1064d5ac70f0Sopenharmony_ci	*pcmp = pcm;
1065d5ac70f0Sopenharmony_ci
1066d5ac70f0Sopenharmony_ci	return 0;
1067d5ac70f0Sopenharmony_ci}
1068d5ac70f0Sopenharmony_ci
1069d5ac70f0Sopenharmony_cistatic int _snd_pcm_route_determine_ttable(snd_config_t *tt,
1070d5ac70f0Sopenharmony_ci					   unsigned int *tt_csize,
1071d5ac70f0Sopenharmony_ci					   unsigned int *tt_ssize,
1072d5ac70f0Sopenharmony_ci					   snd_pcm_chmap_t *chmap)
1073d5ac70f0Sopenharmony_ci{
1074d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, inext;
1075d5ac70f0Sopenharmony_ci	long csize = 0, ssize = 0;
1076d5ac70f0Sopenharmony_ci	int err;
1077d5ac70f0Sopenharmony_ci
1078d5ac70f0Sopenharmony_ci	assert(tt && tt_csize && tt_ssize);
1079d5ac70f0Sopenharmony_ci	snd_config_for_each(i, inext, tt) {
1080d5ac70f0Sopenharmony_ci		snd_config_t *in = snd_config_iterator_entry(i);
1081d5ac70f0Sopenharmony_ci		snd_config_iterator_t j, jnext;
1082d5ac70f0Sopenharmony_ci		long cchannel;
1083d5ac70f0Sopenharmony_ci		const char *id;
1084d5ac70f0Sopenharmony_ci		if (snd_config_get_id(in, &id) < 0)
1085d5ac70f0Sopenharmony_ci			continue;
1086d5ac70f0Sopenharmony_ci		err = safe_strtol(id, &cchannel);
1087d5ac70f0Sopenharmony_ci		if (err < 0) {
1088d5ac70f0Sopenharmony_ci			SNDERR("Invalid client channel: %s", id);
1089d5ac70f0Sopenharmony_ci			return -EINVAL;
1090d5ac70f0Sopenharmony_ci		}
1091d5ac70f0Sopenharmony_ci		if (cchannel + 1 > csize)
1092d5ac70f0Sopenharmony_ci			csize = cchannel + 1;
1093d5ac70f0Sopenharmony_ci		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1094d5ac70f0Sopenharmony_ci			return -EINVAL;
1095d5ac70f0Sopenharmony_ci		snd_config_for_each(j, jnext, in) {
1096d5ac70f0Sopenharmony_ci			snd_config_t *jnode = snd_config_iterator_entry(j);
1097d5ac70f0Sopenharmony_ci			long schannel;
1098d5ac70f0Sopenharmony_ci			const char *id;
1099d5ac70f0Sopenharmony_ci			if (snd_config_get_id(jnode, &id) < 0)
1100d5ac70f0Sopenharmony_ci				continue;
1101d5ac70f0Sopenharmony_ci			err = strtochannel(id, chmap, &schannel, 1);
1102d5ac70f0Sopenharmony_ci			if (err < 0) {
1103d5ac70f0Sopenharmony_ci				SNDERR("Invalid slave channel: %s", id);
1104d5ac70f0Sopenharmony_ci				return -EINVAL;
1105d5ac70f0Sopenharmony_ci			}
1106d5ac70f0Sopenharmony_ci			if (schannel + 1 > ssize)
1107d5ac70f0Sopenharmony_ci				ssize = schannel + 1;
1108d5ac70f0Sopenharmony_ci		}
1109d5ac70f0Sopenharmony_ci	}
1110d5ac70f0Sopenharmony_ci	if (csize == 0 || ssize == 0) {
1111d5ac70f0Sopenharmony_ci		SNDERR("Invalid null ttable configuration");
1112d5ac70f0Sopenharmony_ci		return -EINVAL;
1113d5ac70f0Sopenharmony_ci	}
1114d5ac70f0Sopenharmony_ci	*tt_csize = csize;
1115d5ac70f0Sopenharmony_ci	*tt_ssize = ssize;
1116d5ac70f0Sopenharmony_ci	return 0;
1117d5ac70f0Sopenharmony_ci}
1118d5ac70f0Sopenharmony_ci
1119d5ac70f0Sopenharmony_ci/**
1120d5ac70f0Sopenharmony_ci * \brief Determine route matrix sizes
1121d5ac70f0Sopenharmony_ci * \param tt Configuration root describing route matrix
1122d5ac70f0Sopenharmony_ci * \param tt_csize Returned client size in elements
1123d5ac70f0Sopenharmony_ci * \param tt_ssize Returned slave size in elements
1124d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1125d5ac70f0Sopenharmony_ci */
1126d5ac70f0Sopenharmony_ciint snd_pcm_route_determine_ttable(snd_config_t *tt,
1127d5ac70f0Sopenharmony_ci				   unsigned int *tt_csize,
1128d5ac70f0Sopenharmony_ci				   unsigned int *tt_ssize)
1129d5ac70f0Sopenharmony_ci{
1130d5ac70f0Sopenharmony_ci	return _snd_pcm_route_determine_ttable(tt, tt_csize, tt_ssize, NULL);
1131d5ac70f0Sopenharmony_ci}
1132d5ac70f0Sopenharmony_ci
1133d5ac70f0Sopenharmony_ci/**
1134d5ac70f0Sopenharmony_ci * \brief Load route matrix
1135d5ac70f0Sopenharmony_ci * \param tt Configuration root describing route matrix
1136d5ac70f0Sopenharmony_ci * \param ttable Returned route matrix
1137d5ac70f0Sopenharmony_ci * \param tt_csize Client size in elements
1138d5ac70f0Sopenharmony_ci * \param tt_ssize Slave size in elements
1139d5ac70f0Sopenharmony_ci * \param tt_cused Used client elements
1140d5ac70f0Sopenharmony_ci * \param tt_sused Used slave elements
1141d5ac70f0Sopenharmony_ci * \param schannels Slave channels
1142d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1143d5ac70f0Sopenharmony_ci */
1144d5ac70f0Sopenharmony_cistatic int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1145d5ac70f0Sopenharmony_ci				      unsigned int tt_csize, unsigned int tt_ssize,
1146d5ac70f0Sopenharmony_ci				      unsigned int *tt_cused, unsigned int *tt_sused,
1147d5ac70f0Sopenharmony_ci				      int schannels, snd_pcm_chmap_t *chmap)
1148d5ac70f0Sopenharmony_ci{
1149d5ac70f0Sopenharmony_ci	int cused = -1;
1150d5ac70f0Sopenharmony_ci	int sused = -1;
1151d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, inext;
1152d5ac70f0Sopenharmony_ci	unsigned int k;
1153d5ac70f0Sopenharmony_ci	int err;
1154d5ac70f0Sopenharmony_ci
1155d5ac70f0Sopenharmony_ci	long *scha = alloca(tt_ssize * sizeof(long));
1156d5ac70f0Sopenharmony_ci	if (scha == NULL)
1157d5ac70f0Sopenharmony_ci		return -ENOMEM;
1158d5ac70f0Sopenharmony_ci
1159d5ac70f0Sopenharmony_ci	for (k = 0; k < tt_csize * tt_ssize; ++k)
1160d5ac70f0Sopenharmony_ci		ttable[k] = 0.0;
1161d5ac70f0Sopenharmony_ci	snd_config_for_each(i, inext, tt) {
1162d5ac70f0Sopenharmony_ci		snd_config_t *in = snd_config_iterator_entry(i);
1163d5ac70f0Sopenharmony_ci		snd_config_iterator_t j, jnext;
1164d5ac70f0Sopenharmony_ci		long cchannel;
1165d5ac70f0Sopenharmony_ci		const char *id;
1166d5ac70f0Sopenharmony_ci		if (snd_config_get_id(in, &id) < 0)
1167d5ac70f0Sopenharmony_ci			continue;
1168d5ac70f0Sopenharmony_ci		err = safe_strtol(id, &cchannel);
1169d5ac70f0Sopenharmony_ci		if (err < 0 ||
1170d5ac70f0Sopenharmony_ci		    cchannel < 0 || (unsigned int) cchannel > tt_csize) {
1171d5ac70f0Sopenharmony_ci			SNDERR("Invalid client channel: %s", id);
1172d5ac70f0Sopenharmony_ci			return -EINVAL;
1173d5ac70f0Sopenharmony_ci		}
1174d5ac70f0Sopenharmony_ci		if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
1175d5ac70f0Sopenharmony_ci			return -EINVAL;
1176d5ac70f0Sopenharmony_ci		snd_config_for_each(j, jnext, in) {
1177d5ac70f0Sopenharmony_ci			snd_config_t *jnode = snd_config_iterator_entry(j);
1178d5ac70f0Sopenharmony_ci			double value;
1179d5ac70f0Sopenharmony_ci			int ss;
1180d5ac70f0Sopenharmony_ci			const char *id;
1181d5ac70f0Sopenharmony_ci			if (snd_config_get_id(jnode, &id) < 0)
1182d5ac70f0Sopenharmony_ci				continue;
1183d5ac70f0Sopenharmony_ci
1184d5ac70f0Sopenharmony_ci			ss = strtochannel(id, chmap, scha, tt_ssize);
1185d5ac70f0Sopenharmony_ci			if (ss < 0) {
1186d5ac70f0Sopenharmony_ci				SNDERR("Invalid slave channel: %s", id);
1187d5ac70f0Sopenharmony_ci				return -EINVAL;
1188d5ac70f0Sopenharmony_ci			}
1189d5ac70f0Sopenharmony_ci
1190d5ac70f0Sopenharmony_ci			err = snd_config_get_ireal(jnode, &value);
1191d5ac70f0Sopenharmony_ci			if (err < 0) {
1192d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1193d5ac70f0Sopenharmony_ci				return -EINVAL;
1194d5ac70f0Sopenharmony_ci			}
1195d5ac70f0Sopenharmony_ci
1196d5ac70f0Sopenharmony_ci			for (k = 0; (int) k < ss; k++) {
1197d5ac70f0Sopenharmony_ci				long schannel = scha[k];
1198d5ac70f0Sopenharmony_ci				if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
1199d5ac70f0Sopenharmony_ci				    (schannels > 0 && schannel >= schannels)) {
1200d5ac70f0Sopenharmony_ci					SNDERR("Invalid slave channel: %s", id);
1201d5ac70f0Sopenharmony_ci					return -EINVAL;
1202d5ac70f0Sopenharmony_ci				}
1203d5ac70f0Sopenharmony_ci				ttable[cchannel * tt_ssize + schannel] = value;
1204d5ac70f0Sopenharmony_ci				if (schannel > sused)
1205d5ac70f0Sopenharmony_ci					sused = schannel;
1206d5ac70f0Sopenharmony_ci			}
1207d5ac70f0Sopenharmony_ci		}
1208d5ac70f0Sopenharmony_ci		if (cchannel > cused)
1209d5ac70f0Sopenharmony_ci			cused = cchannel;
1210d5ac70f0Sopenharmony_ci	}
1211d5ac70f0Sopenharmony_ci	*tt_sused = sused + 1;
1212d5ac70f0Sopenharmony_ci	*tt_cused = cused + 1;
1213d5ac70f0Sopenharmony_ci	return 0;
1214d5ac70f0Sopenharmony_ci}
1215d5ac70f0Sopenharmony_ci
1216d5ac70f0Sopenharmony_ci/**
1217d5ac70f0Sopenharmony_ci * \brief Load route matrix
1218d5ac70f0Sopenharmony_ci * \param tt Configuration root describing route matrix
1219d5ac70f0Sopenharmony_ci * \param ttable Returned route matrix
1220d5ac70f0Sopenharmony_ci * \param tt_csize Client size in elements
1221d5ac70f0Sopenharmony_ci * \param tt_ssize Slave size in elements
1222d5ac70f0Sopenharmony_ci * \param tt_cused Used client elements
1223d5ac70f0Sopenharmony_ci * \param tt_sused Used slave elements
1224d5ac70f0Sopenharmony_ci * \param schannels Slave channels
1225d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1226d5ac70f0Sopenharmony_ci */
1227d5ac70f0Sopenharmony_ciint snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
1228d5ac70f0Sopenharmony_ci			      unsigned int tt_csize, unsigned int tt_ssize,
1229d5ac70f0Sopenharmony_ci			      unsigned int *tt_cused, unsigned int *tt_sused,
1230d5ac70f0Sopenharmony_ci			      int schannels)
1231d5ac70f0Sopenharmony_ci{
1232d5ac70f0Sopenharmony_ci	return _snd_pcm_route_load_ttable(tt, ttable, tt_csize, tt_ssize,
1233d5ac70f0Sopenharmony_ci					  tt_cused, tt_sused, schannels, NULL);
1234d5ac70f0Sopenharmony_ci}
1235d5ac70f0Sopenharmony_ci
1236d5ac70f0Sopenharmony_ci/*! \page pcm_plugins
1237d5ac70f0Sopenharmony_ci
1238d5ac70f0Sopenharmony_ci\section pcm_plugins_route Plugin: Route & Volume
1239d5ac70f0Sopenharmony_ci
1240d5ac70f0Sopenharmony_ciThis plugin converts channels and applies volume during the conversion.
1241d5ac70f0Sopenharmony_ciThe format and rate must match for both of them.
1242d5ac70f0Sopenharmony_ci
1243d5ac70f0Sopenharmony_ciSCHANNEL can be a channel name instead of a number (e g FL, LFE).
1244d5ac70f0Sopenharmony_ciIf so, a matching channel map will be selected for the slave.
1245d5ac70f0Sopenharmony_ci
1246d5ac70f0Sopenharmony_ci\code
1247d5ac70f0Sopenharmony_cipcm.name {
1248d5ac70f0Sopenharmony_ci        type route              # Route & Volume conversion PCM
1249d5ac70f0Sopenharmony_ci        slave STR               # Slave name
1250d5ac70f0Sopenharmony_ci        # or
1251d5ac70f0Sopenharmony_ci        slave {                 # Slave definition
1252d5ac70f0Sopenharmony_ci                pcm STR         # Slave PCM name
1253d5ac70f0Sopenharmony_ci                # or
1254d5ac70f0Sopenharmony_ci                pcm { }         # Slave PCM definition
1255d5ac70f0Sopenharmony_ci                [format STR]    # Slave format
1256d5ac70f0Sopenharmony_ci                [channels INT]  # Slave channels
1257d5ac70f0Sopenharmony_ci        }
1258d5ac70f0Sopenharmony_ci        ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1259d5ac70f0Sopenharmony_ci                CCHANNEL {
1260d5ac70f0Sopenharmony_ci                        SCHANNEL REAL   # route value (0.0 - 1.0)
1261d5ac70f0Sopenharmony_ci                }
1262d5ac70f0Sopenharmony_ci        }
1263d5ac70f0Sopenharmony_ci        [chmap MAP]             # Override channel maps; MAP is a string array
1264d5ac70f0Sopenharmony_ci}
1265d5ac70f0Sopenharmony_ci\endcode
1266d5ac70f0Sopenharmony_ci
1267d5ac70f0Sopenharmony_ci\subsection pcm_plugins_route_funcref Function reference
1268d5ac70f0Sopenharmony_ci
1269d5ac70f0Sopenharmony_ci<UL>
1270d5ac70f0Sopenharmony_ci  <LI>snd_pcm_route_open()
1271d5ac70f0Sopenharmony_ci  <LI>_snd_pcm_route_open()
1272d5ac70f0Sopenharmony_ci</UL>
1273d5ac70f0Sopenharmony_ci
1274d5ac70f0Sopenharmony_ci*/
1275d5ac70f0Sopenharmony_ci
1276d5ac70f0Sopenharmony_ci/**
1277d5ac70f0Sopenharmony_ci * \brief Creates a new Route & Volume PCM
1278d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1279d5ac70f0Sopenharmony_ci * \param name Name of PCM
1280d5ac70f0Sopenharmony_ci * \param root Root configuration node
1281d5ac70f0Sopenharmony_ci * \param conf Configuration node with Route & Volume PCM description
1282d5ac70f0Sopenharmony_ci * \param stream Stream type
1283d5ac70f0Sopenharmony_ci * \param mode Stream mode
1284d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1285d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1286d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1287d5ac70f0Sopenharmony_ci *          changed in future.
1288d5ac70f0Sopenharmony_ci */
1289d5ac70f0Sopenharmony_ciint _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
1290d5ac70f0Sopenharmony_ci			snd_config_t *root, snd_config_t *conf,
1291d5ac70f0Sopenharmony_ci			snd_pcm_stream_t stream, int mode)
1292d5ac70f0Sopenharmony_ci{
1293d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1294d5ac70f0Sopenharmony_ci	int err;
1295d5ac70f0Sopenharmony_ci	snd_pcm_t *spcm;
1296d5ac70f0Sopenharmony_ci	snd_config_t *slave = NULL, *sconf;
1297d5ac70f0Sopenharmony_ci	snd_pcm_chmap_t *tt_chmap = NULL, *chmap = NULL;
1298d5ac70f0Sopenharmony_ci	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1299d5ac70f0Sopenharmony_ci	int schannels = -1;
1300d5ac70f0Sopenharmony_ci	snd_config_t *tt = NULL;
1301d5ac70f0Sopenharmony_ci	snd_pcm_route_ttable_entry_t *ttable = NULL;
1302d5ac70f0Sopenharmony_ci	unsigned int csize, ssize;
1303d5ac70f0Sopenharmony_ci	unsigned int cused, sused;
1304d5ac70f0Sopenharmony_ci	snd_pcm_chmap_query_t **chmaps = NULL;
1305d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
1306d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1307d5ac70f0Sopenharmony_ci		const char *id;
1308d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1309d5ac70f0Sopenharmony_ci			continue;
1310d5ac70f0Sopenharmony_ci		if (snd_pcm_conf_generic_id(id))
1311d5ac70f0Sopenharmony_ci			continue;
1312d5ac70f0Sopenharmony_ci		if (strcmp(id, "slave") == 0) {
1313d5ac70f0Sopenharmony_ci			slave = n;
1314d5ac70f0Sopenharmony_ci			continue;
1315d5ac70f0Sopenharmony_ci		}
1316d5ac70f0Sopenharmony_ci		if (strcmp(id, "ttable") == 0) {
1317d5ac70f0Sopenharmony_ci			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1318d5ac70f0Sopenharmony_ci				SNDERR("Invalid type for %s", id);
1319d5ac70f0Sopenharmony_ci				snd_pcm_free_chmaps(chmaps);
1320d5ac70f0Sopenharmony_ci				return -EINVAL;
1321d5ac70f0Sopenharmony_ci			}
1322d5ac70f0Sopenharmony_ci			tt = n;
1323d5ac70f0Sopenharmony_ci			continue;
1324d5ac70f0Sopenharmony_ci		}
1325d5ac70f0Sopenharmony_ci		if (strcmp(id, "chmap") == 0) {
1326d5ac70f0Sopenharmony_ci			chmaps = _snd_pcm_parse_config_chmaps(n);
1327d5ac70f0Sopenharmony_ci			if (!chmaps) {
1328d5ac70f0Sopenharmony_ci				SNDERR("Invalid channel map for %s", id);
1329d5ac70f0Sopenharmony_ci				return -EINVAL;
1330d5ac70f0Sopenharmony_ci			}
1331d5ac70f0Sopenharmony_ci			continue;
1332d5ac70f0Sopenharmony_ci		}
1333d5ac70f0Sopenharmony_ci		SNDERR("Unknown field %s", id);
1334d5ac70f0Sopenharmony_ci		return -EINVAL;
1335d5ac70f0Sopenharmony_ci	}
1336d5ac70f0Sopenharmony_ci	if (!slave) {
1337d5ac70f0Sopenharmony_ci		SNDERR("slave is not defined");
1338d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1339d5ac70f0Sopenharmony_ci		return -EINVAL;
1340d5ac70f0Sopenharmony_ci	}
1341d5ac70f0Sopenharmony_ci	if (!tt) {
1342d5ac70f0Sopenharmony_ci		SNDERR("ttable is not defined");
1343d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1344d5ac70f0Sopenharmony_ci		return -EINVAL;
1345d5ac70f0Sopenharmony_ci	}
1346d5ac70f0Sopenharmony_ci	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1347d5ac70f0Sopenharmony_ci				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1348d5ac70f0Sopenharmony_ci				 SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
1349d5ac70f0Sopenharmony_ci	if (err < 0) {
1350d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1351d5ac70f0Sopenharmony_ci		return err;
1352d5ac70f0Sopenharmony_ci	}
1353d5ac70f0Sopenharmony_ci	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1354d5ac70f0Sopenharmony_ci	    snd_pcm_format_linear(sformat) != 1) {
1355d5ac70f0Sopenharmony_ci	    	snd_config_delete(sconf);
1356d5ac70f0Sopenharmony_ci		SNDERR("slave format is not linear");
1357d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1358d5ac70f0Sopenharmony_ci		return -EINVAL;
1359d5ac70f0Sopenharmony_ci	}
1360d5ac70f0Sopenharmony_ci
1361d5ac70f0Sopenharmony_ci	err = determine_chmap(tt, &tt_chmap);
1362d5ac70f0Sopenharmony_ci	if (err < 0) {
1363d5ac70f0Sopenharmony_ci		free(ttable);
1364d5ac70f0Sopenharmony_ci		return err;
1365d5ac70f0Sopenharmony_ci	}
1366d5ac70f0Sopenharmony_ci
1367d5ac70f0Sopenharmony_ci	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1368d5ac70f0Sopenharmony_ci	snd_config_delete(sconf);
1369d5ac70f0Sopenharmony_ci	if (err < 0) {
1370d5ac70f0Sopenharmony_ci		free(tt_chmap);
1371d5ac70f0Sopenharmony_ci		free(ttable);
1372d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1373d5ac70f0Sopenharmony_ci		return err;
1374d5ac70f0Sopenharmony_ci	}
1375d5ac70f0Sopenharmony_ci
1376d5ac70f0Sopenharmony_ci	if (tt_chmap) {
1377d5ac70f0Sopenharmony_ci		if (!chmaps)
1378d5ac70f0Sopenharmony_ci			chmaps = snd_pcm_query_chmaps(spcm);
1379d5ac70f0Sopenharmony_ci		if (chmaps)
1380d5ac70f0Sopenharmony_ci			err = find_matching_chmap(chmaps, tt_chmap, &chmap,
1381d5ac70f0Sopenharmony_ci						  &schannels);
1382d5ac70f0Sopenharmony_ci		free(tt_chmap);
1383d5ac70f0Sopenharmony_ci		if (chmaps && err < 0) {
1384d5ac70f0Sopenharmony_ci			snd_pcm_free_chmaps(chmaps);
1385d5ac70f0Sopenharmony_ci			snd_pcm_close(spcm);
1386d5ac70f0Sopenharmony_ci			return err;
1387d5ac70f0Sopenharmony_ci		}
1388d5ac70f0Sopenharmony_ci	}
1389d5ac70f0Sopenharmony_ci
1390d5ac70f0Sopenharmony_ci	err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap);
1391d5ac70f0Sopenharmony_ci	if (err < 0) {
1392d5ac70f0Sopenharmony_ci		free(chmap);
1393d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1394d5ac70f0Sopenharmony_ci		snd_pcm_close(spcm);
1395d5ac70f0Sopenharmony_ci		return err;
1396d5ac70f0Sopenharmony_ci	}
1397d5ac70f0Sopenharmony_ci	ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
1398d5ac70f0Sopenharmony_ci	if (ttable == NULL) {
1399d5ac70f0Sopenharmony_ci		free(chmap);
1400d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1401d5ac70f0Sopenharmony_ci		snd_pcm_close(spcm);
1402d5ac70f0Sopenharmony_ci		return -ENOMEM;
1403d5ac70f0Sopenharmony_ci	}
1404d5ac70f0Sopenharmony_ci	err = _snd_pcm_route_load_ttable(tt, ttable, csize, ssize,
1405d5ac70f0Sopenharmony_ci					&cused, &sused, schannels, chmap);
1406d5ac70f0Sopenharmony_ci	if (err < 0) {
1407d5ac70f0Sopenharmony_ci		free(chmap);
1408d5ac70f0Sopenharmony_ci		free(ttable);
1409d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1410d5ac70f0Sopenharmony_ci		snd_pcm_close(spcm);
1411d5ac70f0Sopenharmony_ci		return err;
1412d5ac70f0Sopenharmony_ci	}
1413d5ac70f0Sopenharmony_ci
1414d5ac70f0Sopenharmony_ci	err = snd_pcm_route_open(pcmp, name, sformat, schannels,
1415d5ac70f0Sopenharmony_ci				 ttable, ssize,
1416d5ac70f0Sopenharmony_ci				 cused, sused,
1417d5ac70f0Sopenharmony_ci				 spcm, 1);
1418d5ac70f0Sopenharmony_ci	free(ttable);
1419d5ac70f0Sopenharmony_ci	if (err < 0) {
1420d5ac70f0Sopenharmony_ci		free(chmap);
1421d5ac70f0Sopenharmony_ci		snd_pcm_free_chmaps(chmaps);
1422d5ac70f0Sopenharmony_ci		snd_pcm_close(spcm);
1423d5ac70f0Sopenharmony_ci	} else {
1424d5ac70f0Sopenharmony_ci		snd_pcm_route_t *route = (*pcmp)->private_data;
1425d5ac70f0Sopenharmony_ci
1426d5ac70f0Sopenharmony_ci		route->chmap = chmap;
1427d5ac70f0Sopenharmony_ci		route->chmap_override = chmaps;
1428d5ac70f0Sopenharmony_ci	}
1429d5ac70f0Sopenharmony_ci
1430d5ac70f0Sopenharmony_ci	return err;
1431d5ac70f0Sopenharmony_ci}
1432d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
1433d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_route_open, SND_PCM_DLSYM_VERSION);
1434d5ac70f0Sopenharmony_ci#endif
1435