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_volume.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) 2005-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 /* feeder_volume, a long 'Lost Technology' rather than a new feature. */
   30 
   31 #ifdef _KERNEL
   32 #ifdef HAVE_KERNEL_OPTION_HEADERS
   33 #include "opt_snd.h"
   34 #endif
   35 #include <dev/sound/pcm/sound.h>
   36 #include <dev/sound/pcm/pcm.h>
   37 #include "feeder_if.h"
   38 
   39 #define SND_USE_FXDIV
   40 #include "snd_fxdiv_gen.h"
   41 
   42 SND_DECLARE_FILE("$FreeBSD: releng/12.0/sys/dev/sound/pcm/feeder_volume.c 326255 2017-11-27 14:52:40Z pfg $");
   43 #endif
   44 
   45 typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
   46 
   47 #define FEEDVOLUME_CALC8(s, v)  (SND_VOL_CALC_SAMPLE((intpcm_t)         \
   48                                  (s) << 8, v) >> 8)
   49 #define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v)
   50 #define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
   51 #define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
   52 
   53 #define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN)                           \
   54 static void                                                             \
   55 feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix,                  \
   56     uint32_t channels, uint8_t *dst, uint32_t count)                    \
   57 {                                                                       \
   58         intpcm##BIT##_t v;                                              \
   59         intpcm_t x;                                                     \
   60         uint32_t i;                                                     \
   61                                                                         \
   62         dst += count * PCM_##BIT##_BPS * channels;                      \
   63         do {                                                            \
   64                 i = channels;                                           \
   65                 do {                                                    \
   66                         dst -= PCM_##BIT##_BPS;                         \
   67                         i--;                                            \
   68                         x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst);       \
   69                         v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]);    \
   70                         x = PCM_CLAMP_##SIGN##BIT(v);                   \
   71                         _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x);      \
   72                 } while (i != 0);                                       \
   73         } while (--count != 0);                                         \
   74 }
   75 
   76 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
   77 FEEDVOLUME_DECLARE(S, 16, LE)
   78 FEEDVOLUME_DECLARE(S, 32, LE)
   79 #endif
   80 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
   81 FEEDVOLUME_DECLARE(S, 16, BE)
   82 FEEDVOLUME_DECLARE(S, 32, BE)
   83 #endif
   84 #ifdef SND_FEEDER_MULTIFORMAT
   85 FEEDVOLUME_DECLARE(S,  8, NE)
   86 FEEDVOLUME_DECLARE(S, 24, LE)
   87 FEEDVOLUME_DECLARE(S, 24, BE)
   88 FEEDVOLUME_DECLARE(U,  8, NE)
   89 FEEDVOLUME_DECLARE(U, 16, LE)
   90 FEEDVOLUME_DECLARE(U, 24, LE)
   91 FEEDVOLUME_DECLARE(U, 32, LE)
   92 FEEDVOLUME_DECLARE(U, 16, BE)
   93 FEEDVOLUME_DECLARE(U, 24, BE)
   94 FEEDVOLUME_DECLARE(U, 32, BE)
   95 #endif
   96 
   97 struct feed_volume_info {
   98         uint32_t bps, channels;
   99         feed_volume_t apply;
  100         int volume_class;
  101         int state;
  102         int matrix[SND_CHN_MAX];
  103 };
  104 
  105 #define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN)                             \
  106         {                                                               \
  107                 AFMT_##SIGN##BIT##_##ENDIAN,                            \
  108                 feed_volume_##SIGN##BIT##ENDIAN                         \
  109         }
  110 
  111 static const struct {
  112         uint32_t format;
  113         feed_volume_t apply;
  114 } feed_volume_info_tab[] = {
  115 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
  116         FEEDVOLUME_ENTRY(S, 16, LE),
  117         FEEDVOLUME_ENTRY(S, 32, LE),
  118 #endif
  119 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
  120         FEEDVOLUME_ENTRY(S, 16, BE),
  121         FEEDVOLUME_ENTRY(S, 32, BE),
  122 #endif
  123 #ifdef SND_FEEDER_MULTIFORMAT
  124         FEEDVOLUME_ENTRY(S,  8, NE),
  125         FEEDVOLUME_ENTRY(S, 24, LE),
  126         FEEDVOLUME_ENTRY(S, 24, BE),
  127         FEEDVOLUME_ENTRY(U,  8, NE),
  128         FEEDVOLUME_ENTRY(U, 16, LE),
  129         FEEDVOLUME_ENTRY(U, 24, LE),
  130         FEEDVOLUME_ENTRY(U, 32, LE),
  131         FEEDVOLUME_ENTRY(U, 16, BE),
  132         FEEDVOLUME_ENTRY(U, 24, BE),
  133         FEEDVOLUME_ENTRY(U, 32, BE)
  134 #endif
  135 };
  136 
  137 #define FEEDVOLUME_TAB_SIZE     ((int32_t)                              \
  138                                  (sizeof(feed_volume_info_tab) /        \
  139                                   sizeof(feed_volume_info_tab[0])))
  140 
  141 static int
  142 feed_volume_init(struct pcm_feeder *f)
  143 {
  144         struct feed_volume_info *info;
  145         struct pcmchan_matrix *m;
  146         uint32_t i;
  147         int ret;
  148 
  149         if (f->desc->in != f->desc->out ||
  150             AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX)
  151                 return (EINVAL);
  152 
  153         for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) {
  154                 if (AFMT_ENCODING(f->desc->in) ==
  155                     feed_volume_info_tab[i].format) {
  156                         info = malloc(sizeof(*info), M_DEVBUF,
  157                             M_NOWAIT | M_ZERO);
  158                         if (info == NULL)
  159                                 return (ENOMEM);
  160 
  161                         info->bps = AFMT_BPS(f->desc->in);
  162                         info->channels = AFMT_CHANNEL(f->desc->in);
  163                         info->apply = feed_volume_info_tab[i].apply;
  164                         info->volume_class = SND_VOL_C_PCM;
  165                         info->state = FEEDVOLUME_ENABLE;
  166 
  167                         f->data = info;
  168                         m = feeder_matrix_default_channel_map(info->channels);
  169                         if (m == NULL) {
  170                                 free(info, M_DEVBUF);
  171                                 return (EINVAL);
  172                         }
  173 
  174                         ret = feeder_volume_apply_matrix(f, m);
  175                         if (ret != 0)
  176                                 free(info, M_DEVBUF);
  177 
  178                         return (ret);
  179                 }
  180         }
  181 
  182         return (EINVAL);
  183 }
  184 
  185 static int
  186 feed_volume_free(struct pcm_feeder *f)
  187 {
  188         struct feed_volume_info *info;
  189 
  190         info = f->data;
  191         if (info != NULL)
  192                 free(info, M_DEVBUF);
  193 
  194         f->data = NULL;
  195 
  196         return (0);
  197 }
  198 
  199 static int
  200 feed_volume_set(struct pcm_feeder *f, int what, int value)
  201 {
  202         struct feed_volume_info *info;
  203         struct pcmchan_matrix *m;
  204         int ret;
  205 
  206         info = f->data;
  207         ret = 0;
  208 
  209         switch (what) {
  210         case FEEDVOLUME_CLASS:
  211                 if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END)
  212                         return (EINVAL);
  213                 info->volume_class = value;
  214                 break;
  215         case FEEDVOLUME_CHANNELS:
  216                 if (value < SND_CHN_MIN || value > SND_CHN_MAX)
  217                         return (EINVAL);
  218                 m = feeder_matrix_default_channel_map(value);
  219                 if (m == NULL)
  220                         return (EINVAL);
  221                 ret = feeder_volume_apply_matrix(f, m);
  222                 break;
  223         case FEEDVOLUME_STATE:
  224                 if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS))
  225                         return (EINVAL);
  226                 info->state = value;
  227                 break;
  228         default:
  229                 return (EINVAL);
  230                 break;
  231         }
  232 
  233         return (ret);
  234 }
  235 
  236 static int
  237 feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
  238     uint32_t count, void *source)
  239 {
  240         struct feed_volume_info *info;
  241         uint32_t j, align;
  242         int i, *vol, *matrix;
  243         uint8_t *dst;
  244 
  245         /*
  246          * Fetch filter data operation.
  247          */
  248         info = f->data;
  249 
  250         if (info->state == FEEDVOLUME_BYPASS)
  251                 return (FEEDER_FEED(f->source, c, b, count, source));
  252 
  253         vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
  254         matrix = info->matrix;
  255 
  256         /*
  257          * First, let see if we really need to apply gain at all.
  258          */
  259         j = 0;
  260         i = info->channels;
  261         do {
  262                 if (vol[matrix[--i]] != SND_VOL_FLAT) {
  263                         j = 1;
  264                         break;
  265                 }
  266         } while (i != 0);
  267 
  268         /* Nope, just bypass entirely. */
  269         if (j == 0)
  270                 return (FEEDER_FEED(f->source, c, b, count, source));
  271 
  272         dst = b;
  273         align = info->bps * info->channels;
  274 
  275         do {
  276                 if (count < align)
  277                         break;
  278 
  279                 j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
  280                     align);
  281                 if (j == 0)
  282                         break;
  283 
  284                 info->apply(vol, matrix, info->channels, dst, j);
  285 
  286                 j *= align;
  287                 dst += j;
  288                 count -= j;
  289 
  290         } while (count != 0);
  291 
  292         return (dst - b);
  293 }
  294 
  295 static struct pcm_feederdesc feeder_volume_desc[] = {
  296         { FEEDER_VOLUME, 0, 0, 0, 0 },
  297         { 0, 0, 0, 0, 0 }
  298 };
  299 
  300 static kobj_method_t feeder_volume_methods[] = {
  301         KOBJMETHOD(feeder_init,         feed_volume_init),
  302         KOBJMETHOD(feeder_free,         feed_volume_free),
  303         KOBJMETHOD(feeder_set,          feed_volume_set),
  304         KOBJMETHOD(feeder_feed,         feed_volume_feed),
  305         KOBJMETHOD_END
  306 };
  307 
  308 FEEDER_DECLARE(feeder_volume, NULL);
  309 
  310 /* Extern */
  311 
  312 /*
  313  * feeder_volume_apply_matrix(): For given matrix map, apply its configuration
  314  *                               to feeder_volume matrix structure. There are
  315  *                               possibilites that feeder_volume be inserted
  316  *                               before or after feeder_matrix, which in this
  317  *                               case feeder_volume must be in a good terms
  318  *                               with _current_ matrix.
  319  */
  320 int
  321 feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
  322 {
  323         struct feed_volume_info *info;
  324         uint32_t i;
  325 
  326         if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
  327             f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
  328             m->channels > SND_CHN_MAX)
  329                 return (EINVAL);
  330 
  331         info = f->data;
  332 
  333         for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
  334                 if (i < m->channels)
  335                         info->matrix[i] = m->map[i].type;
  336                 else
  337                         info->matrix[i] = SND_CHN_T_FL;
  338         }
  339 
  340         info->channels = m->channels;
  341 
  342         return (0);
  343 }

Cache object: 560801a81123b10d4c217f72d2861244


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