2 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
29 * accurate way of doing things, but it should be fast and
30 * transparent enough, not to mention capable of handling
31 * possible non-standard way of multichannel interleaving
32 * order. In other words, it is tough to break.
35 * + very generic and compact, provided that the supplied matrix map is in a
37 * + should be fast enough.
40 * + somebody might disagree with it.
41 * + 'matrix' is kind of 0x7a69, due to prolong mental block.
45 #ifdef HAVE_KERNEL_OPTION_HEADERS
48 #include <dev/sound/pcm/sound.h>
49 #include <dev/sound/pcm/pcm.h>
50 #include "feeder_if.h"
53 #include "snd_fxdiv_gen.h"
55 SND_DECLARE_FILE("$FreeBSD$");
58 #define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
60 #define SND_CHN_T_EOF 0x00e0fe0f
61 #define SND_CHN_T_NULL 0x0e0e0e0e
63 struct feed_matrix_info;
65 typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
68 struct feed_matrix_info {
70 uint32_t ialign, oalign;
73 #ifdef FEEDMATRIX_GENERIC
78 int chn[SND_CHN_T_MAX + 1];
80 } matrix[SND_CHN_T_MAX + 1];
81 uint8_t reservoir[FEEDMATRIX_RESERVOIR];
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_4_0] = SND_CHN_MATRIX_MAP_4_0,
90 [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
91 [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
92 [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
93 [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
94 [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
95 [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
98 static int feeder_matrix_default_ids[9] = {
99 [0] = SND_CHN_MATRIX_UNKNOWN,
100 [1] = SND_CHN_MATRIX_1,
101 [2] = SND_CHN_MATRIX_2,
102 [3] = SND_CHN_MATRIX_3,
103 [4] = SND_CHN_MATRIX_4,
104 [5] = SND_CHN_MATRIX_5,
105 [6] = SND_CHN_MATRIX_6,
106 [7] = SND_CHN_MATRIX_7,
107 [8] = SND_CHN_MATRIX_8
111 #define FEEDMATRIX_CLIP_CHECK(...)
113 #define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \
114 if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \
115 errx(1, "\n\n%s(): Sample clipping: %jd\n", \
116 __func__, (intmax_t)(v)); \
120 #define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \
122 feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \
123 uint8_t *src, uint8_t *dst, uint32_t count) \
130 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \
132 if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \
133 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
135 dst += PCM_##BIT##_BPS; \
137 } else if (info->matrix[i].chn[1] == \
139 v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
140 src + info->matrix[i].chn[0]); \
141 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
143 dst += PCM_##BIT##_BPS; \
149 info->matrix[i].chn[j] != SND_CHN_T_EOF; \
151 v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
152 src + info->matrix[i].chn[j]); \
156 accum = (accum * info->matrix[i].mul) >> \
157 info->matrix[i].shift; \
159 FEEDMATRIX_CLIP_CHECK(accum, BIT); \
161 v = (accum > PCM_S##BIT##_MAX) ? \
163 ((accum < PCM_S##BIT##_MIN) ? \
166 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
167 dst += PCM_##BIT##_BPS; \
169 src += info->ialign; \
170 } while (--count != 0); \
173 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
174 FEEDMATRIX_DECLARE(S, 16, LE)
175 FEEDMATRIX_DECLARE(S, 32, LE)
177 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
178 FEEDMATRIX_DECLARE(S, 16, BE)
179 FEEDMATRIX_DECLARE(S, 32, BE)
181 #ifdef SND_FEEDER_MULTIFORMAT
182 FEEDMATRIX_DECLARE(S, 8, NE)
183 FEEDMATRIX_DECLARE(S, 24, LE)
184 FEEDMATRIX_DECLARE(S, 24, BE)
185 FEEDMATRIX_DECLARE(U, 8, NE)
186 FEEDMATRIX_DECLARE(U, 16, LE)
187 FEEDMATRIX_DECLARE(U, 24, LE)
188 FEEDMATRIX_DECLARE(U, 32, LE)
189 FEEDMATRIX_DECLARE(U, 16, BE)
190 FEEDMATRIX_DECLARE(U, 24, BE)
191 FEEDMATRIX_DECLARE(U, 32, BE)
194 #define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \
196 AFMT_##SIGN##BIT##_##ENDIAN, \
197 feed_matrix_##SIGN##BIT##ENDIAN \
200 static const struct {
203 } feed_matrix_tab[] = {
204 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
205 FEEDMATRIX_ENTRY(S, 16, LE),
206 FEEDMATRIX_ENTRY(S, 32, LE),
208 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
209 FEEDMATRIX_ENTRY(S, 16, BE),
210 FEEDMATRIX_ENTRY(S, 32, BE),
212 #ifdef SND_FEEDER_MULTIFORMAT
213 FEEDMATRIX_ENTRY(S, 8, NE),
214 FEEDMATRIX_ENTRY(S, 24, LE),
215 FEEDMATRIX_ENTRY(S, 24, BE),
216 FEEDMATRIX_ENTRY(U, 8, NE),
217 FEEDMATRIX_ENTRY(U, 16, LE),
218 FEEDMATRIX_ENTRY(U, 24, LE),
219 FEEDMATRIX_ENTRY(U, 32, LE),
220 FEEDMATRIX_ENTRY(U, 16, BE),
221 FEEDMATRIX_ENTRY(U, 24, BE),
222 FEEDMATRIX_ENTRY(U, 32, BE)
227 feed_matrix_reset(struct feed_matrix_info *info)
231 for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
233 j < (sizeof(info->matrix[i].chn) /
234 sizeof(info->matrix[i].chn[0])); j++) {
235 info->matrix[i].chn[j] = SND_CHN_T_EOF;
237 info->matrix[i].mul = 1;
238 info->matrix[i].shift = 0;
242 #ifdef FEEDMATRIX_GENERIC
244 feed_matrix_apply_generic(struct feed_matrix_info *info,
245 uint8_t *src, uint8_t *dst, uint32_t count)
252 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
254 if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
258 } else if (info->matrix[i].chn[1] ==
260 v = info->rd(src + info->matrix[i].chn[0]);
268 info->matrix[i].chn[j] != SND_CHN_T_EOF;
270 v = info->rd(src + info->matrix[i].chn[j]);
274 accum = (accum * info->matrix[i].mul) >>
275 info->matrix[i].shift;
277 FEEDMATRIX_CLIP_CHECK(accum, 32);
279 v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
280 ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
285 } while (--count != 0);
290 feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
291 struct pcmchan_matrix *m_out)
293 uint32_t i, j, ch, in_mask, merge_mask;
297 if (info == NULL || m_in == NULL || m_out == NULL ||
298 AFMT_CHANNEL(info->in) != m_in->channels ||
299 AFMT_CHANNEL(info->out) != m_out->channels ||
300 m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
301 m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
304 feed_matrix_reset(info);
307 * If both in and out are part of standard matrix and identical, skip
308 * everything alltogether.
310 if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
311 m_in->id > SND_CHN_MATRIX_END))
315 * Special case for mono input matrix. If the output supports
316 * possible 'center' channel, route it there. Otherwise, let it be
317 * matrixed to left/right.
319 if (m_in->id == SND_CHN_MATRIX_1_0) {
320 if (m_out->id == SND_CHN_MATRIX_1_0)
321 in_mask = SND_CHN_T_MASK_FL;
322 else if (m_out->mask & SND_CHN_T_MASK_FC)
323 in_mask = SND_CHN_T_MASK_FC;
325 in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
327 in_mask = m_in->mask;
329 /* Merge, reduce, expand all possibilites. */
330 for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
331 m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
332 merge_mask = m_out->map[ch].members & in_mask;
333 if (merge_mask == 0) {
334 info->matrix[ch].chn[0] = SND_CHN_T_NULL;
339 for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
340 i += SND_CHN_T_STEP) {
341 if (merge_mask & (1 << i)) {
342 if (m_in->offset[i] >= 0 &&
343 m_in->offset[i] < (int)m_in->channels)
344 info->matrix[ch].chn[j++] =
345 m_in->offset[i] * info->bps;
347 info->matrix[ch].chn[j++] =
354 #define FEEDMATRIX_ATTN_SHIFT 16
358 * XXX For channel that require accumulation from
359 * multiple channels, apply a slight attenuation to
362 mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
363 shift = FEEDMATRIX_ATTN_SHIFT;
364 while ((mul & 1) == 0 && shift > 0) {
368 info->matrix[ch].mul = mul;
369 info->matrix[ch].shift = shift;
374 fprintf(stderr, "Total: %d\n", ch);
376 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
377 fprintf(stderr, "%d: [", i);
378 for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
380 fprintf(stderr, ", ");
381 fprintf(stderr, "%d",
382 (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
383 0xffffffff : info->matrix[i].chn[j] / info->bps);
385 fprintf(stderr, "] attn: (x * %d) >> %d\n",
386 info->matrix[i].mul, info->matrix[i].shift);
394 feed_matrix_init(struct pcm_feeder *f)
396 struct feed_matrix_info *info;
397 struct pcmchan_matrix *m_in, *m_out;
401 if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
404 info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
408 info->in = f->desc->in;
409 info->out = f->desc->out;
410 info->bps = AFMT_BPS(info->in);
411 info->ialign = AFMT_ALIGN(info->in);
412 info->oalign = AFMT_ALIGN(info->out);
415 for (i = 0; info->apply == NULL &&
416 i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
417 if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
418 info->apply = feed_matrix_tab[i].apply;
421 if (info->apply == NULL) {
422 #ifdef FEEDMATRIX_GENERIC
423 info->rd = feeder_format_read_op(info->in);
424 info->wr = feeder_format_write_op(info->out);
425 if (info->rd == NULL || info->wr == NULL) {
426 free(info, M_DEVBUF);
429 info->apply = feed_matrix_apply_generic;
431 free(info, M_DEVBUF);
436 m_in = feeder_matrix_format_map(info->in);
437 m_out = feeder_matrix_format_map(info->out);
439 ret = feed_matrix_setup(info, m_in, m_out);
441 free(info, M_DEVBUF);
451 feed_matrix_free(struct pcm_feeder *f)
453 struct feed_matrix_info *info;
457 free(info, M_DEVBUF);
465 feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
466 uint32_t count, void *source)
468 struct feed_matrix_info *info;
473 if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
474 return (FEEDER_FEED(f->source, c, b, count, source));
477 count = SND_FXROUND(count, info->oalign);
478 inmax = info->ialign + info->oalign;
481 * This loop might look simmilar to other feeder_* loops, but be
482 * advised: matrixing might involve overlapping (think about
483 * swapping end to front or something like that). In this regard it
484 * might be simmilar to feeder_format, but feeder_format works on
485 * 'sample' domain where it can be fitted into single 32bit integer
486 * while matrixing works on 'sample frame' domain.
489 if (count < info->oalign)
493 src = info->reservoir;
496 if (info->ialign == info->oalign)
497 j = count - info->oalign;
498 else if (info->ialign > info->oalign)
499 j = SND_FXROUND(count - info->oalign,
502 j = (SND_FXDIV(count, info->oalign) - 1) *
504 src = dst + count - j;
507 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
512 info->apply(info, src, dst, j);
518 } while (count != 0);
523 static struct pcm_feederdesc feeder_matrix_desc[] = {
524 { FEEDER_MATRIX, 0, 0, 0, 0 },
528 static kobj_method_t feeder_matrix_methods[] = {
529 KOBJMETHOD(feeder_init, feed_matrix_init),
530 KOBJMETHOD(feeder_free, feed_matrix_free),
531 KOBJMETHOD(feeder_feed, feed_matrix_feed),
535 FEEDER_DECLARE(feeder_matrix, NULL);
539 feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
540 struct pcmchan_matrix *m_out)
543 if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
547 return (feed_matrix_setup(f->data, m_in, m_out));
551 * feeder_matrix_default_id(): For a given number of channels, return
552 * default prefered id (example: both 5.1 and
553 * 6.0 are simply 6 channels, but 5.1 is more
557 feeder_matrix_default_id(uint32_t ch)
560 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
561 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
562 return (SND_CHN_MATRIX_UNKNOWN);
564 return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
568 * feeder_matrix_default_channel_map(): Ditto, but return matrix map
571 struct pcmchan_matrix *
572 feeder_matrix_default_channel_map(uint32_t ch)
575 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
576 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
579 return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
583 * feeder_matrix_default_format(): For a given audio format, return the
584 * proper audio format based on preferable
588 feeder_matrix_default_format(uint32_t format)
590 struct pcmchan_matrix *m;
593 ch = AFMT_CHANNEL(format);
594 ext = AFMT_EXTCHANNEL(format);
597 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
598 if (feeder_matrix_maps[i].channels == ch &&
599 feeder_matrix_maps[i].ext == ext)
600 return (SND_FORMAT(format, ch, ext));
604 m = feeder_matrix_default_channel_map(ch);
608 return (SND_FORMAT(format, ch, m->ext));
612 * feeder_matrix_format_id(): For a given audio format, return its matrix
616 feeder_matrix_format_id(uint32_t format)
620 ch = AFMT_CHANNEL(format);
621 ext = AFMT_EXTCHANNEL(format);
623 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
624 if (feeder_matrix_maps[i].channels == ch &&
625 feeder_matrix_maps[i].ext == ext)
626 return (feeder_matrix_maps[i].id);
629 return (SND_CHN_MATRIX_UNKNOWN);
633 * feeder_matrix_format_map(): For a given audio format, return its matrix
636 struct pcmchan_matrix *
637 feeder_matrix_format_map(uint32_t format)
641 ch = AFMT_CHANNEL(format);
642 ext = AFMT_EXTCHANNEL(format);
644 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
645 if (feeder_matrix_maps[i].channels == ch &&
646 feeder_matrix_maps[i].ext == ext)
647 return (&feeder_matrix_maps[i]);
654 * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
656 struct pcmchan_matrix *
657 feeder_matrix_id_map(int id)
660 if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
663 return (&feeder_matrix_maps[id]);
667 * feeder_matrix_compare(): Compare the simmilarities of matrices.
670 feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
677 if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
678 m_in->mask != m_out->mask)
681 for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
682 if (m_in->map[i].type != m_out->map[i].type)
684 if (m_in->map[i].type == SND_CHN_T_MAX)
686 if (m_in->map[i].members != m_out->map[i].members)
688 if (i <= SND_CHN_T_END) {
689 if (m_in->offset[m_in->map[i].type] !=
690 m_out->offset[m_out->map[i].type])
699 * XXX 4front intepretation of "surround" is ambigous and sort of
700 * conflicting with "rear"/"back". Map it to "side". Well..
703 static int snd_chn_to_oss[SND_CHN_T_MAX] = {
704 [SND_CHN_T_FL] = CHID_L,
705 [SND_CHN_T_FR] = CHID_R,
706 [SND_CHN_T_FC] = CHID_C,
707 [SND_CHN_T_LF] = CHID_LFE,
708 [SND_CHN_T_SL] = CHID_LS,
709 [SND_CHN_T_SR] = CHID_RS,
710 [SND_CHN_T_BL] = CHID_LR,
711 [SND_CHN_T_BR] = CHID_RR
714 #define SND_CHN_OSS_VALIDMASK \
715 (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
716 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
717 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \
718 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
720 #define SND_CHN_OSS_MAX 8
721 #define SND_CHN_OSS_BEGIN CHID_L
722 #define SND_CHN_OSS_END CHID_RR
724 static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
725 [CHID_L] = SND_CHN_T_FL,
726 [CHID_R] = SND_CHN_T_FR,
727 [CHID_C] = SND_CHN_T_FC,
728 [CHID_LFE] = SND_CHN_T_LF,
729 [CHID_LS] = SND_CHN_T_SL,
730 [CHID_RS] = SND_CHN_T_SR,
731 [CHID_LR] = SND_CHN_T_BL,
732 [CHID_RR] = SND_CHN_T_BR
736 * Used by SNDCTL_DSP_GET_CHNORDER.
739 feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
740 unsigned long long *map)
742 unsigned long long tmpmap;
745 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
746 m->channels > SND_CHN_OSS_MAX)
749 tmpmap = 0x0000000000000000ULL;
751 for (i = 0; m->map[i].type != SND_CHN_T_MAX &&
752 i < SND_CHN_OSS_MAX; i++) {
753 if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
756 (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
766 * Used by SNDCTL_DSP_SET_CHNORDER.
769 feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
770 unsigned long long *map)
772 struct pcmchan_matrix tmp;
776 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
777 m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
784 memset(tmp.offset, -1, sizeof(tmp.offset));
787 for (i = 0; i < SND_CHN_OSS_MAX; i++) {
788 ch = (*map >> (i * 4)) & 0xf;
789 if (ch < SND_CHN_OSS_BEGIN) {
790 if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
793 tmp.map[i] = m->map[i];
795 } else if (ch > SND_CHN_OSS_END)
799 ch = oss_to_snd_chn[ch];
801 /* channel not exist in matrix */
802 if (!(chmask & m->mask))
804 /* duplicated channel */
805 if (chmask & tmp.mask)
807 tmp.map[i] = m->map[m->offset[ch]];
808 if (tmp.map[i].type != ch)
813 if (chmask & SND_CHN_T_MASK_LF)
817 if (tmp.channels != m->channels || tmp.ext != m->ext ||
818 tmp.mask != m->mask ||
819 tmp.map[m->channels].type != SND_CHN_T_MAX)