1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
31 * accurate way of doing things, but it should be fast and
32 * transparent enough, not to mention capable of handling
33 * possible non-standard way of multichannel interleaving
34 * order. In other words, it is tough to break.
35 *
36 * The Good:
37 * + very generic and compact, provided that the supplied matrix map is in a
38 * sane form.
39 * + should be fast enough.
40 *
41 * The Bad:
42 * + somebody might disagree with it.
43 * + 'matrix' is kind of 0x7a69, due to prolong mental block.
44 */
45
46 #ifdef _KERNEL
47 #ifdef HAVE_KERNEL_OPTION_HEADERS
48 #include "opt_snd.h"
49 #endif
50 #include <dev/sound/pcm/sound.h>
51 #include <dev/sound/pcm/pcm.h>
52 #include "feeder_if.h"
53
54 #define SND_USE_FXDIV
55 #include "snd_fxdiv_gen.h"
56 #endif
57
58 #define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
59
60 #define SND_CHN_T_EOF 0x00e0fe0f
61 #define SND_CHN_T_NULL 0x0e0e0e0e
62
63 struct feed_matrix_info;
64
65 typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
66 uint8_t *, uint32_t);
67
68 struct feed_matrix_info {
69 uint32_t bps;
70 uint32_t ialign, oalign;
71 uint32_t in, out;
72 feed_matrix_t apply;
73 #ifdef FEEDMATRIX_GENERIC
74 intpcm_read_t *rd;
75 intpcm_write_t *wr;
76 #endif
77 struct {
78 int chn[SND_CHN_T_MAX + 1];
79 int mul, shift;
80 } matrix[SND_CHN_T_MAX + 1];
81 uint8_t reservoir[FEEDMATRIX_RESERVOIR];
82 };
83
84 static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
85 [SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
86 [SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
87 [SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
88 [SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
89 [SND_CHN_MATRIX_3_1] = SND_CHN_MATRIX_MAP_3_1,
90 [SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
91 [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
92 [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
93 [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
94 [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
95 [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
96 [SND_CHN_MATRIX_7_0] = SND_CHN_MATRIX_MAP_7_0,
97 [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
98 };
99
100 static int feeder_matrix_default_ids[9] = {
101 [0] = SND_CHN_MATRIX_UNKNOWN,
102 [1] = SND_CHN_MATRIX_1,
103 [2] = SND_CHN_MATRIX_2,
104 [3] = SND_CHN_MATRIX_3,
105 [4] = SND_CHN_MATRIX_4,
106 [5] = SND_CHN_MATRIX_5,
107 [6] = SND_CHN_MATRIX_6,
108 [7] = SND_CHN_MATRIX_7,
109 [8] = SND_CHN_MATRIX_8
110 };
111
112 #ifdef _KERNEL
113 #define FEEDMATRIX_CLIP_CHECK(...)
114 #else
115 #define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \
116 if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \
117 errx(1, "\n\n%s(): Sample clipping: %jd\n", \
118 __func__, (intmax_t)(v)); \
119 } while (0)
120 #endif
121
122 #define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \
123 static void \
124 feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \
125 uint8_t *src, uint8_t *dst, uint32_t count) \
126 { \
127 intpcm64_t accum; \
128 intpcm_t v; \
129 int i, j; \
130 \
131 do { \
132 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \
133 i++) { \
134 if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \
135 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
136 0); \
137 dst += PCM_##BIT##_BPS; \
138 continue; \
139 } else if (info->matrix[i].chn[1] == \
140 SND_CHN_T_EOF) { \
141 v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
142 src + info->matrix[i].chn[0]); \
143 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
144 v); \
145 dst += PCM_##BIT##_BPS; \
146 continue; \
147 } \
148 \
149 accum = 0; \
150 for (j = 0; \
151 info->matrix[i].chn[j] != SND_CHN_T_EOF; \
152 j++) { \
153 v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
154 src + info->matrix[i].chn[j]); \
155 accum += v; \
156 } \
157 \
158 accum = (accum * info->matrix[i].mul) >> \
159 info->matrix[i].shift; \
160 \
161 FEEDMATRIX_CLIP_CHECK(accum, BIT); \
162 \
163 v = (accum > PCM_S##BIT##_MAX) ? \
164 PCM_S##BIT##_MAX : \
165 ((accum < PCM_S##BIT##_MIN) ? \
166 PCM_S##BIT##_MIN : \
167 accum); \
168 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
169 dst += PCM_##BIT##_BPS; \
170 } \
171 src += info->ialign; \
172 } while (--count != 0); \
173 }
174
175 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
176 FEEDMATRIX_DECLARE(S, 16, LE)
177 FEEDMATRIX_DECLARE(S, 32, LE)
178 #endif
179 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
180 FEEDMATRIX_DECLARE(S, 16, BE)
181 FEEDMATRIX_DECLARE(S, 32, BE)
182 #endif
183 #ifdef SND_FEEDER_MULTIFORMAT
184 FEEDMATRIX_DECLARE(S, 8, NE)
185 FEEDMATRIX_DECLARE(S, 24, LE)
186 FEEDMATRIX_DECLARE(S, 24, BE)
187 FEEDMATRIX_DECLARE(U, 8, NE)
188 FEEDMATRIX_DECLARE(U, 16, LE)
189 FEEDMATRIX_DECLARE(U, 24, LE)
190 FEEDMATRIX_DECLARE(U, 32, LE)
191 FEEDMATRIX_DECLARE(U, 16, BE)
192 FEEDMATRIX_DECLARE(U, 24, BE)
193 FEEDMATRIX_DECLARE(U, 32, BE)
194 #endif
195
196 #define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \
197 { \
198 AFMT_##SIGN##BIT##_##ENDIAN, \
199 feed_matrix_##SIGN##BIT##ENDIAN \
200 }
201
202 static const struct {
203 uint32_t format;
204 feed_matrix_t apply;
205 } feed_matrix_tab[] = {
206 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
207 FEEDMATRIX_ENTRY(S, 16, LE),
208 FEEDMATRIX_ENTRY(S, 32, LE),
209 #endif
210 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
211 FEEDMATRIX_ENTRY(S, 16, BE),
212 FEEDMATRIX_ENTRY(S, 32, BE),
213 #endif
214 #ifdef SND_FEEDER_MULTIFORMAT
215 FEEDMATRIX_ENTRY(S, 8, NE),
216 FEEDMATRIX_ENTRY(S, 24, LE),
217 FEEDMATRIX_ENTRY(S, 24, BE),
218 FEEDMATRIX_ENTRY(U, 8, NE),
219 FEEDMATRIX_ENTRY(U, 16, LE),
220 FEEDMATRIX_ENTRY(U, 24, LE),
221 FEEDMATRIX_ENTRY(U, 32, LE),
222 FEEDMATRIX_ENTRY(U, 16, BE),
223 FEEDMATRIX_ENTRY(U, 24, BE),
224 FEEDMATRIX_ENTRY(U, 32, BE)
225 #endif
226 };
227
228 static void
feed_matrix_reset(struct feed_matrix_info * info)229 feed_matrix_reset(struct feed_matrix_info *info)
230 {
231 uint32_t i, j;
232
233 for (i = 0; i < nitems(info->matrix); i++) {
234 for (j = 0;
235 j < (sizeof(info->matrix[i].chn) /
236 sizeof(info->matrix[i].chn[0])); j++) {
237 info->matrix[i].chn[j] = SND_CHN_T_EOF;
238 }
239 info->matrix[i].mul = 1;
240 info->matrix[i].shift = 0;
241 }
242 }
243
244 #ifdef FEEDMATRIX_GENERIC
245 static void
feed_matrix_apply_generic(struct feed_matrix_info * info,uint8_t * src,uint8_t * dst,uint32_t count)246 feed_matrix_apply_generic(struct feed_matrix_info *info,
247 uint8_t *src, uint8_t *dst, uint32_t count)
248 {
249 intpcm64_t accum;
250 intpcm_t v;
251 int i, j;
252
253 do {
254 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
255 i++) {
256 if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
257 info->wr(dst, 0);
258 dst += info->bps;
259 continue;
260 } else if (info->matrix[i].chn[1] ==
261 SND_CHN_T_EOF) {
262 v = info->rd(src + info->matrix[i].chn[0]);
263 info->wr(dst, v);
264 dst += info->bps;
265 continue;
266 }
267
268 accum = 0;
269 for (j = 0;
270 info->matrix[i].chn[j] != SND_CHN_T_EOF;
271 j++) {
272 v = info->rd(src + info->matrix[i].chn[j]);
273 accum += v;
274 }
275
276 accum = (accum * info->matrix[i].mul) >>
277 info->matrix[i].shift;
278
279 FEEDMATRIX_CLIP_CHECK(accum, 32);
280
281 v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
282 ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
283 info->wr(dst, v);
284 dst += info->bps;
285 }
286 src += info->ialign;
287 } while (--count != 0);
288 }
289 #endif
290
291 static int
feed_matrix_setup(struct feed_matrix_info * info,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)292 feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
293 struct pcmchan_matrix *m_out)
294 {
295 uint32_t i, j, ch, in_mask, merge_mask;
296 int mul, shift;
297
298 if (info == NULL || m_in == NULL || m_out == NULL ||
299 AFMT_CHANNEL(info->in) != m_in->channels ||
300 AFMT_CHANNEL(info->out) != m_out->channels ||
301 m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
302 m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
303 return (EINVAL);
304
305 feed_matrix_reset(info);
306
307 /*
308 * If both in and out are part of standard matrix and identical, skip
309 * everything altogether.
310 */
311 if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
312 m_in->id > SND_CHN_MATRIX_END))
313 return (0);
314
315 /*
316 * Special case for mono input matrix. If the output supports
317 * possible 'center' channel, route it there. Otherwise, let it be
318 * matrixed to left/right.
319 */
320 if (m_in->id == SND_CHN_MATRIX_1_0) {
321 if (m_out->id == SND_CHN_MATRIX_1_0)
322 in_mask = SND_CHN_T_MASK_FL;
323 else if (m_out->mask & SND_CHN_T_MASK_FC)
324 in_mask = SND_CHN_T_MASK_FC;
325 else
326 in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
327 } else
328 in_mask = m_in->mask;
329
330 /* Merge, reduce, expand all possibilites. */
331 for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
332 m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
333 merge_mask = m_out->map[ch].members & in_mask;
334 if (merge_mask == 0) {
335 info->matrix[ch].chn[0] = SND_CHN_T_NULL;
336 continue;
337 }
338
339 j = 0;
340 for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
341 i += SND_CHN_T_STEP) {
342 if (merge_mask & (1 << i)) {
343 if (m_in->offset[i] >= 0 &&
344 m_in->offset[i] < (int)m_in->channels)
345 info->matrix[ch].chn[j++] =
346 m_in->offset[i] * info->bps;
347 else {
348 info->matrix[ch].chn[j++] =
349 SND_CHN_T_EOF;
350 break;
351 }
352 }
353 }
354
355 #define FEEDMATRIX_ATTN_SHIFT 16
356
357 if (j > 1) {
358 /*
359 * XXX For channel that require accumulation from
360 * multiple channels, apply a slight attenuation to
361 * avoid clipping.
362 */
363 mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
364 shift = FEEDMATRIX_ATTN_SHIFT;
365 while ((mul & 1) == 0 && shift > 0) {
366 mul >>= 1;
367 shift--;
368 }
369 info->matrix[ch].mul = mul;
370 info->matrix[ch].shift = shift;
371 }
372 }
373
374 #ifndef _KERNEL
375 fprintf(stderr, "Total: %d\n", ch);
376
377 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
378 fprintf(stderr, "%d: [", i);
379 for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
380 if (j != 0)
381 fprintf(stderr, ", ");
382 fprintf(stderr, "%d",
383 (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
384 0xffffffff : info->matrix[i].chn[j] / info->bps);
385 }
386 fprintf(stderr, "] attn: (x * %d) >> %d\n",
387 info->matrix[i].mul, info->matrix[i].shift);
388 }
389 #endif
390
391 return (0);
392 }
393
394 static int
feed_matrix_init(struct pcm_feeder * f)395 feed_matrix_init(struct pcm_feeder *f)
396 {
397 struct feed_matrix_info *info;
398 struct pcmchan_matrix *m_in, *m_out;
399 uint32_t i;
400 int ret;
401
402 if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
403 return (EINVAL);
404
405 info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
406 if (info == NULL)
407 return (ENOMEM);
408
409 info->in = f->desc->in;
410 info->out = f->desc->out;
411 info->bps = AFMT_BPS(info->in);
412 info->ialign = AFMT_ALIGN(info->in);
413 info->oalign = AFMT_ALIGN(info->out);
414 info->apply = NULL;
415
416 for (i = 0; info->apply == NULL &&
417 i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
418 if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
419 info->apply = feed_matrix_tab[i].apply;
420 }
421
422 if (info->apply == NULL) {
423 #ifdef FEEDMATRIX_GENERIC
424 info->rd = feeder_format_read_op(info->in);
425 info->wr = feeder_format_write_op(info->out);
426 if (info->rd == NULL || info->wr == NULL) {
427 free(info, M_DEVBUF);
428 return (EINVAL);
429 }
430 info->apply = feed_matrix_apply_generic;
431 #else
432 free(info, M_DEVBUF);
433 return (EINVAL);
434 #endif
435 }
436
437 m_in = feeder_matrix_format_map(info->in);
438 m_out = feeder_matrix_format_map(info->out);
439
440 ret = feed_matrix_setup(info, m_in, m_out);
441 if (ret != 0) {
442 free(info, M_DEVBUF);
443 return (ret);
444 }
445
446 f->data = info;
447
448 return (0);
449 }
450
451 static int
feed_matrix_free(struct pcm_feeder * f)452 feed_matrix_free(struct pcm_feeder *f)
453 {
454 struct feed_matrix_info *info;
455
456 info = f->data;
457 if (info != NULL)
458 free(info, M_DEVBUF);
459
460 f->data = NULL;
461
462 return (0);
463 }
464
465 static int
feed_matrix_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)466 feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
467 uint32_t count, void *source)
468 {
469 struct feed_matrix_info *info;
470 uint32_t j, inmax;
471 uint8_t *src, *dst;
472
473 info = f->data;
474 if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
475 return (FEEDER_FEED(f->source, c, b, count, source));
476
477 dst = b;
478 count = SND_FXROUND(count, info->oalign);
479 inmax = info->ialign + info->oalign;
480
481 /*
482 * This loop might look simmilar to other feeder_* loops, but be
483 * advised: matrixing might involve overlapping (think about
484 * swapping end to front or something like that). In this regard it
485 * might be simmilar to feeder_format, but feeder_format works on
486 * 'sample' domain where it can be fitted into single 32bit integer
487 * while matrixing works on 'sample frame' domain.
488 */
489 do {
490 if (count < info->oalign)
491 break;
492
493 if (count < inmax) {
494 src = info->reservoir;
495 j = info->ialign;
496 } else {
497 if (info->ialign == info->oalign)
498 j = count - info->oalign;
499 else if (info->ialign > info->oalign)
500 j = SND_FXROUND(count - info->oalign,
501 info->ialign);
502 else
503 j = (SND_FXDIV(count, info->oalign) - 1) *
504 info->ialign;
505 src = dst + count - j;
506 }
507
508 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
509 info->ialign);
510 if (j == 0)
511 break;
512
513 info->apply(info, src, dst, j);
514
515 j *= info->oalign;
516 dst += j;
517 count -= j;
518
519 } while (count != 0);
520
521 return (dst - b);
522 }
523
524 static struct pcm_feederdesc feeder_matrix_desc[] = {
525 { FEEDER_MATRIX, 0, 0, 0, 0 },
526 { 0, 0, 0, 0, 0 }
527 };
528
529 static kobj_method_t feeder_matrix_methods[] = {
530 KOBJMETHOD(feeder_init, feed_matrix_init),
531 KOBJMETHOD(feeder_free, feed_matrix_free),
532 KOBJMETHOD(feeder_feed, feed_matrix_feed),
533 KOBJMETHOD_END
534 };
535
536 FEEDER_DECLARE(feeder_matrix, NULL);
537
538 /* External */
539 int
feeder_matrix_setup(struct pcm_feeder * f,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)540 feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
541 struct pcmchan_matrix *m_out)
542 {
543
544 if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
545 f->data == NULL)
546 return (EINVAL);
547
548 return (feed_matrix_setup(f->data, m_in, m_out));
549 }
550
551 /*
552 * feeder_matrix_default_id(): For a given number of channels, return
553 * default preferred id (example: both 5.1 and
554 * 6.0 are simply 6 channels, but 5.1 is more
555 * preferable).
556 */
557 int
feeder_matrix_default_id(uint32_t ch)558 feeder_matrix_default_id(uint32_t ch)
559 {
560
561 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
562 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
563 return (SND_CHN_MATRIX_UNKNOWN);
564
565 return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
566 }
567
568 /*
569 * feeder_matrix_default_channel_map(): Ditto, but return matrix map
570 * instead.
571 */
572 struct pcmchan_matrix *
feeder_matrix_default_channel_map(uint32_t ch)573 feeder_matrix_default_channel_map(uint32_t ch)
574 {
575
576 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
577 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
578 return (NULL);
579
580 return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
581 }
582
583 /*
584 * feeder_matrix_default_format(): For a given audio format, return the
585 * proper audio format based on preferable
586 * matrix.
587 */
588 uint32_t
feeder_matrix_default_format(uint32_t format)589 feeder_matrix_default_format(uint32_t format)
590 {
591 struct pcmchan_matrix *m;
592 uint32_t i, ch, ext;
593
594 ch = AFMT_CHANNEL(format);
595 ext = AFMT_EXTCHANNEL(format);
596
597 if (ext != 0) {
598 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
599 if (feeder_matrix_maps[i].channels == ch &&
600 feeder_matrix_maps[i].ext == ext)
601 return (SND_FORMAT(format, ch, ext));
602 }
603 }
604
605 m = feeder_matrix_default_channel_map(ch);
606 if (m == NULL)
607 return (0x00000000);
608
609 return (SND_FORMAT(format, ch, m->ext));
610 }
611
612 /*
613 * feeder_matrix_format_id(): For a given audio format, return its matrix
614 * id.
615 */
616 int
feeder_matrix_format_id(uint32_t format)617 feeder_matrix_format_id(uint32_t format)
618 {
619 uint32_t i, ch, ext;
620
621 ch = AFMT_CHANNEL(format);
622 ext = AFMT_EXTCHANNEL(format);
623
624 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
625 if (feeder_matrix_maps[i].channels == ch &&
626 feeder_matrix_maps[i].ext == ext)
627 return (feeder_matrix_maps[i].id);
628 }
629
630 return (SND_CHN_MATRIX_UNKNOWN);
631 }
632
633 /*
634 * feeder_matrix_format_map(): For a given audio format, return its matrix
635 * map.
636 */
637 struct pcmchan_matrix *
feeder_matrix_format_map(uint32_t format)638 feeder_matrix_format_map(uint32_t format)
639 {
640 uint32_t i, ch, ext;
641
642 ch = AFMT_CHANNEL(format);
643 ext = AFMT_EXTCHANNEL(format);
644
645 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
646 if (feeder_matrix_maps[i].channels == ch &&
647 feeder_matrix_maps[i].ext == ext)
648 return (&feeder_matrix_maps[i]);
649 }
650
651 return (NULL);
652 }
653
654 /*
655 * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
656 */
657 struct pcmchan_matrix *
feeder_matrix_id_map(int id)658 feeder_matrix_id_map(int id)
659 {
660
661 if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
662 return (NULL);
663
664 return (&feeder_matrix_maps[id]);
665 }
666
667 /*
668 * feeder_matrix_compare(): Compare the simmilarities of matrices.
669 */
670 int
feeder_matrix_compare(struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)671 feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
672 {
673 uint32_t i;
674
675 if (m_in == m_out)
676 return (0);
677
678 if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
679 m_in->mask != m_out->mask)
680 return (1);
681
682 for (i = 0; i < nitems(m_in->map); i++) {
683 if (m_in->map[i].type != m_out->map[i].type)
684 return (1);
685 if (m_in->map[i].type == SND_CHN_T_MAX)
686 break;
687 if (m_in->map[i].members != m_out->map[i].members)
688 return (1);
689 if (i <= SND_CHN_T_END) {
690 if (m_in->offset[m_in->map[i].type] !=
691 m_out->offset[m_out->map[i].type])
692 return (1);
693 }
694 }
695
696 return (0);
697 }
698
699 /*
700 * XXX 4front interpretation of "surround" is ambigous and sort of
701 * conflicting with "rear"/"back". Map it to "side". Well..
702 * who cares?
703 */
704 static int snd_chn_to_oss[SND_CHN_T_MAX] = {
705 [SND_CHN_T_FL] = CHID_L,
706 [SND_CHN_T_FR] = CHID_R,
707 [SND_CHN_T_FC] = CHID_C,
708 [SND_CHN_T_LF] = CHID_LFE,
709 [SND_CHN_T_SL] = CHID_LS,
710 [SND_CHN_T_SR] = CHID_RS,
711 [SND_CHN_T_BL] = CHID_LR,
712 [SND_CHN_T_BR] = CHID_RR
713 };
714
715 #define SND_CHN_OSS_VALIDMASK \
716 (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
717 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
718 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \
719 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
720
721 #define SND_CHN_OSS_MAX 8
722 #define SND_CHN_OSS_BEGIN CHID_L
723 #define SND_CHN_OSS_END CHID_RR
724
725 static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
726 [CHID_L] = SND_CHN_T_FL,
727 [CHID_R] = SND_CHN_T_FR,
728 [CHID_C] = SND_CHN_T_FC,
729 [CHID_LFE] = SND_CHN_T_LF,
730 [CHID_LS] = SND_CHN_T_SL,
731 [CHID_RS] = SND_CHN_T_SR,
732 [CHID_LR] = SND_CHN_T_BL,
733 [CHID_RR] = SND_CHN_T_BR
734 };
735
736 /*
737 * Used by SNDCTL_DSP_GET_CHNORDER.
738 */
739 int
feeder_matrix_oss_get_channel_order(struct pcmchan_matrix * m,unsigned long long * map)740 feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
741 unsigned long long *map)
742 {
743 unsigned long long tmpmap;
744 uint32_t i;
745
746 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
747 m->channels > SND_CHN_OSS_MAX)
748 return (EINVAL);
749
750 tmpmap = 0x0000000000000000ULL;
751
752 for (i = 0; i < SND_CHN_OSS_MAX && m->map[i].type != SND_CHN_T_MAX;
753 i++) {
754 if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
755 return (EINVAL);
756 tmpmap |=
757 (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
758 (i * 4);
759 }
760
761 *map = tmpmap;
762
763 return (0);
764 }
765
766 /*
767 * Used by SNDCTL_DSP_SET_CHNORDER.
768 */
769 int
feeder_matrix_oss_set_channel_order(struct pcmchan_matrix * m,unsigned long long * map)770 feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
771 unsigned long long *map)
772 {
773 struct pcmchan_matrix tmp;
774 uint32_t chmask, i;
775 int ch, cheof;
776
777 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
778 m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
779 return (EINVAL);
780
781 tmp = *m;
782 tmp.channels = 0;
783 tmp.ext = 0;
784 tmp.mask = 0;
785 memset(tmp.offset, -1, sizeof(tmp.offset));
786 cheof = 0;
787
788 for (i = 0; i < SND_CHN_OSS_MAX; i++) {
789 ch = (*map >> (i * 4)) & 0xf;
790 if (ch < SND_CHN_OSS_BEGIN) {
791 if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
792 return (EINVAL);
793 cheof++;
794 tmp.map[i] = m->map[i];
795 continue;
796 } else if (ch > SND_CHN_OSS_END)
797 return (EINVAL);
798 else if (cheof != 0)
799 return (EINVAL);
800 ch = oss_to_snd_chn[ch];
801 chmask = 1 << ch;
802 /* channel not exist in matrix */
803 if (!(chmask & m->mask))
804 return (EINVAL);
805 /* duplicated channel */
806 if (chmask & tmp.mask)
807 return (EINVAL);
808 tmp.map[i] = m->map[m->offset[ch]];
809 if (tmp.map[i].type != ch)
810 return (EINVAL);
811 tmp.offset[ch] = i;
812 tmp.mask |= chmask;
813 tmp.channels++;
814 if (chmask & SND_CHN_T_MASK_LF)
815 tmp.ext++;
816 }
817
818 if (tmp.channels != m->channels || tmp.ext != m->ext ||
819 tmp.mask != m->mask ||
820 tmp.map[m->channels].type != SND_CHN_T_MAX)
821 return (EINVAL);
822
823 *m = tmp;
824
825 return (0);
826 }
827