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-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 /*
   28  * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
   29  *                accurate way of doing things, but it should be fast and
   30  *                transparent enough, not to mention capable of handling
   31  *                possible non-standard way of multichannel interleaving
   32  *                order. In other words, it is tough to break.
   33  *
   34  * The Good:
   35  * + very generic and compact, provided that the supplied matrix map is in a
   36  *   sane form.
   37  * + should be fast enough.
   38  *
   39  * The Bad:
   40  * + somebody might disagree with it.
   41  * + 'matrix' is kind of 0x7a69, due to prolong mental block.
   42  */
   43 
   44 #ifdef _KERNEL
   45 #ifdef HAVE_KERNEL_OPTION_HEADERS
   46 #include "opt_snd.h"
   47 #endif
   48 #include <dev/sound/pcm/sound.h>
   49 #include <dev/sound/pcm/pcm.h>
   50 #include "feeder_if.h"
   51 
   52 #define SND_USE_FXDIV
   53 #include "snd_fxdiv_gen.h"
   54 
   55 SND_DECLARE_FILE("$FreeBSD$");
   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 < (sizeof(info->matrix) / sizeof(info->matrix[0])); 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 
  299         if (info == NULL || m_in == NULL || m_out == NULL ||
  300             AFMT_CHANNEL(info->in) != m_in->channels ||
  301             AFMT_CHANNEL(info->out) != m_out->channels ||
  302             m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
  303             m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
  304                 return (EINVAL);
  305 
  306         feed_matrix_reset(info);
  307 
  308         /*
  309          * If both in and out are part of standard matrix and identical, skip
  310          * everything alltogether.
  311          */
  312         if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
  313             m_in->id > SND_CHN_MATRIX_END))
  314                 return (0);
  315 
  316         /*
  317          * Special case for mono input matrix. If the output supports
  318          * possible 'center' channel, route it there. Otherwise, let it be
  319          * matrixed to left/right.
  320          */
  321         if (m_in->id == SND_CHN_MATRIX_1_0) {
  322                 if (m_out->id == SND_CHN_MATRIX_1_0)
  323                         in_mask = SND_CHN_T_MASK_FL;
  324                 else if (m_out->mask & SND_CHN_T_MASK_FC)
  325                         in_mask = SND_CHN_T_MASK_FC;
  326                 else
  327                         in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
  328         } else
  329                 in_mask = m_in->mask;
  330 
  331         /* Merge, reduce, expand all possibilites. */
  332         for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
  333             m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
  334                 merge_mask = m_out->map[ch].members & in_mask;
  335                 if (merge_mask == 0) {
  336                         info->matrix[ch].chn[0] = SND_CHN_T_NULL;
  337                         continue;
  338                 }
  339 
  340                 j = 0;
  341                 for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
  342                     i += SND_CHN_T_STEP) {
  343                         if (merge_mask & (1 << i)) {
  344                                 if (m_in->offset[i] >= 0 &&
  345                                     m_in->offset[i] < (int)m_in->channels)
  346                                         info->matrix[ch].chn[j++] =
  347                                             m_in->offset[i] * info->bps;
  348                                 else {
  349                                         info->matrix[ch].chn[j++] =
  350                                             SND_CHN_T_EOF;
  351                                         break;
  352                                 }
  353                         }
  354                 }
  355 
  356 #define FEEDMATRIX_ATTN_SHIFT   16
  357 
  358                 if (j > 1) {
  359                         /*
  360                          * XXX For channel that require accumulation from
  361                          * multiple channels, apply a slight attenuation to
  362                          * avoid clipping.
  363                          */
  364                         mul   = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
  365                         shift = FEEDMATRIX_ATTN_SHIFT;
  366                         while ((mul & 1) == 0 && shift > 0) {
  367                                 mul >>= 1;
  368                                 shift--;
  369                         }
  370                         info->matrix[ch].mul   = mul;
  371                         info->matrix[ch].shift = shift;
  372                 }
  373         }
  374 
  375 #ifndef _KERNEL
  376         fprintf(stderr, "Total: %d\n", ch);
  377 
  378         for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
  379                 fprintf(stderr, "%d: [", i);
  380                 for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
  381                         if (j != 0)
  382                                 fprintf(stderr, ", ");
  383                         fprintf(stderr, "%d",
  384                             (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
  385                             0xffffffff : info->matrix[i].chn[j] / info->bps);
  386                 }
  387                 fprintf(stderr, "] attn: (x * %d) >> %d\n",
  388                     info->matrix[i].mul, info->matrix[i].shift);
  389         }
  390 #endif
  391 
  392         return (0);
  393 }
  394 
  395 static int
  396 feed_matrix_init(struct pcm_feeder *f)
  397 {
  398         struct feed_matrix_info *info;
  399         struct pcmchan_matrix *m_in, *m_out;
  400         uint32_t i;
  401         int ret;
  402 
  403         if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
  404                 return (EINVAL);
  405 
  406         info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
  407         if (info == NULL)
  408                 return (ENOMEM);
  409 
  410         info->in = f->desc->in;
  411         info->out = f->desc->out;
  412         info->bps = AFMT_BPS(info->in);
  413         info->ialign = AFMT_ALIGN(info->in);
  414         info->oalign = AFMT_ALIGN(info->out);
  415         info->apply = NULL;
  416 
  417         for (i = 0; info->apply == NULL &&
  418             i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
  419                 if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
  420                         info->apply = feed_matrix_tab[i].apply;
  421         }
  422 
  423         if (info->apply == NULL) {
  424 #ifdef FEEDMATRIX_GENERIC
  425                 info->rd = feeder_format_read_op(info->in);
  426                 info->wr = feeder_format_write_op(info->out);
  427                 if (info->rd == NULL || info->wr == NULL) {
  428                         free(info, M_DEVBUF);
  429                         return (EINVAL);
  430                 }
  431                 info->apply = feed_matrix_apply_generic;
  432 #else
  433                 free(info, M_DEVBUF);
  434                 return (EINVAL);
  435 #endif
  436         }
  437 
  438         m_in  = feeder_matrix_format_map(info->in);
  439         m_out = feeder_matrix_format_map(info->out);
  440 
  441         ret = feed_matrix_setup(info, m_in, m_out);
  442         if (ret != 0) {
  443                 free(info, M_DEVBUF);
  444                 return (ret);
  445         }
  446 
  447         f->data = info;
  448 
  449         return (0);
  450 }
  451 
  452 static int
  453 feed_matrix_free(struct pcm_feeder *f)
  454 {
  455         struct feed_matrix_info *info;
  456 
  457         info = f->data;
  458         if (info != NULL)
  459                 free(info, M_DEVBUF);
  460 
  461         f->data = NULL;
  462 
  463         return (0);
  464 }
  465 
  466 static int
  467 feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
  468     uint32_t count, void *source)
  469 {
  470         struct feed_matrix_info *info;
  471         uint32_t j, inmax;
  472         uint8_t *src, *dst;
  473 
  474         info = f->data;
  475         if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
  476                 return (FEEDER_FEED(f->source, c, b, count, source));
  477 
  478         dst = b;
  479         count = SND_FXROUND(count, info->oalign);
  480         inmax = info->ialign + info->oalign;
  481 
  482         /*
  483          * This loop might look simmilar to other feeder_* loops, but be
  484          * advised: matrixing might involve overlapping (think about
  485          * swapping end to front or something like that). In this regard it
  486          * might be simmilar to feeder_format, but feeder_format works on
  487          * 'sample' domain where it can be fitted into single 32bit integer
  488          * while matrixing works on 'sample frame' domain.
  489          */
  490         do {
  491                 if (count < info->oalign)
  492                         break;
  493 
  494                 if (count < inmax) {
  495                         src = info->reservoir;
  496                         j = info->ialign;
  497                 } else {
  498                         if (info->ialign == info->oalign)
  499                                 j = count - info->oalign;
  500                         else if (info->ialign > info->oalign)
  501                                 j = SND_FXROUND(count - info->oalign,
  502                                     info->ialign);
  503                         else
  504                                 j = (SND_FXDIV(count, info->oalign) - 1) *
  505                                     info->ialign;
  506                         src = dst + count - j;
  507                 }
  508 
  509                 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
  510                     info->ialign);
  511                 if (j == 0)
  512                         break;
  513 
  514                 info->apply(info, src, dst, j);
  515 
  516                 j *= info->oalign;
  517                 dst += j;
  518                 count -= j;
  519 
  520         } while (count != 0);
  521 
  522         return (dst - b);
  523 }
  524 
  525 static struct pcm_feederdesc feeder_matrix_desc[] = {
  526         { FEEDER_MATRIX, 0, 0, 0, 0 },
  527         { 0, 0, 0, 0, 0 }
  528 };
  529 
  530 static kobj_method_t feeder_matrix_methods[] = {
  531         KOBJMETHOD(feeder_init,         feed_matrix_init),
  532         KOBJMETHOD(feeder_free,         feed_matrix_free),
  533         KOBJMETHOD(feeder_feed,         feed_matrix_feed),
  534         KOBJMETHOD_END
  535 };
  536 
  537 FEEDER_DECLARE(feeder_matrix, NULL);
  538 
  539 /* External */
  540 int
  541 feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
  542     struct pcmchan_matrix *m_out)
  543 {
  544 
  545         if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
  546             f->data == NULL)
  547                 return (EINVAL);
  548 
  549         return (feed_matrix_setup(f->data, m_in, m_out));
  550 }
  551 
  552 /*
  553  * feeder_matrix_default_id(): For a given number of channels, return
  554  *                             default prefered id (example: both 5.1 and
  555  *                             6.0 are simply 6 channels, but 5.1 is more
  556  *                             preferable).
  557  */
  558 int
  559 feeder_matrix_default_id(uint32_t ch)
  560 {
  561 
  562         if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
  563             ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
  564                 return (SND_CHN_MATRIX_UNKNOWN);
  565 
  566         return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
  567 }
  568 
  569 /*
  570  * feeder_matrix_default_channel_map(): Ditto, but return matrix map
  571  *                                      instead.
  572  */
  573 struct pcmchan_matrix *
  574 feeder_matrix_default_channel_map(uint32_t ch)
  575 {
  576 
  577         if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
  578             ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
  579                 return (NULL);
  580 
  581         return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
  582 }
  583 
  584 /*
  585  * feeder_matrix_default_format(): For a given audio format, return the
  586  *                                 proper audio format based on preferable
  587  *                                 matrix.
  588  */
  589 uint32_t
  590 feeder_matrix_default_format(uint32_t format)
  591 {
  592         struct pcmchan_matrix *m;
  593         uint32_t i, ch, ext;
  594 
  595         ch = AFMT_CHANNEL(format);
  596         ext = AFMT_EXTCHANNEL(format);
  597 
  598         if (ext != 0) {
  599                 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
  600                         if (feeder_matrix_maps[i].channels == ch &&
  601                             feeder_matrix_maps[i].ext == ext)
  602                         return (SND_FORMAT(format, ch, ext));
  603                 }
  604         }
  605 
  606         m = feeder_matrix_default_channel_map(ch);
  607         if (m == NULL)
  608                 return (0x00000000);
  609 
  610         return (SND_FORMAT(format, ch, m->ext));
  611 }
  612 
  613 /*
  614  * feeder_matrix_format_id(): For a given audio format, return its matrix
  615  *                            id.
  616  */
  617 int
  618 feeder_matrix_format_id(uint32_t format)
  619 {
  620         uint32_t i, ch, ext;
  621 
  622         ch = AFMT_CHANNEL(format);
  623         ext = AFMT_EXTCHANNEL(format);
  624 
  625         for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
  626                 if (feeder_matrix_maps[i].channels == ch &&
  627                     feeder_matrix_maps[i].ext == ext)
  628                         return (feeder_matrix_maps[i].id);
  629         }
  630 
  631         return (SND_CHN_MATRIX_UNKNOWN);
  632 }
  633 
  634 /*
  635  * feeder_matrix_format_map(): For a given audio format, return its matrix
  636  *                             map.
  637  */
  638 struct pcmchan_matrix *
  639 feeder_matrix_format_map(uint32_t format)
  640 {
  641         uint32_t i, ch, ext;
  642 
  643         ch = AFMT_CHANNEL(format);
  644         ext = AFMT_EXTCHANNEL(format);
  645 
  646         for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
  647                 if (feeder_matrix_maps[i].channels == ch &&
  648                     feeder_matrix_maps[i].ext == ext)
  649                         return (&feeder_matrix_maps[i]);
  650         }
  651 
  652         return (NULL);
  653 }
  654 
  655 /*
  656  * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
  657  */
  658 struct pcmchan_matrix *
  659 feeder_matrix_id_map(int id)
  660 {
  661 
  662         if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
  663                 return (NULL);
  664 
  665         return (&feeder_matrix_maps[id]);
  666 }
  667 
  668 /*
  669  * feeder_matrix_compare(): Compare the simmilarities of matrices.
  670  */
  671 int
  672 feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
  673 {
  674         uint32_t i;
  675 
  676         if (m_in == m_out)
  677                 return (0);
  678 
  679         if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
  680             m_in->mask != m_out->mask)
  681                 return (1);
  682 
  683         for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
  684                 if (m_in->map[i].type != m_out->map[i].type)
  685                         return (1);
  686                 if (m_in->map[i].type == SND_CHN_T_MAX)
  687                         break;
  688                 if (m_in->map[i].members != m_out->map[i].members)
  689                         return (1);
  690                 if (i <= SND_CHN_T_END) {
  691                         if (m_in->offset[m_in->map[i].type] !=
  692                             m_out->offset[m_out->map[i].type])
  693                                 return (1);
  694                 }
  695         }
  696 
  697         return (0);
  698 }
  699 
  700 /*
  701  * XXX 4front intepretation of "surround" is ambigous and sort of
  702  *     conflicting with "rear"/"back". Map it to "side". Well.. 
  703  *     who cares?
  704  */
  705 static int snd_chn_to_oss[SND_CHN_T_MAX] = {
  706         [SND_CHN_T_FL] = CHID_L,
  707         [SND_CHN_T_FR] = CHID_R,
  708         [SND_CHN_T_FC] = CHID_C,
  709         [SND_CHN_T_LF] = CHID_LFE,
  710         [SND_CHN_T_SL] = CHID_LS,
  711         [SND_CHN_T_SR] = CHID_RS,
  712         [SND_CHN_T_BL] = CHID_LR,
  713         [SND_CHN_T_BR] = CHID_RR
  714 };
  715 
  716 #define SND_CHN_OSS_VALIDMASK                                           \
  717                         (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR |        \
  718                          SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF |        \
  719                          SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR |        \
  720                          SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
  721 
  722 #define SND_CHN_OSS_MAX         8
  723 #define SND_CHN_OSS_BEGIN       CHID_L
  724 #define SND_CHN_OSS_END         CHID_RR
  725 
  726 static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
  727         [CHID_L]   = SND_CHN_T_FL,
  728         [CHID_R]   = SND_CHN_T_FR,
  729         [CHID_C]   = SND_CHN_T_FC,
  730         [CHID_LFE] = SND_CHN_T_LF,
  731         [CHID_LS]  = SND_CHN_T_SL,
  732         [CHID_RS]  = SND_CHN_T_SR,
  733         [CHID_LR]  = SND_CHN_T_BL,
  734         [CHID_RR]  = SND_CHN_T_BR
  735 };
  736 
  737 /*
  738  * Used by SNDCTL_DSP_GET_CHNORDER.
  739  */
  740 int
  741 feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
  742     unsigned long long *map)
  743 {
  744         unsigned long long tmpmap;
  745         uint32_t i;
  746 
  747         if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
  748             m->channels > SND_CHN_OSS_MAX)
  749                 return (EINVAL);
  750 
  751         tmpmap = 0x0000000000000000ULL;
  752 
  753         for (i = 0; i < SND_CHN_OSS_MAX && m->map[i].type != SND_CHN_T_MAX;
  754             i++) {
  755                 if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
  756                         return (EINVAL);
  757                 tmpmap |=
  758                     (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
  759                     (i * 4);
  760         }
  761 
  762         *map = tmpmap;
  763 
  764         return (0);
  765 }
  766 
  767 /*
  768  * Used by SNDCTL_DSP_SET_CHNORDER.
  769  */
  770 int
  771 feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
  772     unsigned long long *map)
  773 {
  774         struct pcmchan_matrix tmp;
  775         uint32_t chmask, i;
  776         int ch, cheof;
  777 
  778         if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
  779             m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
  780                 return (EINVAL);
  781 
  782         tmp = *m;
  783         tmp.channels = 0;
  784         tmp.ext = 0;
  785         tmp.mask = 0;
  786         memset(tmp.offset, -1, sizeof(tmp.offset));
  787         cheof = 0;
  788 
  789         for (i = 0; i < SND_CHN_OSS_MAX; i++) {
  790                 ch = (*map >> (i * 4)) & 0xf;
  791                 if (ch < SND_CHN_OSS_BEGIN) {
  792                         if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
  793                                 return (EINVAL);
  794                         cheof++;
  795                         tmp.map[i] = m->map[i];
  796                         continue;
  797                 } else if (ch > SND_CHN_OSS_END)
  798                         return (EINVAL);
  799                 else if (cheof != 0)
  800                         return (EINVAL);
  801                 ch = oss_to_snd_chn[ch];
  802                 chmask = 1 << ch;
  803                 /* channel not exist in matrix */
  804                 if (!(chmask & m->mask))
  805                         return (EINVAL);
  806                 /* duplicated channel */
  807                 if (chmask & tmp.mask)
  808                         return (EINVAL);
  809                 tmp.map[i] = m->map[m->offset[ch]];
  810                 if (tmp.map[i].type != ch)
  811                         return (EINVAL);
  812                 tmp.offset[ch] = i;
  813                 tmp.mask |= chmask;
  814                 tmp.channels++;
  815                 if (chmask & SND_CHN_T_MASK_LF)
  816                         tmp.ext++;
  817         }
  818 
  819         if (tmp.channels != m->channels || tmp.ext != m->ext ||
  820             tmp.mask != m->mask ||
  821             tmp.map[m->channels].type != SND_CHN_T_MAX)
  822                 return (EINVAL);
  823 
  824         *m = tmp;
  825 
  826         return (0);
  827 }

Cache object: 0739671ccc491f0a9b7f5757f2d4241c


[ 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.