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

Cache object: 8473bc0fd444dabffc13b69a0bd60599


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