1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <math.h>
37 #include <aw-alsa-lib/plugin/pcm_route.h>
38 #include "bswap.h"
39 #include "pcm_local.h"
40 #include "pcm_plugin_generic.h"
41
42 #include "plugin_ops.h"
43
44 /* The best possible hack to support missing optimization in gcc 2.7.2.3 */
45 #if SND_PCM_PLUGIN_ROUTE_RESOLUTION & (SND_PCM_PLUGIN_ROUTE_RESOLUTION - 1) != 0
46 #define div(a) a /= SND_PCM_PLUGIN_ROUTE_RESOLUTION
47 #elif SND_PCM_PLUGIN_ROUTE_RESOLUTION == 16
48 #define div(a) a >>= 4
49 #else
50 #error "Add some code here"
51 #endif
52
53 typedef struct {
54 int channel;
55 int as_int;
56 #if SND_PCM_PLUGIN_ROUTE_FLOAT
57 float as_float;
58 #endif
59 } snd_pcm_route_ttable_src_t;
60
61 typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
62
63 typedef struct {
64 enum {UINT64, FLOAT} sum_idx;
65 unsigned int get_idx;
66 unsigned int put_idx;
67 unsigned int conv_idx;
68 int use_getput;
69 unsigned int src_size;
70 snd_pcm_format_t dst_sfmt;
71 unsigned int nsrcs;
72 unsigned int ndsts;
73 snd_pcm_route_ttable_dst_t *dsts;
74 } snd_pcm_route_params_t;
75
76 typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
77 snd_pcm_uframes_t dst_offset,
78 const snd_pcm_channel_area_t *src_areas,
79 snd_pcm_uframes_t src_offset,
80 unsigned int src_channels,
81 snd_pcm_uframes_t frames,
82 const snd_pcm_route_ttable_dst_t *ttable,
83 const snd_pcm_route_params_t *params);
84
85 struct snd_pcm_route_ttable_dst {
86 int att; /* Attenuated */
87 unsigned int nsrcs;
88 snd_pcm_route_ttable_src_t* srcs;
89 route_f func;
90 };
91
92 typedef union {
93 int32_t as_sint32;
94 int64_t as_sint64;
95 #if SND_PCM_PLUGIN_ROUTE_FLOAT
96 float as_float;
97 #endif
98 } sum_t;
99
100 typedef struct {
101 /* This field need to be the first */
102 snd_pcm_plugin_t plug;
103 snd_pcm_format_t sformat;
104 int schannels;
105 snd_pcm_route_params_t params;
106 snd_pcm_chmap_t *chmap;
107 } snd_pcm_route_t;
108
109
snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)110 static void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
111 snd_pcm_uframes_t dst_offset,
112 const snd_pcm_channel_area_t *src_areas,
113 snd_pcm_uframes_t src_offset,
114 unsigned int src_channels,
115 snd_pcm_uframes_t frames,
116 const snd_pcm_route_ttable_dst_t* ttable,
117 const snd_pcm_route_params_t *params)
118 {
119 snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
120 }
121
snd_pcm_route_convert1_one(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)122 static void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
123 snd_pcm_uframes_t dst_offset,
124 const snd_pcm_channel_area_t *src_areas,
125 snd_pcm_uframes_t src_offset,
126 unsigned int src_channels,
127 snd_pcm_uframes_t frames,
128 const snd_pcm_route_ttable_dst_t* ttable,
129 const snd_pcm_route_params_t *params)
130 {
131 #define CONV_LABELS
132 #include "plugin_ops.h"
133 #undef CONV_LABELS
134 void *conv;
135 const snd_pcm_channel_area_t *src_area = 0;
136 unsigned int srcidx;
137 const char *src;
138 char *dst;
139 int src_step, dst_step;
140 for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
141 unsigned int channel = ttable->srcs[srcidx].channel;
142 if (channel >= src_channels)
143 continue;
144 src_area = &src_areas[channel];
145 if (src_area->addr != NULL)
146 break;
147 }
148 if (srcidx == ttable->nsrcs || srcidx == src_channels) {
149 snd_pcm_route_convert1_zero(dst_area, dst_offset,
150 src_areas, src_offset,
151 src_channels,
152 frames, ttable, params);
153 return;
154 }
155
156 conv = conv_labels[params->conv_idx];
157 src = snd_pcm_channel_area_addr(src_area, src_offset);
158 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
159 src_step = snd_pcm_channel_area_step(src_area);
160 dst_step = snd_pcm_channel_area_step(dst_area);
161 while (frames-- > 0) {
162 goto *conv;
163 #define CONV_END after
164 #include "plugin_ops.h"
165 #undef CONV_END
166 after:
167 src += src_step;
168 dst += dst_step;
169 }
170 }
171
snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)172 static void snd_pcm_route_convert1_one_getput(const snd_pcm_channel_area_t *dst_area,
173 snd_pcm_uframes_t dst_offset,
174 const snd_pcm_channel_area_t *src_areas,
175 snd_pcm_uframes_t src_offset,
176 unsigned int src_channels,
177 snd_pcm_uframes_t frames,
178 const snd_pcm_route_ttable_dst_t* ttable,
179 const snd_pcm_route_params_t *params)
180 {
181 #define CONV24_LABELS
182 #include "plugin_ops.h"
183 #undef CONV24_LABELS
184 void *get, *put;
185 const snd_pcm_channel_area_t *src_area = 0;
186 unsigned int srcidx;
187 const char *src;
188 char *dst;
189 int src_step, dst_step;
190 u_int32_t sample = 0;
191 for (srcidx = 0; srcidx < ttable->nsrcs && srcidx < src_channels; ++srcidx) {
192 unsigned int channel = ttable->srcs[srcidx].channel;
193 if (channel >= src_channels)
194 continue;
195 src_area = &src_areas[channel];
196 if (src_area->addr != NULL)
197 break;
198 }
199 if (srcidx == ttable->nsrcs || srcidx == src_channels) {
200 snd_pcm_route_convert1_zero(dst_area, dst_offset,
201 src_areas, src_offset,
202 src_channels,
203 frames, ttable, params);
204 return;
205 }
206
207 get = get32_labels[params->get_idx];
208 put = put32_labels[params->put_idx];
209 src = snd_pcm_channel_area_addr(src_area, src_offset);
210 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
211 src_step = snd_pcm_channel_area_step(src_area);
212 dst_step = snd_pcm_channel_area_step(dst_area);
213 while (frames-- > 0) {
214 goto *get;
215 #define CONV24_END after
216 #include "plugin_ops.h"
217 #undef CONV24_END
218 after:
219 src += src_step;
220 dst += dst_step;
221 }
222 }
223
snd_pcm_route_convert1_many(const snd_pcm_channel_area_t * dst_area,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,snd_pcm_uframes_t frames,const snd_pcm_route_ttable_dst_t * ttable,const snd_pcm_route_params_t * params)224 static void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
225 snd_pcm_uframes_t dst_offset,
226 const snd_pcm_channel_area_t *src_areas,
227 snd_pcm_uframes_t src_offset,
228 unsigned int src_channels,
229 snd_pcm_uframes_t frames,
230 const snd_pcm_route_ttable_dst_t* ttable,
231 const snd_pcm_route_params_t *params)
232 {
233 #define GET32_LABELS
234 #define PUT32_LABELS
235 #include "plugin_ops.h"
236 #undef GET32_LABELS
237 #undef PUT32_LABELS
238 static void *const zero_labels[2] = {
239 &&zero_int64,
240 #if SND_PCM_PLUGIN_ROUTE_FLOAT
241 &&zero_float
242 #endif
243 };
244 /* sum_type att */
245 static void *const add_labels[2 * 2] = {
246 &&add_int64_noatt, &&add_int64_att,
247 #if SND_PCM_PLUGIN_ROUTE_FLOAT
248 &&add_float_noatt, &&add_float_att
249 #endif
250 };
251 /* sum_type att */
252 static void *const norm_labels[2 * 2] = {
253 &&norm_int64_noatt,
254 &&norm_int64_att,
255 #if SND_PCM_PLUGIN_ROUTE_FLOAT
256 &&norm_float,
257 &&norm_float,
258 #endif
259 };
260 void *zero, *get32, *add, *norm, *put32;
261 int nsrcs = ttable->nsrcs;
262 char *dst;
263 int dst_step;
264 const char *srcs[nsrcs];
265 int src_steps[nsrcs];
266 snd_pcm_route_ttable_src_t src_tt[nsrcs];
267 int32_t sample = 0;
268 int srcidx, srcidx1 = 0;
269 const char *src = 0;
270 for (srcidx = 0; srcidx < nsrcs && (unsigned)srcidx < src_channels; ++srcidx) {
271 const snd_pcm_channel_area_t *src_area;
272 unsigned int channel = ttable->srcs[srcidx].channel;
273 if (channel >= src_channels)
274 continue;
275 src_area = &src_areas[channel];
276 srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
277 src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
278 src_tt[srcidx1] = ttable->srcs[srcidx];
279 srcidx1++;
280 }
281 nsrcs = srcidx1;
282 if (nsrcs == 0) {
283 snd_pcm_route_convert1_zero(dst_area, dst_offset,
284 src_areas, src_offset,
285 src_channels,
286 frames, ttable, params);
287 return;
288 } else if (nsrcs == 1 && src_tt[0].as_int == SND_PCM_PLUGIN_ROUTE_RESOLUTION) {
289 if (params->use_getput)
290 snd_pcm_route_convert1_one_getput(dst_area, dst_offset,
291 src_areas, src_offset,
292 src_channels,
293 frames, ttable, params);
294 else
295 snd_pcm_route_convert1_one(dst_area, dst_offset,
296 src_areas, src_offset,
297 src_channels,
298 frames, ttable, params);
299 return;
300 }
301
302 zero = zero_labels[params->sum_idx];
303 get32 = get32_labels[params->get_idx];
304 add = add_labels[params->sum_idx * 2 + ttable->att];
305 norm = norm_labels[params->sum_idx * 2 + ttable->att];
306 put32 = put32_labels[params->put_idx];
307 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
308 dst_step = snd_pcm_channel_area_step(dst_area);
309
310 while (frames-- > 0) {
311 snd_pcm_route_ttable_src_t *ttp = src_tt;
312 sum_t sum;
313
314 /* Zero sum */
315 goto *zero;
316 zero_int64:
317 sum.as_sint64 = 0;
318 goto zero_end;
319 #if SND_PCM_PLUGIN_ROUTE_FLOAT
320 zero_float:
321 sum.as_float = 0.0;
322 goto zero_end;
323 #endif
324 zero_end:
325 for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
326 src = srcs[srcidx];
327
328 /* Get sample */
329 goto *get32;
330 #define GET32_END after_get
331 #include "plugin_ops.h"
332 #undef GET32_END
333 after_get:
334
335 /* Sum */
336 goto *add;
337 add_int64_att:
338 sum.as_sint64 += (int64_t) sample * ttp->as_int;
339 goto after_sum;
340 add_int64_noatt:
341 if (ttp->as_int)
342 sum.as_sint64 += sample;
343 goto after_sum;
344 #if SND_PCM_PLUGIN_ROUTE_FLOAT
345 add_float_att:
346 sum.as_float += sample * ttp->as_float;
347 goto after_sum;
348 add_float_noatt:
349 if (ttp->as_int)
350 sum.as_float += sample;
351 goto after_sum;
352 #endif
353 after_sum:
354 srcs[srcidx] += src_steps[srcidx];
355 ttp++;
356 }
357
358 /* Normalization */
359 goto *norm;
360 norm_int64_att:
361 div(sum.as_sint64);
362 /* fallthru */
363 norm_int64_noatt:
364 if (sum.as_sint64 > (int64_t)0x7fffffff)
365 sample = 0x7fffffff; /* maximum positive value */
366 else if (sum.as_sint64 < -(int64_t)0x80000000)
367 sample = 0x80000000; /* maximum negative value */
368 else
369 sample = sum.as_sint64;
370 goto after_norm;
371
372 #if SND_PCM_PLUGIN_ROUTE_FLOAT
373 norm_float:
374 sum.as_float = rint(sum.as_float);
375 if (sum.as_float > (int64_t)0x7fffffff)
376 sample = 0x7fffffff; /* maximum positive value */
377 else if (sum.as_float < -(int64_t)0x80000000)
378 sample = 0x80000000; /* maximum negative value */
379 else
380 sample = sum.as_float;
381 goto after_norm;
382 #endif
383 after_norm:
384
385 /* Put sample */
386 goto *put32;
387 #define PUT32_END after_put32
388 #include "plugin_ops.h"
389 #undef PUT32_END
390 after_put32:
391
392 dst += dst_step;
393 }
394 }
395
snd_pcm_route_convert(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_channels,unsigned int dst_channels,snd_pcm_uframes_t frames,snd_pcm_route_params_t * params)396 static void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
397 snd_pcm_uframes_t dst_offset,
398 const snd_pcm_channel_area_t *src_areas,
399 snd_pcm_uframes_t src_offset,
400 unsigned int src_channels,
401 unsigned int dst_channels,
402 snd_pcm_uframes_t frames,
403 snd_pcm_route_params_t *params)
404 {
405 unsigned int dst_channel;
406 snd_pcm_route_ttable_dst_t *dstp;
407 const snd_pcm_channel_area_t *dst_area;
408
409 dstp = params->dsts;
410 dst_area = dst_areas;
411 for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
412 if (dst_channel >= params->ndsts)
413 snd_pcm_route_convert1_zero(dst_area, dst_offset,
414 src_areas, src_offset,
415 src_channels,
416 frames, dstp, params);
417 else
418 dstp->func(dst_area, dst_offset,
419 src_areas, src_offset,
420 src_channels,
421 frames, dstp, params);
422 dstp++;
423 dst_area++;
424 }
425 }
426
427 static snd_pcm_uframes_t
snd_pcm_route_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)428 snd_pcm_route_write_areas(snd_pcm_t *pcm,
429 const snd_pcm_channel_area_t *areas,
430 snd_pcm_uframes_t offset,
431 snd_pcm_uframes_t size,
432 const snd_pcm_channel_area_t *slave_areas,
433 snd_pcm_uframes_t slave_offset,
434 snd_pcm_uframes_t *slave_sizep)
435 {
436 snd_pcm_route_t *route = pcm->private_data;
437 snd_pcm_t *slave = route->plug.gen.slave;
438 if (size > *slave_sizep)
439 size = *slave_sizep;
440 snd_pcm_route_convert(slave_areas, slave_offset,
441 areas, offset,
442 pcm->channels,
443 slave->channels,
444 size, &route->params);
445 *slave_sizep = size;
446 return size;
447 }
448
449 static snd_pcm_uframes_t
snd_pcm_route_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)450 snd_pcm_route_read_areas(snd_pcm_t *pcm,
451 const snd_pcm_channel_area_t *areas,
452 snd_pcm_uframes_t offset,
453 snd_pcm_uframes_t size,
454 const snd_pcm_channel_area_t *slave_areas,
455 snd_pcm_uframes_t slave_offset,
456 snd_pcm_uframes_t *slave_sizep)
457 {
458 snd_pcm_route_t *route = pcm->private_data;
459 snd_pcm_t *slave = route->plug.gen.slave;
460 if (size > *slave_sizep)
461 size = *slave_sizep;
462 snd_pcm_route_convert(areas, offset,
463 slave_areas, slave_offset,
464 slave->channels,
465 pcm->channels,
466 size, &route->params);
467 *slave_sizep = size;
468 return size;
469 }
470
snd_pcm_route_determine_ttable(const snd_pcm_route_ttable_config_t * tt_config,unsigned int * tt_csize,unsigned int * tt_ssize)471 int snd_pcm_route_determine_ttable(
472 const snd_pcm_route_ttable_config_t *tt_config,
473 unsigned int *tt_csize, unsigned int *tt_ssize)
474 {
475 int csize = 0, ssize = 0;
476 int i;
477
478 assert(tt_config && tt_csize && tt_ssize);
479 i = 0;
480 while (1) {
481 if (snd_pcm_route_is_ttable_config_end(&tt_config[i]))
482 break;
483 int cchannel = tt_config[i].cchannel;
484 int schannel = tt_config[i].schannel;
485 if (cchannel + 1 > csize)
486 csize = cchannel + 1;
487 if (schannel + 1 > ssize)
488 ssize = schannel + 1;
489 i++;
490 }
491
492 if (csize == 0 || ssize == 0) {
493 awalsa_debug("Invalid null ttable configuration\n");
494 return -EINVAL;
495 }
496 *tt_csize = csize;
497 *tt_ssize = ssize;
498 return 0;
499 }
500
snd_pcm_route_load_ttable(const snd_pcm_route_ttable_config_t * tt_config,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_csize,unsigned int tt_ssize,unsigned int * tt_cused,unsigned int * tt_sused,int schannels)501 int snd_pcm_route_load_ttable(
502 const snd_pcm_route_ttable_config_t *tt_config,
503 snd_pcm_route_ttable_entry_t *ttable,
504 unsigned int tt_csize, unsigned int tt_ssize,
505 unsigned int *tt_cused, unsigned int *tt_sused, int schannels)
506 {
507 int cused = -1;
508 int sused = -1;
509 int i;
510 unsigned int k;
511
512 for (k = 0; k < tt_csize * tt_ssize; ++k)
513 ttable[k] = 0.0;
514
515 i = 0;
516 while (1) {
517 if (snd_pcm_route_is_ttable_config_end(&tt_config[i]))
518 break;
519
520 int cchannel = tt_config[i].cchannel;
521 int schannel = tt_config[i].schannel;
522 snd_pcm_route_ttable_entry_t value = tt_config[i].route_value;
523
524 if (cchannel < 0 || (unsigned int) cchannel > tt_csize) {
525 awalsa_err("invalid client channel: %d\n", cchannel);
526 return -EINVAL;
527 }
528 if (schannel < 0 || (unsigned int) schannel > tt_ssize ||
529 (schannels > 0 && schannel >= schannels)) {
530 awalsa_err("invalid slave channel: %d\n", schannel);
531 return -EINVAL;
532 }
533 ttable[cchannel * tt_ssize + schannel] = value;
534 if (schannel > sused)
535 sused = schannel;
536 if (cchannel > cused)
537 cused = cchannel;
538 i++;
539 }
540
541 *tt_sused = sused + 1;
542 *tt_cused = cused + 1;
543 return 0;
544 }
545
route_chmap_init(snd_pcm_t * pcm)546 static int route_chmap_init(snd_pcm_t *pcm)
547 {
548 int set_map = 0;
549 snd_pcm_chmap_t *current;
550 snd_pcm_route_t *route = pcm->private_data;
551 if (!route->chmap)
552 return 0;
553 if (__snd_pcm_state(pcm) != SND_PCM_STATE_PREPARED)
554 return 0;
555
556 /* Check if we really need to set the chmap or not.
557 This is important in case set_chmap is not implemented. */
558 current = snd_pcm_get_chmap(route->plug.gen.slave);
559 if (!current)
560 return -ENOSYS;
561 if (current->channels != route->chmap->channels)
562 set_map = 1;
563 else
564 set_map = memcmp(current->pos, route->chmap->pos,
565 current->channels);
566 free(current);
567 if (!set_map)
568 return 0;
569
570 return snd_pcm_set_chmap(route->plug.gen.slave, route->chmap);
571 }
572
snd_pcm_route_close(snd_pcm_t * pcm)573 static int snd_pcm_route_close(snd_pcm_t *pcm)
574 {
575 snd_pcm_route_t *route = pcm->private_data;
576 snd_pcm_route_params_t *params = &route->params;
577 unsigned int dst_channel;
578
579 awalsa_debug("\n");
580 if (params->dsts) {
581 for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
582 free(params->dsts[dst_channel].srcs);
583 }
584 free(params->dsts);
585 }
586 free(route->chmap);
587 return snd_pcm_generic_close(pcm);
588 }
589
snd_pcm_route_hw_refine_cprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)590 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
591 {
592 int err;
593 snd_interval_t access_mask = { .mask = SND_PCM_ACCBIT_SHM };
594 snd_interval_t format_mask = { .mask = SND_PCM_FMTBIT_LINEAR };
595
596 awalsa_debug("\n");
597 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
598 &access_mask);
599 if (err < 0)
600 return err;
601 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
602 &format_mask);
603 if (err < 0)
604 return err;
605 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
606 if (err < 0)
607 return err;
608 #if 0
609 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
610 #endif
611 return 0;
612 }
613
snd_pcm_route_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)614 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
615 {
616 snd_pcm_route_t *route = pcm->private_data;
617 snd_interval_t saccess_mask = { .mask = SND_PCM_ACCBIT_MMAP };
618
619 awalsa_debug("\n");
620 _snd_pcm_hw_params_any(sparams);
621 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
622 &saccess_mask);
623 if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
624 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_FORMAT,
625 (unsigned long) route->sformat, 0);
626 }
627 if (route->schannels >= 0) {
628 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
629 (unsigned int) route->schannels, 0);
630 }
631 return 0;
632 }
633
snd_pcm_route_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)634 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
635 snd_pcm_hw_params_t *sparams)
636 {
637 snd_pcm_route_t *route = pcm->private_data;
638 int err;
639 unsigned int links = (SND_PCM_HW_PARBIT_RATE |
640 SND_PCM_HW_PARBIT_PERIODS |
641 SND_PCM_HW_PARBIT_PERIOD_SIZE |
642 SND_PCM_HW_PARBIT_PERIOD_TIME |
643 SND_PCM_HW_PARBIT_BUFFER_SIZE |
644 SND_PCM_HW_PARBIT_BUFFER_TIME);
645
646 awalsa_debug("\n");
647 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
648 links |= (SND_PCM_HW_PARBIT_FORMAT |
649 SND_PCM_HW_PARBIT_SAMPLE_BITS);
650 if (route->schannels < 0)
651 links |= SND_PCM_HW_PARBIT_CHANNELS;
652 err = _snd_pcm_hw_params_refine(sparams, links, params);
653 if (err < 0)
654 return err;
655 return 0;
656 }
657
snd_pcm_route_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)658 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
659 snd_pcm_hw_params_t *sparams)
660 {
661 snd_pcm_route_t *route = pcm->private_data;
662 int err;
663 unsigned int links = (SND_PCM_HW_PARBIT_RATE |
664 SND_PCM_HW_PARBIT_PERIODS |
665 SND_PCM_HW_PARBIT_PERIOD_SIZE |
666 SND_PCM_HW_PARBIT_PERIOD_TIME |
667 SND_PCM_HW_PARBIT_BUFFER_SIZE |
668 SND_PCM_HW_PARBIT_BUFFER_TIME);
669
670 awalsa_debug("\n");
671 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
672 links |= (SND_PCM_HW_PARBIT_FORMAT |
673 SND_PCM_HW_PARBIT_SAMPLE_BITS);
674 if (route->schannels < 0)
675 links |= SND_PCM_HW_PARBIT_CHANNELS;
676 err = _snd_pcm_hw_params_refine(params, links, sparams);
677 if (err < 0)
678 return err;
679 return 0;
680 }
681
snd_pcm_route_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)682 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
683 {
684 #if 0
685 snd_pcm_route_t *route = pcm->private_data;
686 snd_pcm_t *slave = route->plug.gen.slave;
687 int ret;
688
689 awalsa_debug("\n");
690 ret = slave->ops->hw_refine(slave->op_arg, params);
691 if (ret < 0)
692 return ret;
693
694 return 0;
695 #else
696 awalsa_debug("\n");
697 return snd_pcm_hw_refine_slave(pcm, params,
698 snd_pcm_route_hw_refine_cprepare,
699 snd_pcm_route_hw_refine_cchange,
700 snd_pcm_route_hw_refine_sprepare,
701 snd_pcm_route_hw_refine_schange,
702 snd_pcm_generic_hw_refine);
703 #endif
704 }
705
snd_pcm_route_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)706 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
707 {
708 snd_pcm_route_t *route = pcm->private_data;
709 snd_pcm_t *slave = route->plug.gen.slave;
710 snd_pcm_format_t src_format, dst_format;
711 int err;
712 #if 0
713 snd_pcm_hw_params_t *slave_params;
714 unsigned int cchannels;
715
716 awalsa_debug("\n");
717
718 snd_pcm_hw_params_get_channels(params, &cchannels);
719 snd_pcm_hw_params_alloca(&slave_params);
720 memcpy(slave_params, params, sizeof(snd_pcm_hw_params_t));
721
722 snd_pcm_hw_param_change(slave_params, SND_PCM_HW_PARAM_ACCESS);
723 err = snd_pcm_hw_params_set_access(slave, slave_params, SND_PCM_ACCESS_MMAP_INTERLEAVED);
724 if (err < 0) {
725 awalsa_err("snd_pcm_hw_params_set_access failed\n");
726 return err;
727 }
728 snd_pcm_hw_param_change(slave_params, SND_PCM_HW_PARAM_CHANNELS);
729 err = snd_pcm_hw_params_set_channels(slave, slave_params, route->schannels);
730 if (err < 0) {
731 awalsa_err("snd_pcm_hw_params_set_channels failed\n");
732 return err;
733 }
734
735 /* TODO: whether pcm route can change format? */
736 #if 0
737 snd_pcm_hw_param_change(slave_params, SND_PCM_HW_PARAM_FORMAT);
738 err = snd_pcm_hw_params_set_format(slave, slave_params, route->sformat);
739 if (err < 0) {
740 awalsa_err("snd_pcm_hw_params_set_format failed\n");
741 return err;
742 }
743 #endif
744
745 err = _snd_pcm_hw_params_internal(slave, slave_params);
746 if (err < 0) {
747 awalsa_err("failed to set slave hw params\n");
748 return err;
749 }
750
751 memcpy(params, slave_params, sizeof(snd_pcm_hw_params_t));
752 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_CHANNELS);
753 err = snd_pcm_hw_params_set_channels(pcm, params, cchannels);
754 if (err < 0) {
755 awalsa_err("snd_pcm_hw_params_set_channels failed\n");
756 return err;
757 }
758 #else
759
760 awalsa_debug("\n");
761 err = snd_pcm_hw_params_slave(pcm, params,
762 snd_pcm_route_hw_refine_cchange,
763 snd_pcm_route_hw_refine_sprepare,
764 snd_pcm_route_hw_refine_schange,
765 snd_pcm_generic_hw_params);
766 if (err < 0)
767 return err;
768 #endif
769
770 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
771 err = snd_pcm_hw_params_get_format(params, &src_format);
772 dst_format = slave->format;
773 } else {
774 src_format = slave->format;
775 err = snd_pcm_hw_params_get_format(params, &dst_format);
776 }
777 if (err < 0)
778 return err;
779 /* 3 bytes formats? */
780 route->params.use_getput =
781 (snd_pcm_format_physical_width(src_format) + 7) / 3 == 3 ||
782 (snd_pcm_format_physical_width(dst_format) + 7) / 3 == 3;
783 route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S32_LE);
784 route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32_LE, dst_format);
785 route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
786 route->params.src_size = snd_pcm_format_width(src_format) / 8;
787 route->params.dst_sfmt = dst_format;
788 #if SND_PCM_PLUGIN_ROUTE_FLOAT
789 route->params.sum_idx = FLOAT;
790 #else
791 route->params.sum_idx = UINT64;
792 #endif
793 return 0;
794 }
795
snd_pcm_route_get_chmap(snd_pcm_t * pcm)796 static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm)
797 {
798 snd_pcm_route_t *route = pcm->private_data;
799 snd_pcm_chmap_t *map, *slave_map;
800 unsigned int src, dst, nsrcs;
801
802 awalsa_debug("\n");
803 slave_map = snd_pcm_generic_get_chmap(pcm);
804 if (!slave_map)
805 return NULL;
806 nsrcs = route->params.nsrcs;
807 map = calloc(4, nsrcs + 1);
808 if (!map) {
809 free(slave_map);
810 return NULL;
811 }
812 map->channels = nsrcs;
813 for (src = 0; src < nsrcs; src++)
814 map->pos[src] = SND_CHMAP_NA;
815 for (dst = 0; dst < route->params.ndsts; dst++) {
816 snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
817 for (src = 0; src < d->nsrcs; src++) {
818 unsigned int c = d->srcs[src].channel;
819 if (c < nsrcs && map->pos[c] == SND_CHMAP_NA)
820 map->pos[c] = slave_map->pos[dst];
821 }
822 }
823 free(slave_map);
824 return map;
825 }
826
snd_pcm_route_query_chmaps(snd_pcm_t * pcm)827 static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm)
828 {
829 snd_pcm_chmap_query_t **maps;
830 awalsa_debug("\n");
831 snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm);
832 if (!map)
833 return NULL;
834 maps = _snd_pcm_make_single_query_chmaps(map);
835 free(map);
836 return maps;
837 }
838
snd_pcm_route_dump(snd_pcm_t * pcm)839 static void snd_pcm_route_dump(snd_pcm_t *pcm)
840 {
841 snd_pcm_route_t *route = pcm->private_data;
842 unsigned int dst;
843
844 if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
845 printf("Route conversion PCM\n");
846 else
847 printf("Route conversion PCM (sformat=%s)\n",
848 snd_pcm_format_name(route->sformat));
849 printf(" Transformation table:\n");
850 for (dst = 0; dst < route->params.ndsts; dst++) {
851 snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
852 unsigned int src;
853 printf(" %d <- ", dst);
854 if (d->nsrcs == 0) {
855 printf("none\n");
856 continue;
857 }
858 src = 0;
859 while (1) {
860 snd_pcm_route_ttable_src_t *s = &d->srcs[src];
861 if (d->att)
862 #if SND_PCM_PLUGIN_ROUTE_FLOAT
863 printf("%d*%g", s->channel, s->as_float);
864 #else
865 printf("%d*%g", s->channel, (double)s->as_int / (double)SND_PCM_PLUGIN_ROUTE_RESOLUTION);
866 #endif
867 else
868 printf("%d", s->channel);
869 src++;
870 if (src == d->nsrcs)
871 break;
872 printf(" + ");
873 }
874 printf("\n");
875 }
876 if (pcm->setup) {
877 printf("Its setup is:\n");
878 snd_pcm_dump_setup(pcm);
879 }
880 printf("Slave: ");
881 snd_pcm_dump(route->plug.gen.slave);
882 }
883
884 static const snd_pcm_ops_t snd_pcm_route_ops = {
885 .close = snd_pcm_route_close,
886 .hw_refine = snd_pcm_route_hw_refine,
887 .hw_params = snd_pcm_route_hw_params,
888 .hw_free = snd_pcm_generic_hw_free,
889 .sw_params = snd_pcm_generic_sw_params,
890 .channel_info = snd_pcm_generic_channel_info,
891 .dump = snd_pcm_route_dump,
892 .mmap = snd_pcm_generic_mmap,
893 .munmap = snd_pcm_generic_munmap,
894 .query_chmaps = snd_pcm_route_query_chmaps,
895 .get_chmap = snd_pcm_route_get_chmap,
896 .set_chmap = NULL, /* NYI */
897 };
898
route_load_ttable(snd_pcm_route_params_t * params,snd_pcm_stream_t stream,unsigned int tt_ssize,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_cused,unsigned int tt_sused)899 static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
900 unsigned int tt_ssize,
901 snd_pcm_route_ttable_entry_t *ttable,
902 unsigned int tt_cused, unsigned int tt_sused)
903 {
904 unsigned int src_channel, dst_channel;
905 snd_pcm_route_ttable_dst_t *dptr;
906 unsigned int sused, dused, smul, dmul;
907 if (stream == SND_PCM_STREAM_PLAYBACK) {
908 sused = tt_cused;
909 dused = tt_sused;
910 smul = tt_ssize;
911 dmul = 1;
912 } else {
913 sused = tt_sused;
914 dused = tt_cused;
915 smul = 1;
916 dmul = tt_ssize;
917 }
918 params->ndsts = dused;
919 params->nsrcs = sused;
920 dptr = calloc(dused, sizeof(*params->dsts));
921 if (!dptr)
922 return -ENOMEM;
923 params->dsts = dptr;
924 for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
925 snd_pcm_route_ttable_entry_t t = 0;
926 int att = 0;
927 int nsrcs = 0;
928 snd_pcm_route_ttable_src_t srcs[sused];
929 for (src_channel = 0; src_channel < sused; ++src_channel) {
930 snd_pcm_route_ttable_entry_t v;
931 v = ttable[src_channel * smul + dst_channel * dmul];
932 if (v != 0) {
933 srcs[nsrcs].channel = src_channel;
934 #if SND_PCM_PLUGIN_ROUTE_FLOAT
935 /* Also in user space for non attenuated */
936 srcs[nsrcs].as_int = (v == SND_PCM_PLUGIN_ROUTE_FULL ? SND_PCM_PLUGIN_ROUTE_RESOLUTION : 0);
937 srcs[nsrcs].as_float = v;
938 #else
939 assert(v >= 0 && v <= SND_PCM_PLUGIN_ROUTE_FULL);
940 srcs[nsrcs].as_int = v;
941 #endif
942 if (v != SND_PCM_PLUGIN_ROUTE_FULL)
943 att = 1;
944 t += v;
945 nsrcs++;
946 }
947 }
948 #if 0
949 assert(t <= SND_PCM_PLUGIN_ROUTE_FULL);
950 #endif
951 dptr->att = att;
952 dptr->nsrcs = nsrcs;
953 if (nsrcs == 0)
954 dptr->func = snd_pcm_route_convert1_zero;
955 else
956 dptr->func = snd_pcm_route_convert1_many;
957 if (nsrcs > 0) {
958 dptr->srcs = calloc((unsigned int) nsrcs, sizeof(*srcs));
959 if (!dptr->srcs)
960 return -ENOMEM;
961 memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
962 } else
963 dptr->srcs = 0;
964 dptr++;
965 }
966 return 0;
967 }
968
969 /**
970 * \brief Creates a new Route & Volume PCM
971 * \param pcmp Returns created PCM handle
972 * \param name Name of PCM
973 * \param sformat Slave format
974 * \param schannels Slave channels
975 * \param ttable Attenuation table
976 * \param tt_ssize Attenuation table - slave size
977 * \param tt_cused Attenuation table - client used count
978 * \param tt_sused Attenuation table - slave used count
979 * \param slave Slave PCM handle
980 * \param close_slave When set, the slave PCM handle is closed with copy PCM
981 * \retval zero on success otherwise a negative error code
982 * \warning Using of this function might be dangerous in the sense
983 * of compatibility reasons. The prototype might be freely
984 * changed in future.
985 */
snd_pcm_route_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,int schannels,snd_pcm_route_ttable_entry_t * ttable,unsigned int tt_ssize,unsigned int tt_cused,unsigned int tt_sused,snd_pcm_t * slave,int close_slave)986 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
987 snd_pcm_format_t sformat, int schannels,
988 snd_pcm_route_ttable_entry_t *ttable,
989 unsigned int tt_ssize,
990 unsigned int tt_cused, unsigned int tt_sused,
991 snd_pcm_t *slave, int close_slave)
992 {
993 snd_pcm_t *pcm;
994 snd_pcm_route_t *route;
995 int err;
996 assert(pcmp && slave && ttable);
997
998 if (sformat != SND_PCM_FORMAT_UNKNOWN && snd_pcm_format_linear(sformat) != 1)
999 return -EINVAL;
1000
1001 route = calloc(1, sizeof(snd_pcm_route_t));
1002 if (!route) {
1003 return -ENOMEM;
1004 }
1005 snd_pcm_plugin_init(&route->plug);
1006 route->sformat = sformat;
1007 route->schannels = schannels;
1008 route->plug.read = snd_pcm_route_read_areas;
1009 route->plug.write = snd_pcm_route_write_areas;
1010 route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
1011 route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
1012 route->plug.gen.slave = slave;
1013 route->plug.gen.close_slave = close_slave;
1014 route->plug.init = route_chmap_init;
1015
1016 err = snd_pcm_new(&pcm, SND_PCM_TYPE_ROUTE, name, slave->stream, slave->mode);
1017 if (err < 0) {
1018 free(route);
1019 return err;
1020 }
1021 pcm->ops = &snd_pcm_route_ops;
1022 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
1023 pcm->private_data = route;
1024 snd_pcm_set_hw_ptr(pcm, &route->plug.hw_ptr, -1, 0);
1025 snd_pcm_set_appl_ptr(pcm, &route->plug.appl_ptr, -1, 0);
1026 err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
1027 if (err < 0) {
1028 snd_pcm_close(pcm);
1029 return err;
1030 }
1031 *pcmp = pcm;
1032
1033 return 0;
1034 }
1035
_snd_pcm_route_open(snd_pcm_t ** pcmp,const snd_pcm_config_t * pcm_config,snd_pcm_stream_t stream,int mode)1036 int _snd_pcm_route_open(snd_pcm_t **pcmp, const snd_pcm_config_t *pcm_config,
1037 snd_pcm_stream_t stream, int mode)
1038 {
1039 const snd_pcm_route_config_t *route_config =
1040 (const snd_pcm_route_config_t *)(pcm_config->config);
1041 const snd_pcm_route_ttable_config_t *tt_config = route_config->ttable;
1042 const char *name = pcm_config->name;
1043
1044 int ret = 0;
1045 snd_pcm_t *spcm = NULL;
1046 const snd_pcm_config_t *sconf = NULL;
1047 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1048 int schannels = -1;
1049 snd_pcm_route_ttable_entry_t *ttable = NULL;
1050 unsigned int csize, ssize;
1051 unsigned int cused, sused;
1052
1053 /* TODO: whether it is the correct way to get slave channels? */
1054 schannels = route_config->slave.channels;
1055
1056 sconf = snd_pcm_config_get_config(route_config->slave.pcm);
1057 if (!sconf) {
1058 awalsa_err("can't find pcm slave:%s\n", route_config->slave.pcm);
1059 ret = -EINVAL;
1060 goto err_out;
1061 }
1062 ret = snd_pcm_open_config(&spcm, sconf, stream, mode);
1063 if (ret < 0) {
1064 awalsa_err("unable to open slave\n");
1065 goto err_out;
1066 }
1067
1068 ret = snd_pcm_route_determine_ttable(tt_config, &csize, &ssize);
1069 if (ret < 0) {
1070 awalsa_err("failed to determine ttable\n");
1071 goto err_close_spcm;
1072 }
1073 ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t));
1074 if (!ttable) {
1075 awalsa_err("no memory\n");
1076 ret = -ENOMEM;
1077 goto err_close_spcm;
1078 }
1079 ret = snd_pcm_route_load_ttable(tt_config, ttable, csize, ssize,
1080 &cused, &sused, schannels);
1081 if (ret < 0) {
1082 awalsa_err("failed to load ttable\n");
1083 goto err_free_ttable;
1084 }
1085
1086 ret = snd_pcm_route_open(pcmp, name, sformat, schannels,
1087 ttable, ssize, cused, sused, spcm, 1);
1088 free(ttable);
1089 if (ret < 0) {
1090 awalsa_err("snd_pcm_route_open failed\n");
1091 goto err_close_spcm;
1092 }
1093
1094 return 0;
1095
1096 err_free_ttable:
1097 free(ttable);
1098 err_close_spcm:
1099 snd_pcm_close(spcm);
1100 err_out:
1101 return ret;
1102 }
1103