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