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 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 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 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 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 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 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 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 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 * 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 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 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 * 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 * 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 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 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 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