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