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_mixer.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  * 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 #ifdef _KERNEL
   30 #ifdef HAVE_KERNEL_OPTION_HEADERS
   31 #include "opt_snd.h"
   32 #endif
   33 #include <dev/sound/pcm/sound.h>
   34 #include <dev/sound/pcm/pcm.h>
   35 #include <dev/sound/pcm/vchan.h>
   36 #include "feeder_if.h"
   37 
   38 #define SND_USE_FXDIV
   39 #include "snd_fxdiv_gen.h"
   40 
   41 SND_DECLARE_FILE("$FreeBSD$");
   42 #endif
   43 
   44 #undef SND_FEEDER_MULTIFORMAT
   45 #define SND_FEEDER_MULTIFORMAT  1
   46 
   47 typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t);
   48 
   49 #define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN)                            \
   50 static void                                                             \
   51 feed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst,              \
   52     uint32_t count)                                                     \
   53 {                                                                       \
   54         intpcm##BIT##_t z;                                              \
   55         intpcm_t x, y;                                                  \
   56                                                                         \
   57         src += count;                                                   \
   58         dst += count;                                                   \
   59                                                                         \
   60         do {                                                            \
   61                 src -= PCM_##BIT##_BPS;                                 \
   62                 dst -= PCM_##BIT##_BPS;                                 \
   63                 count -= PCM_##BIT##_BPS;                               \
   64                 x = PCM_READ_##SIGN##BIT##_##ENDIAN(src);               \
   65                 y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst);               \
   66                 z = INTPCM##BIT##_T(x) + y;                             \
   67                 x = PCM_CLAMP_##SIGN##BIT(z);                           \
   68                 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x);              \
   69         } while (count != 0);                                           \
   70 }
   71 
   72 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
   73 FEEDMIXER_DECLARE(S, 16, LE)
   74 FEEDMIXER_DECLARE(S, 32, LE)
   75 #endif
   76 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
   77 FEEDMIXER_DECLARE(S, 16, BE)
   78 FEEDMIXER_DECLARE(S, 32, BE)
   79 #endif
   80 #ifdef SND_FEEDER_MULTIFORMAT
   81 FEEDMIXER_DECLARE(S,  8, NE)
   82 FEEDMIXER_DECLARE(S, 24, LE)
   83 FEEDMIXER_DECLARE(S, 24, BE)
   84 FEEDMIXER_DECLARE(U,  8, NE)
   85 FEEDMIXER_DECLARE(U, 16, LE)
   86 FEEDMIXER_DECLARE(U, 24, LE)
   87 FEEDMIXER_DECLARE(U, 32, LE)
   88 FEEDMIXER_DECLARE(U, 16, BE)
   89 FEEDMIXER_DECLARE(U, 24, BE)
   90 FEEDMIXER_DECLARE(U, 32, BE)
   91 #endif
   92 
   93 struct feed_mixer_info {
   94         uint32_t format;
   95         int bps;
   96         feed_mixer_t mix;
   97 };
   98 
   99 #define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN)                              \
  100         {                                                               \
  101                 AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS,           \
  102                 feed_mixer_##SIGN##BIT##ENDIAN                          \
  103         }
  104 
  105 static struct feed_mixer_info feed_mixer_info_tab[] = {
  106         FEEDMIXER_ENTRY(S,  8, NE),
  107 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
  108         FEEDMIXER_ENTRY(S, 16, LE),
  109         FEEDMIXER_ENTRY(S, 32, LE),
  110 #endif
  111 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
  112         FEEDMIXER_ENTRY(S, 16, BE),
  113         FEEDMIXER_ENTRY(S, 32, BE),
  114 #endif
  115 #ifdef SND_FEEDER_MULTIFORMAT
  116         FEEDMIXER_ENTRY(S, 24, LE),
  117         FEEDMIXER_ENTRY(S, 24, BE),
  118         FEEDMIXER_ENTRY(U,  8, NE),
  119         FEEDMIXER_ENTRY(U, 16, LE),
  120         FEEDMIXER_ENTRY(U, 24, LE),
  121         FEEDMIXER_ENTRY(U, 32, LE),
  122         FEEDMIXER_ENTRY(U, 16, BE),
  123         FEEDMIXER_ENTRY(U, 24, BE),
  124         FEEDMIXER_ENTRY(U, 32, BE),
  125 #endif
  126         {    AFMT_AC3, PCM_16_BPS, NULL },
  127         { AFMT_MU_LAW,  PCM_8_BPS, feed_mixer_U8NE },   /* dummy */
  128         {  AFMT_A_LAW,  PCM_8_BPS, feed_mixer_U8NE }    /* dummy */
  129 };
  130 
  131 #define FEEDMIXER_TAB_SIZE      ((int32_t)                              \
  132                                  (sizeof(feed_mixer_info_tab) /         \
  133                                   sizeof(feed_mixer_info_tab[0])))
  134 
  135 #define FEEDMIXER_DATA(i, c)    ((void *)                               \
  136                                  ((uintptr_t)((((i) & 0x1f) << 7) |     \
  137                                  ((c) & 0x7f))))
  138 #define FEEDMIXER_INFOIDX(d)    ((uint32_t)((uintptr_t)(d) >> 7) & 0x1f)
  139 #define FEEDMIXER_CHANNELS(d)   ((uint32_t)((uintptr_t)(d)) & 0x7f)
  140 
  141 static int
  142 feed_mixer_init(struct pcm_feeder *f)
  143 {
  144         int i;
  145 
  146         if (f->desc->in != f->desc->out)
  147                 return (EINVAL);
  148 
  149         for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) {
  150                 if (AFMT_ENCODING(f->desc->in) ==
  151                     feed_mixer_info_tab[i].format) {
  152                         f->data =
  153                             FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in));
  154                         return (0);
  155                 }
  156         }
  157 
  158         return (EINVAL);
  159 }
  160 
  161 static int
  162 feed_mixer_set(struct pcm_feeder *f, int what, int value)
  163 {
  164 
  165         switch (what) {
  166         case FEEDMIXER_CHANNELS:
  167                 if (value < SND_CHN_MIN || value > SND_CHN_MAX)
  168                         return (EINVAL);
  169                 f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value);
  170                 break;
  171         default:
  172                 return (EINVAL);
  173                 break;
  174         }
  175 
  176         return (0);
  177 }
  178 
  179 static __inline int
  180 feed_mixer_rec(struct pcm_channel *c)
  181 {
  182         struct pcm_channel *ch;
  183         struct snd_dbuf *b, *bs;
  184         uint32_t cnt, maxfeed;
  185         int rdy;
  186 
  187         /*
  188          * Reset ready and moving pointer. We're not using bufsoft
  189          * anywhere since its sole purpose is to become the primary
  190          * distributor for the recorded buffer and also as an interrupt
  191          * threshold progress indicator.
  192          */
  193         b = c->bufsoft;
  194         b->rp = 0;
  195         b->rl = 0;
  196         cnt = sndbuf_getsize(b);
  197         maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(b));
  198 
  199         do {
  200                 cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf,
  201                     min(cnt, maxfeed), c->bufhard);
  202                 if (cnt != 0) {
  203                         sndbuf_acquire(b, b->tmpbuf, cnt);
  204                         cnt = sndbuf_getfree(b);
  205                 }
  206         } while (cnt != 0);
  207 
  208         /* Not enough data */
  209         if (b->rl < sndbuf_getalign(b)) {
  210                 b->rl = 0;
  211                 return (0);
  212         }
  213 
  214         /*
  215          * Keep track of ready and moving pointer since we will use
  216          * bufsoft over and over again, pretending nothing has happened.
  217          */
  218         rdy = b->rl;
  219 
  220         CHN_FOREACH(ch, c, children.busy) {
  221                 CHN_LOCK(ch);
  222                 if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) {
  223                         CHN_UNLOCK(ch);
  224                         continue;
  225                 }
  226 #ifdef SND_DEBUG
  227                 if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) {
  228                         if (vchan_sync(ch) != 0) {
  229                                 CHN_UNLOCK(ch);
  230                                 continue;
  231                         }
  232                 }
  233 #endif
  234                 bs = ch->bufsoft;
  235                 if (ch->flags & CHN_F_MMAP)
  236                         sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
  237                 cnt = sndbuf_getfree(bs);
  238                 if (cnt < sndbuf_getalign(bs)) {
  239                         CHN_UNLOCK(ch);
  240                         continue;
  241                 }
  242                 maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(bs));
  243                 do {
  244                         cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf,
  245                             min(cnt, maxfeed), b);
  246                         if (cnt != 0) {
  247                                 sndbuf_acquire(bs, bs->tmpbuf, cnt);
  248                                 cnt = sndbuf_getfree(bs);
  249                         }
  250                 } while (cnt != 0);
  251                 /*
  252                  * Not entirely flushed out...
  253                  */
  254                 if (b->rl != 0)
  255                         ch->xruns++;
  256                 CHN_UNLOCK(ch);
  257                 /*
  258                  * Rewind buffer position for next virtual channel.
  259                  */
  260                 b->rp = 0;
  261                 b->rl = rdy;
  262         }
  263 
  264         /*
  265          * Set ready pointer to indicate that our children are ready
  266          * to be woken up, also as an interrupt threshold progress
  267          * indicator.
  268          */
  269         b->rl = 1;
  270 
  271         c->flags &= ~CHN_F_DIRTY;
  272 
  273         /*
  274          * Return 0 to bail out early from sndbuf_feed() loop.
  275          * No need to increase feedcount counter since part of this
  276          * feeder chains already include feed_root().
  277          */
  278         return (0);
  279 }
  280 
  281 static int
  282 feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
  283     uint32_t count, void *source)
  284 {
  285         struct feed_mixer_info *info;
  286         struct snd_dbuf *src = source;
  287         struct pcm_channel *ch;
  288         uint32_t cnt, mcnt, rcnt, sz;
  289         int passthrough;
  290         uint8_t *tmp;
  291 
  292         if (c->direction == PCMDIR_REC)
  293                 return (feed_mixer_rec(c));
  294 
  295         sz = sndbuf_getsize(src);
  296         if (sz < count)
  297                 count = sz;
  298 
  299         info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)];
  300         sz = info->bps * FEEDMIXER_CHANNELS(f->data);
  301         count = SND_FXROUND(count, sz);
  302         if (count < sz)
  303                 return (0);
  304 
  305         /*
  306          * We are going to use our source as a temporary buffer since it's
  307          * got no other purpose.  We obtain our data by traversing the channel
  308          * list of children and calling mixer function to mix count bytes from
  309          * each into our destination buffer, b.
  310          */
  311         tmp = sndbuf_getbuf(src);
  312         rcnt = 0;
  313         mcnt = 0;
  314         passthrough = 0;        /* 'passthrough' / 'exclusive' marker */
  315 
  316         CHN_FOREACH(ch, c, children.busy) {
  317                 CHN_LOCK(ch);
  318                 if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) {
  319                         CHN_UNLOCK(ch);
  320                         continue;
  321                 }
  322 #ifdef SND_DEBUG
  323                 if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) {
  324                         if (vchan_sync(ch) != 0) {
  325                                 CHN_UNLOCK(ch);
  326                                 continue;
  327                         }
  328                 }
  329 #endif
  330                 if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING))
  331                         sndbuf_acquire(ch->bufsoft, NULL,
  332                             sndbuf_getfree(ch->bufsoft));
  333                 if (info->mix == NULL) {
  334                         /*
  335                          * Passthrough. Dump the first digital/passthrough
  336                          * channel into destination buffer, and the rest into
  337                          * nothingness (mute effect).
  338                          */
  339                         if (passthrough == 0 &&
  340                             (ch->format & AFMT_PASSTHROUGH)) {
  341                                 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
  342                                     b, count, ch->bufsoft), sz);
  343                                 passthrough = 1;
  344                         } else
  345                                 FEEDER_FEED(ch->feeder, ch, tmp, count,
  346                                     ch->bufsoft);
  347                 } else if (c->flags & CHN_F_EXCLUSIVE) {
  348                         /*
  349                          * Exclusive. Dump the first 'exclusive' channel into
  350                          * destination buffer, and the rest into nothingness
  351                          * (mute effect).
  352                          */
  353                         if (passthrough == 0 && (ch->flags & CHN_F_EXCLUSIVE)) {
  354                                 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
  355                                     b, count, ch->bufsoft), sz);
  356                                 passthrough = 1;
  357                         } else
  358                                 FEEDER_FEED(ch->feeder, ch, tmp, count,
  359                                     ch->bufsoft);
  360                 } else {
  361                         if (rcnt == 0) {
  362                                 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
  363                                     b, count, ch->bufsoft), sz);
  364                                 mcnt = count - rcnt;
  365                         } else {
  366                                 cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
  367                                     tmp, count, ch->bufsoft), sz);
  368                                 if (cnt != 0) {
  369                                         if (mcnt != 0) {
  370                                                 memset(b + rcnt,
  371                                                     sndbuf_zerodata(
  372                                                     f->desc->out), mcnt);
  373                                                 mcnt = 0;
  374                                         }
  375                                         info->mix(tmp, b, cnt);
  376                                         if (cnt > rcnt)
  377                                                 rcnt = cnt;
  378                                 }
  379                         }
  380                 }
  381                 CHN_UNLOCK(ch);
  382         }
  383 
  384         if (++c->feedcount == 0)
  385                 c->feedcount = 2;
  386 
  387         c->flags &= ~CHN_F_DIRTY;
  388 
  389         return (rcnt);
  390 }
  391 
  392 static struct pcm_feederdesc feeder_mixer_desc[] = {
  393         { FEEDER_MIXER, 0, 0, 0, 0 },
  394         { 0, 0, 0, 0, 0 }
  395 };
  396 
  397 static kobj_method_t feeder_mixer_methods[] = {
  398         KOBJMETHOD(feeder_init,         feed_mixer_init),
  399         KOBJMETHOD(feeder_set,          feed_mixer_set),
  400         KOBJMETHOD(feeder_feed,         feed_mixer_feed),
  401         KOBJMETHOD_END
  402 };
  403 
  404 FEEDER_DECLARE(feeder_mixer, NULL);

Cache object: 0d7957476e9d632f9fce5df04edb9843


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