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