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

Cache object: 873626f4bdb76c25f1b1164c7fe1d467


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