The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/pcm/feeder_matrix.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

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

Cache object: 49ba075b3e27f0b00f63941e0b14ab76


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.