OSDN Git Service

Improved .asoundrc changes
[android-x86/external-alsa-lib.git] / src / pcm / pcm_route.c
1 /*
2  *  PCM - Linear conversion
3  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4  *
5  *
6  *   This library is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU Library General Public License as
8  *   published by the Free Software Foundation; either version 2 of
9  *   the License, or (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU Library General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Library General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21   
22 #include <byteswap.h>
23 #include <math.h>
24 #include "pcm_local.h"
25 #include "pcm_plugin.h"
26
27 /* The best possible hack to support missing optimization in gcc 2.7.2.3 */
28 #if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
29 #define div(a) a /= ROUTE_PLUGIN_RESOLUTION
30 #elif ROUTE_PLUGIN_RESOLUTION == 16
31 #define div(a) a >>= 4
32 #else
33 #error "Add some code here"
34 #endif
35
36 typedef struct {
37         int channel;
38         int as_int;
39 #if ROUTE_PLUGIN_FLOAT
40         float as_float;
41 #endif
42 } snd_pcm_route_ttable_src_t;
43
44 typedef struct snd_pcm_route_ttable_dst snd_pcm_route_ttable_dst_t;
45
46 typedef struct {
47         enum {UINT32=0, UINT64=1, FLOAT=2} sum_idx;
48         int get_idx;
49         int put_idx;
50         int conv_idx;
51         int src_size;
52         snd_pcm_format_t dst_sfmt;
53         unsigned int ndsts;
54         snd_pcm_route_ttable_dst_t *dsts;
55 } snd_pcm_route_params_t;
56
57
58 typedef void (*route_f)(const snd_pcm_channel_area_t *dst_area,
59                         snd_pcm_uframes_t dst_offset,
60                         const snd_pcm_channel_area_t *src_areas,
61                         snd_pcm_uframes_t src_offset,
62                         snd_pcm_uframes_t frames,
63                         const snd_pcm_route_ttable_dst_t *ttable,
64                         const snd_pcm_route_params_t *params);
65
66 struct snd_pcm_route_ttable_dst {
67         int att;        /* Attenuated */
68         unsigned int nsrcs;
69         snd_pcm_route_ttable_src_t* srcs;
70         route_f func;
71 };
72
73 typedef union {
74         u_int32_t as_uint32;
75         u_int64_t as_uint64;
76 #if ROUTE_PLUGIN_FLOAT
77         float as_float;
78 #endif
79 } sum_t;
80
81 typedef struct {
82         /* This field need to be the first */
83         snd_pcm_plugin_t plug;
84         snd_pcm_format_t sformat;
85         int schannels;
86         snd_pcm_route_params_t params;
87 } snd_pcm_route_t;
88
89
90 void snd_pcm_route_convert1_zero(const snd_pcm_channel_area_t *dst_area,
91                                  snd_pcm_uframes_t dst_offset,
92                                  const snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
93                                  snd_pcm_uframes_t src_offset ATTRIBUTE_UNUSED,
94                                  snd_pcm_uframes_t frames,
95                                  const snd_pcm_route_ttable_dst_t* ttable ATTRIBUTE_UNUSED,
96                                  const snd_pcm_route_params_t *params)
97 {
98         snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
99 }
100
101 void snd_pcm_route_convert1_one(const snd_pcm_channel_area_t *dst_area,
102                                 snd_pcm_uframes_t dst_offset,
103                                 const snd_pcm_channel_area_t *src_areas,
104                                 snd_pcm_uframes_t src_offset,
105                                 snd_pcm_uframes_t frames,
106                                 const snd_pcm_route_ttable_dst_t* ttable,
107                                 const snd_pcm_route_params_t *params)
108 {
109 #define CONV_LABELS
110 #include "plugin_ops.h"
111 #undef CONV_LABELS
112         void *conv;
113         const snd_pcm_channel_area_t *src_area = 0;
114         unsigned int srcidx;
115         const char *src;
116         char *dst;
117         int src_step, dst_step;
118         for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
119                 src_area = &src_areas[ttable->srcs[srcidx].channel];
120                 if (src_area->addr != NULL)
121                         break;
122         }
123         if (srcidx == ttable->nsrcs) {
124                 snd_pcm_route_convert1_zero(dst_area, dst_offset,
125                                             src_areas, src_offset,
126                                             frames, ttable, params);
127                 return;
128         }
129         
130         conv = conv_labels[params->conv_idx];
131         src = snd_pcm_channel_area_addr(src_area, src_offset);
132         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
133         src_step = snd_pcm_channel_area_step(src_area);
134         dst_step = snd_pcm_channel_area_step(dst_area);
135         while (frames-- > 0) {
136                 goto *conv;
137 #define CONV_END after
138 #include "plugin_ops.h"
139 #undef CONV_END
140         after:
141                 src += src_step;
142                 dst += dst_step;
143         }
144 }
145
146 void snd_pcm_route_convert1_many(const snd_pcm_channel_area_t *dst_area,
147                                  snd_pcm_uframes_t dst_offset,
148                                  const snd_pcm_channel_area_t *src_areas,
149                                  snd_pcm_uframes_t src_offset,
150                                  snd_pcm_uframes_t frames,
151                                  const snd_pcm_route_ttable_dst_t* ttable,
152                                  const snd_pcm_route_params_t *params)
153 {
154 #define GETU_LABELS
155 #define PUT32_LABELS
156 #include "plugin_ops.h"
157 #undef GETU_LABELS
158 #undef PUT32_LABELS
159         static void *zero_labels[3] = {
160                 &&zero_int32, &&zero_int64,
161 #if ROUTE_PLUGIN_FLOAT
162                 &&zero_float
163 #endif
164         };
165         /* sum_type att */
166         static void *add_labels[3 * 2] = {
167                 &&add_int32_noatt, &&add_int32_att,
168                 &&add_int64_noatt, &&add_int64_att,
169 #if ROUTE_PLUGIN_FLOAT
170                 &&add_float_noatt, &&add_float_att
171 #endif
172         };
173         /* sum_type att shift */
174         static void *norm_labels[3 * 2 * 4] = {
175                 0,
176                 &&norm_int32_8_noatt,
177                 &&norm_int32_16_noatt,
178                 &&norm_int32_24_noatt,
179                 0,
180                 &&norm_int32_8_att,
181                 &&norm_int32_16_att,
182                 &&norm_int32_24_att,
183                 &&norm_int64_0_noatt,
184                 &&norm_int64_8_noatt,
185                 &&norm_int64_16_noatt,
186                 &&norm_int64_24_noatt,
187                 &&norm_int64_0_att,
188                 &&norm_int64_8_att,
189                 &&norm_int64_16_att,
190                 &&norm_int64_24_att,
191 #if ROUTE_PLUGIN_FLOAT
192                 &&norm_float_0,
193                 &&norm_float_8,
194                 &&norm_float_16,
195                 &&norm_float_24,
196                 &&norm_float_0,
197                 &&norm_float_8,
198                 &&norm_float_16,
199                 &&norm_float_24,
200 #endif
201         };
202         void *zero, *get, *add, *norm, *put32;
203         int nsrcs = ttable->nsrcs;
204         char *dst;
205         int dst_step;
206         const char *srcs[nsrcs];
207         int src_steps[nsrcs];
208         snd_pcm_route_ttable_src_t src_tt[nsrcs];
209         u_int32_t sample = 0;
210         int srcidx, srcidx1 = 0;
211         for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
212                 const snd_pcm_channel_area_t *src_area = &src_areas[ttable->srcs[srcidx].channel];
213                 srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
214                 src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
215                 src_tt[srcidx1] = ttable->srcs[srcidx];
216                 srcidx1++;
217         }
218         nsrcs = srcidx1;
219         if (nsrcs == 0) {
220                 snd_pcm_route_convert1_zero(dst_area, dst_offset,
221                                             src_areas, src_offset,
222                                             frames, ttable, params);
223                 return;
224         } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
225                 snd_pcm_route_convert1_one(dst_area, dst_offset,
226                                            src_areas, src_offset,
227                                            frames, ttable, params);
228                 return;
229         }
230
231         zero = zero_labels[params->sum_idx];
232         get = getu_labels[params->get_idx];
233         add = add_labels[params->sum_idx * 2 + ttable->att];
234         norm = norm_labels[params->sum_idx * 8 + ttable->att * 4 + 4 - params->src_size];
235         put32 = put32_labels[params->put_idx];
236         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
237         dst_step = snd_pcm_channel_area_step(dst_area);
238
239         while (frames-- > 0) {
240                 snd_pcm_route_ttable_src_t *ttp = src_tt;
241                 sum_t sum;
242
243                 /* Zero sum */
244                 goto *zero;
245         zero_int32:
246                 sum.as_uint32 = 0;
247                 goto zero_end;
248         zero_int64: 
249                 sum.as_uint64 = 0;
250                 goto zero_end;
251 #if ROUTE_PLUGIN_FLOAT
252         zero_float:
253                 sum.as_float = 0.0;
254                 goto zero_end;
255 #endif
256         zero_end:
257                 for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
258                         const char *src = srcs[srcidx];
259                         
260                         /* Get sample */
261                         goto *get;
262 #define GETU_END after_get
263 #include "plugin_ops.h"
264 #undef GETU_END
265                 after_get:
266
267                         /* Sum */
268                         goto *add;
269                 add_int32_att:
270                         sum.as_uint32 += sample * ttp->as_int;
271                         goto after_sum;
272                 add_int32_noatt:
273                         if (ttp->as_int)
274                                 sum.as_uint32 += sample;
275                         goto after_sum;
276                 add_int64_att:
277                         sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
278                         goto after_sum;
279                 add_int64_noatt:
280                         if (ttp->as_int)
281                                 sum.as_uint64 += sample;
282                         goto after_sum;
283 #if ROUTE_PLUGIN_FLOAT
284                 add_float_att:
285                         sum.as_float += sample * ttp->as_float;
286                         goto after_sum;
287                 add_float_noatt:
288                         if (ttp->as_int)
289                                 sum.as_float += sample;
290                         goto after_sum;
291 #endif
292                 after_sum:
293                         srcs[srcidx] += src_steps[srcidx];
294                         ttp++;
295                 }
296                 
297                 /* Normalization */
298                 goto *norm;
299         norm_int32_8_att:
300                 sum.as_uint64 = sum.as_uint32;
301         norm_int64_8_att:
302                 sum.as_uint64 <<= 8;
303         norm_int64_0_att:
304                 div(sum.as_uint64);
305                 goto norm_int;
306
307         norm_int32_16_att:
308                 sum.as_uint64 = sum.as_uint32;
309         norm_int64_16_att:
310                 sum.as_uint64 <<= 16;
311                 div(sum.as_uint64);
312                 goto norm_int;
313
314         norm_int32_24_att:
315                 sum.as_uint64 = sum.as_uint32;
316         norm_int64_24_att:
317                 sum.as_uint64 <<= 24;
318                 div(sum.as_uint64);
319                 goto norm_int;
320
321         norm_int32_8_noatt:
322                 sum.as_uint64 = sum.as_uint32;
323         norm_int64_8_noatt:
324                 sum.as_uint64 <<= 8;
325                 goto norm_int;
326
327         norm_int32_16_noatt:
328                 sum.as_uint64 = sum.as_uint32;
329         norm_int64_16_noatt:
330                 sum.as_uint64 <<= 16;
331                 goto norm_int;
332
333         norm_int32_24_noatt:
334                 sum.as_uint64 = sum.as_uint32;
335         norm_int64_24_noatt:
336                 sum.as_uint64 <<= 24;
337                 goto norm_int;
338
339         norm_int64_0_noatt:
340         norm_int:
341                 if (sum.as_uint64 > (u_int32_t)0xffffffff)
342                         sample = (u_int32_t)0xffffffff;
343                 else
344                         sample = sum.as_uint64;
345                 goto after_norm;
346
347 #if ROUTE_PLUGIN_FLOAT
348         norm_float_8:
349                 sum.as_float *= 1 << 8;
350                 goto norm_float;
351         norm_float_16:
352                 sum.as_float *= 1 << 16;
353                 goto norm_float;
354         norm_float_24:
355                 sum.as_float *= 1 << 24;
356                 goto norm_float;
357         norm_float_0:
358         norm_float:
359                 sum.as_float = floor(sum.as_float + 0.5);
360                 if (sum.as_float > (u_int32_t)0xffffffff)
361                         sample = (u_int32_t)0xffffffff;
362                 else
363                         sample = sum.as_float;
364                 goto after_norm;
365 #endif
366         after_norm:
367                 
368                 /* Put sample */
369                 goto *put32;
370 #define PUT32_END after_put32
371 #include "plugin_ops.h"
372 #undef PUT32_END
373         after_put32:
374                 
375                 dst += dst_step;
376         }
377 }
378
379 void snd_pcm_route_convert(const snd_pcm_channel_area_t *dst_areas,
380                            snd_pcm_uframes_t dst_offset,
381                            const snd_pcm_channel_area_t *src_areas,
382                            snd_pcm_uframes_t src_offset,
383                            snd_pcm_uframes_t dst_channels,
384                            snd_pcm_uframes_t frames,
385                            snd_pcm_route_params_t *params)
386 {
387         unsigned int dst_channel;
388         snd_pcm_route_ttable_dst_t *dstp;
389         const snd_pcm_channel_area_t *dst_area;
390
391         dstp = params->dsts;
392         dst_area = dst_areas;
393         for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
394                 if (dst_channel >= params->ndsts)
395                         snd_pcm_route_convert1_zero(dst_area, dst_offset,
396                                                     src_areas, src_offset,
397                                                     frames, dstp, params);
398                 else
399                         dstp->func(dst_area, dst_offset,
400                                    src_areas, src_offset,
401                                    frames, dstp, params);
402                 dstp++;
403                 dst_area++;
404         }
405 }
406
407 static int snd_pcm_route_close(snd_pcm_t *pcm)
408 {
409         snd_pcm_route_t *route = pcm->private_data;
410         snd_pcm_route_params_t *params = &route->params;
411         int err = 0;
412         unsigned int dst_channel;
413         if (route->plug.close_slave)
414                 err = snd_pcm_close(route->plug.slave);
415         if (params->dsts) {
416                 for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
417                         if (params->dsts[dst_channel].srcs != NULL)
418                                 free(params->dsts[dst_channel].srcs);
419                 }
420                 free(params->dsts);
421         }
422         free(route);
423         return 0;
424 }
425
426 static int snd_pcm_route_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
427 {
428         int err;
429         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_PLUGIN };
430         snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
431         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
432                                          &access_mask);
433         if (err < 0)
434                 return err;
435         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
436                                          &format_mask);
437         if (err < 0)
438                 return err;
439         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
440         if (err < 0)
441                 return err;
442         err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
443         if (err < 0)
444                 return err;
445         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
446         return 0;
447 }
448
449 static int snd_pcm_route_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
450 {
451         snd_pcm_route_t *route = pcm->private_data;
452         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
453         _snd_pcm_hw_params_any(sparams);
454         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
455                                    &saccess_mask);
456         if (route->sformat != SND_PCM_FORMAT_UNKNOWN) {
457                 _snd_pcm_hw_params_set_format(sparams, route->sformat);
458                 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
459         }
460         if (route->schannels >= 0) {
461                 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
462                                       route->schannels, 0);
463         }
464         return 0;
465 }
466
467 static int snd_pcm_route_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
468                                             snd_pcm_hw_params_t *sparams)
469 {
470         snd_pcm_route_t *route = pcm->private_data;
471         int err;
472         unsigned int links = (SND_PCM_HW_PARBIT_RATE |
473                               SND_PCM_HW_PARBIT_PERIODS |
474                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
475                               SND_PCM_HW_PARBIT_PERIOD_TIME |
476                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
477                               SND_PCM_HW_PARBIT_BUFFER_TIME |
478                               SND_PCM_HW_PARBIT_TICK_TIME);
479         if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
480                 links |= (SND_PCM_HW_PARBIT_FORMAT | 
481                           SND_PCM_HW_PARBIT_SUBFORMAT |
482                           SND_PCM_HW_PARBIT_SAMPLE_BITS);
483         if (route->schannels < 0)
484                 links |= SND_PCM_HW_PARBIT_CHANNELS;
485         err = _snd_pcm_hw_params_refine(sparams, links, params);
486         if (err < 0)
487                 return err;
488         return 0;
489 }
490         
491 static int snd_pcm_route_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
492                                             snd_pcm_hw_params_t *sparams)
493 {
494         snd_pcm_route_t *route = pcm->private_data;
495         int err;
496         unsigned int links = (SND_PCM_HW_PARBIT_RATE |
497                               SND_PCM_HW_PARBIT_PERIODS |
498                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
499                               SND_PCM_HW_PARBIT_PERIOD_TIME |
500                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
501                               SND_PCM_HW_PARBIT_BUFFER_TIME |
502                               SND_PCM_HW_PARBIT_TICK_TIME);
503         if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
504                 links |= (SND_PCM_HW_PARBIT_FORMAT | 
505                           SND_PCM_HW_PARBIT_SUBFORMAT |
506                           SND_PCM_HW_PARBIT_SAMPLE_BITS);
507         if (route->schannels < 0)
508                 links |= SND_PCM_HW_PARBIT_CHANNELS;
509         err = _snd_pcm_hw_params_refine(params, links, sparams);
510         if (err < 0)
511                 return err;
512         return 0;
513 }
514
515 static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
516 {
517         return snd_pcm_hw_refine_slave(pcm, params,
518                                        snd_pcm_route_hw_refine_cprepare,
519                                        snd_pcm_route_hw_refine_cchange,
520                                        snd_pcm_route_hw_refine_sprepare,
521                                        snd_pcm_route_hw_refine_schange,
522                                        snd_pcm_plugin_hw_refine_slave);
523 }
524
525 static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
526 {
527         snd_pcm_route_t *route = pcm->private_data;
528         snd_pcm_t *slave = route->plug.slave;
529         snd_pcm_format_t src_format, dst_format;
530         int err = snd_pcm_hw_params_slave(pcm, params,
531                                           snd_pcm_route_hw_refine_cchange,
532                                           snd_pcm_route_hw_refine_sprepare,
533                                           snd_pcm_route_hw_refine_schange,
534                                           snd_pcm_plugin_hw_params_slave);
535         if (err < 0)
536                 return err;
537
538         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
539                 src_format = snd_pcm_hw_params_get_format(params);
540                 dst_format = slave->format;
541         } else {
542                 src_format = slave->format;
543                 dst_format = snd_pcm_hw_params_get_format(params);
544         }
545         route->params.get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_U16);
546         route->params.put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_U32, dst_format);
547         route->params.conv_idx = snd_pcm_linear_convert_index(src_format, dst_format);
548         route->params.src_size = snd_pcm_format_width(src_format) / 8;
549         route->params.dst_sfmt = dst_format;
550 #if ROUTE_PLUGIN_FLOAT
551         route->params.sum_idx = FLOAT;
552 #else
553         if (src_size == 4)
554                 route->params.sum_idx = UINT64;
555         else
556                 route->params.sum_idx = UINT32;
557 #endif
558         return 0;
559 }
560
561 static snd_pcm_uframes_t
562 snd_pcm_route_write_areas(snd_pcm_t *pcm,
563                           const snd_pcm_channel_area_t *areas,
564                           snd_pcm_uframes_t offset,
565                           snd_pcm_uframes_t size,
566                           const snd_pcm_channel_area_t *slave_areas,
567                           snd_pcm_uframes_t slave_offset,
568                           snd_pcm_uframes_t *slave_sizep)
569 {
570         snd_pcm_route_t *route = pcm->private_data;
571         snd_pcm_t *slave = route->plug.slave;
572         if (size > *slave_sizep)
573                 size = *slave_sizep;
574         snd_pcm_route_convert(slave_areas, slave_offset,
575                               areas, offset, 
576                               slave->channels, size, &route->params);
577         *slave_sizep = size;
578         return size;
579 }
580
581 static snd_pcm_uframes_t
582 snd_pcm_route_read_areas(snd_pcm_t *pcm,
583                          const snd_pcm_channel_area_t *areas,
584                          snd_pcm_uframes_t offset,
585                          snd_pcm_uframes_t size,
586                          const snd_pcm_channel_area_t *slave_areas,
587                          snd_pcm_uframes_t slave_offset,
588                          snd_pcm_uframes_t *slave_sizep)
589 {
590         snd_pcm_route_t *route = pcm->private_data;
591         if (size > *slave_sizep)
592                 size = *slave_sizep;
593         snd_pcm_route_convert(areas, offset, 
594                               slave_areas, slave_offset,
595                               pcm->channels, size, &route->params);
596         *slave_sizep = size;
597         return size;
598 }
599
600 static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out)
601 {
602         snd_pcm_route_t *route = pcm->private_data;
603         unsigned int dst;
604         if (route->sformat == SND_PCM_FORMAT_UNKNOWN)
605                 snd_output_printf(out, "Route conversion PCM\n");
606         else
607                 snd_output_printf(out, "Route conversion PCM (sformat=%s)\n", 
608                         snd_pcm_format_name(route->sformat));
609         snd_output_puts(out, "Transformation table:\n");
610         for (dst = 0; dst < route->params.ndsts; dst++) {
611                 snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst];
612                 unsigned int src;
613                 if (d->nsrcs == 0)
614                         continue;
615                 snd_output_printf(out, "%d <- ", dst);
616                 src = 0;
617                 while (1) {
618                         snd_pcm_route_ttable_src_t *s = &d->srcs[src];
619                         if (d->att)
620                                 snd_output_printf(out, "%d*%g", s->channel, s->as_float);
621                         else
622                                 snd_output_printf(out, "%d", s->channel);
623                         src++;
624                         if (src == d->nsrcs)
625                                 break;
626                         snd_output_puts(out, " + ");
627                 }
628                 snd_output_putc(out, '\n');
629         }
630         if (pcm->setup) {
631                 snd_output_printf(out, "Its setup is:\n");
632                 snd_pcm_dump_setup(pcm, out);
633         }
634         snd_output_printf(out, "Slave: ");
635         snd_pcm_dump(route->plug.slave, out);
636 }
637
638 snd_pcm_ops_t snd_pcm_route_ops = {
639         close: snd_pcm_route_close,
640         info: snd_pcm_plugin_info,
641         hw_refine: snd_pcm_route_hw_refine,
642         hw_params: snd_pcm_route_hw_params,
643         hw_free: snd_pcm_plugin_hw_free,
644         sw_params: snd_pcm_plugin_sw_params,
645         channel_info: snd_pcm_plugin_channel_info,
646         dump: snd_pcm_route_dump,
647         nonblock: snd_pcm_plugin_nonblock,
648         async: snd_pcm_plugin_async,
649         mmap: snd_pcm_plugin_mmap,
650         munmap: snd_pcm_plugin_munmap,
651 };
652
653 int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream,
654                       unsigned int tt_ssize,
655                       snd_pcm_route_ttable_entry_t *ttable,
656                       unsigned int tt_cused, unsigned int tt_sused)
657 {
658         unsigned int src_channel, dst_channel;
659         snd_pcm_route_ttable_dst_t *dptr;
660         unsigned int sused, dused, smul, dmul;
661         if (stream == SND_PCM_STREAM_PLAYBACK) {
662                 sused = tt_cused;
663                 dused = tt_sused;
664                 smul = tt_ssize;
665                 dmul = 1;
666         } else {
667                 sused = tt_sused;
668                 dused = tt_cused;
669                 smul = 1;
670                 dmul = tt_ssize;
671         }
672         params->ndsts = dused;
673         dptr = calloc(dused, sizeof(*params->dsts));
674         if (!dptr)
675                 return -ENOMEM;
676         params->dsts = dptr;
677         for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
678                 snd_pcm_route_ttable_entry_t t = 0;
679                 int att = 0;
680                 int nsrcs = 0;
681                 snd_pcm_route_ttable_src_t srcs[sused];
682                 for (src_channel = 0; src_channel < sused; ++src_channel) {
683                         snd_pcm_route_ttable_entry_t v;
684                         v = ttable[src_channel * smul + dst_channel * dmul];
685                         assert(v >= 0 && v <= FULL);
686                         if (v != 0) {
687                                 srcs[nsrcs].channel = src_channel;
688 #if ROUTE_PLUGIN_FLOAT
689                                 /* Also in user space for non attenuated */
690                                 srcs[nsrcs].as_int = (v == FULL ? ROUTE_PLUGIN_RESOLUTION : 0);
691                                 srcs[nsrcs].as_float = v;
692 #else
693                                 srcs[nsrcs].as_int = v;
694 #endif
695                                 if (v != FULL)
696                                         att = 1;
697                                 t += v;
698                                 nsrcs++;
699                         }
700                 }
701 #if 0
702                 assert(t <= FULL);
703 #endif
704                 dptr->att = att;
705                 dptr->nsrcs = nsrcs;
706                 if (nsrcs == 0)
707                         dptr->func = snd_pcm_route_convert1_zero;
708                 else if (nsrcs == 1 && !att)
709                         dptr->func = snd_pcm_route_convert1_one;
710                 else
711                         dptr->func = snd_pcm_route_convert1_many;
712                 if (nsrcs > 0) {
713                         dptr->srcs = calloc(nsrcs, sizeof(*srcs));
714                         if (!dptr->srcs)
715                                 return -ENOMEM;
716                         memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
717                 } else
718                         dptr->srcs = 0;
719                 dptr++;
720         }
721         return 0;
722 }
723
724
725 int snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
726                        snd_pcm_format_t sformat, unsigned int schannels,
727                        snd_pcm_route_ttable_entry_t *ttable,
728                        unsigned int tt_ssize,
729                        unsigned int tt_cused, unsigned int tt_sused,
730                        snd_pcm_t *slave, int close_slave)
731 {
732         snd_pcm_t *pcm;
733         snd_pcm_route_t *route;
734         int err;
735         assert(pcmp && slave && ttable);
736         if (sformat != SND_PCM_FORMAT_UNKNOWN && 
737             snd_pcm_format_linear(sformat) != 1)
738                 return -EINVAL;
739         route = calloc(1, sizeof(snd_pcm_route_t));
740         if (!route) {
741                 return -ENOMEM;
742         }
743         route->sformat = sformat;
744         route->schannels = schannels;
745         route->plug.read = snd_pcm_route_read_areas;
746         route->plug.write = snd_pcm_route_write_areas;
747         route->plug.slave = slave;
748         route->plug.close_slave = close_slave;
749
750         pcm = calloc(1, sizeof(snd_pcm_t));
751         if (!pcm) {
752                 free(route);
753                 return -ENOMEM;
754         }
755         if (name)
756                 pcm->name = strdup(name);
757         pcm->type = SND_PCM_TYPE_ROUTE;
758         pcm->stream = slave->stream;
759         pcm->mode = slave->mode;
760         pcm->ops = &snd_pcm_route_ops;
761         pcm->op_arg = pcm;
762         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
763         pcm->fast_op_arg = pcm;
764         pcm->private_data = route;
765         pcm->poll_fd = slave->poll_fd;
766         pcm->hw_ptr = &route->plug.hw_ptr;
767         pcm->appl_ptr = &route->plug.appl_ptr;
768         err = route_load_ttable(&route->params, pcm->stream, tt_ssize, ttable, tt_cused, tt_sused);
769         if (err < 0) {
770                 snd_pcm_close(pcm);
771                 return err;
772         }
773         *pcmp = pcm;
774
775         return 0;
776 }
777
778 int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *ttable,
779                               unsigned int tt_csize, unsigned int tt_ssize,
780                               unsigned int *tt_cused, unsigned int *tt_sused,
781                               int schannels)
782 {
783         int cused = -1;
784         int sused = -1;
785         snd_config_iterator_t i, inext;
786         unsigned int k;
787         for (k = 0; k < tt_csize * tt_ssize; ++k)
788                 ttable[k] = 0.0;
789         snd_config_for_each(i, inext, tt) {
790                 snd_config_t *in = snd_config_iterator_entry(i);
791                 snd_config_iterator_t j, jnext;
792                 char *p;
793                 long cchannel;
794                 errno = 0;
795                 cchannel = strtol(snd_config_get_id(in), &p, 10);
796                 if (errno || *p || 
797                     cchannel < 0 || (unsigned int) cchannel > tt_csize) {
798                         SNDERR("Invalid client channel: %s", snd_config_get_id(in));
799                         return -EINVAL;
800                 }
801                 if (snd_config_get_type(in) != SND_CONFIG_TYPE_COMPOUND)
802                         return -EINVAL;
803                 snd_config_for_each(j, jnext, in) {
804                         snd_config_t *jn = snd_config_iterator_entry(j);
805                         double value;
806                         long schannel;
807                         int err;
808                         const char *id = snd_config_get_id(jn);
809                         errno = 0;
810                         schannel = strtol(id, &p, 10);
811                         if (errno || *p || 
812                             schannel < 0 || (unsigned int) schannel > tt_ssize || 
813                             (schannels > 0 && schannel >= schannels)) {
814                                 SNDERR("Invalid slave channel: %s", id);
815                                 return -EINVAL;
816                         }
817                         err = snd_config_get_real(jn, &value);
818                         if (err < 0) {
819                                 long v;
820                                 err = snd_config_get_integer(jn, &v);
821                                 if (err < 0) {
822                                         SNDERR("Invalid type for %s", id);
823                                         return -EINVAL;
824                                 }
825                                 value = v;
826                         }
827                         ttable[cchannel * tt_ssize + schannel] = value;
828                         if (schannel > sused)
829                                 sused = schannel;
830                 }
831                 if (cchannel > cused)
832                         cused = cchannel;
833         }
834         *tt_sused = sused + 1;
835         *tt_cused = cused + 1;
836         return 0;
837 }
838
839 #define MAX_CHANNELS 32
840
841 int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
842                         snd_config_t *conf, 
843                         snd_pcm_stream_t stream, int mode)
844 {
845         snd_config_iterator_t i, next;
846         const char *sname = NULL;
847         int err;
848         snd_pcm_t *spcm;
849         snd_config_t *slave = NULL;
850         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
851         int schannels = -1;
852         snd_config_t *tt = NULL;
853         snd_pcm_route_ttable_entry_t ttable[MAX_CHANNELS*MAX_CHANNELS];
854         unsigned int cused, sused;
855         snd_config_for_each(i, next, conf) {
856                 snd_config_t *n = snd_config_iterator_entry(i);
857                 const char *id = snd_config_get_id(n);
858                 if (strcmp(id, "comment") == 0)
859                         continue;
860                 if (strcmp(id, "type") == 0)
861                         continue;
862                 if (strcmp(id, "slave") == 0) {
863                         slave = n;
864                         continue;
865                 }
866                 if (strcmp(id, "ttable") == 0) {
867                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
868                                 SNDERR("Invalid type for %s", id);
869                                 return -EINVAL;
870                         }
871                         tt = n;
872                         continue;
873                 }
874                 SNDERR("Unknown field %s", id);
875                 return -EINVAL;
876         }
877         if (!slave) {
878                 SNDERR("slave is not defined");
879                 return -EINVAL;
880         }
881         if (!tt) {
882                 SNDERR("ttable is not defined");
883                 return -EINVAL;
884         }
885         err = snd_pcm_slave_conf(slave, &sname, 2,
886                                  SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
887                                  SND_PCM_HW_PARAM_CHANNELS, 0, &schannels);
888         if (err < 0)
889                 return err;
890         if (sformat != SND_PCM_FORMAT_UNKNOWN &&
891             snd_pcm_format_linear(sformat) != 1) {
892                 SNDERR("slave format is not linear");
893                 return -EINVAL;
894         }
895
896         err = snd_pcm_route_load_ttable(tt, ttable, MAX_CHANNELS, MAX_CHANNELS,
897                                         &cused, &sused, schannels);
898         if (err < 0)
899                 return err;
900
901         /* This is needed cause snd_config_update may destroy config */
902         sname = strdup(sname);
903         if (!sname)
904                 return  -ENOMEM;
905         err = snd_pcm_open(&spcm, sname, stream, mode);
906         free((void *) sname);
907         if (err < 0)
908                 return err;
909         err = snd_pcm_route_open(pcmp, name, sformat, schannels,
910                                  ttable, MAX_CHANNELS,
911                                  cused, sused,
912                                  spcm, 1);
913         if (err < 0)
914                 snd_pcm_close(spcm);
915         return err;
916 }
917                                 
918