1 /*- 2 * Copyright (c) 2005 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 /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ 28 29 #include <dev/sound/pcm/sound.h> 30 #include "feeder_if.h" 31 32 SND_DECLARE_FILE("$FreeBSD$"); 33 34 #define FVOL_OSS_SCALE 100 35 #define FVOL_RESOLUTION PCM_FXSHIFT 36 #define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / FVOL_OSS_SCALE) 37 #define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f) 38 #define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8) 39 #define FVOL_MAX (1 << FVOL_RESOLUTION) 40 #define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION) 41 42 typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t); 43 44 #define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ 45 static uint32_t \ 46 feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(uint8_t *b, int *vol, \ 47 uint32_t count) \ 48 { \ 49 int32_t j; \ 50 int i; \ 51 \ 52 i = count; \ 53 b += i; \ 54 \ 55 do { \ 56 b -= PCM_##FMTBIT##_BPS; \ 57 i -= PCM_##FMTBIT##_BPS; \ 58 j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \ 59 j = FVOL_CALC((VOL_INTCAST)j, \ 60 vol[(i / PCM_##FMTBIT##_BPS) & 1]); \ 61 PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \ 62 } while (i != 0); \ 63 \ 64 return (count); \ 65 } 66 67 FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne) 68 FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le) 69 FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le) 70 FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le) 71 FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be) 72 FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be) 73 FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be) 74 FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne) 75 FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le) 76 FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le) 77 FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le) 78 FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be) 79 FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be) 80 FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be) 81 82 struct feed_volume_info { 83 uint32_t format; 84 int bps; 85 feed_volume_filter filter; 86 }; 87 88 static struct feed_volume_info feed_volume_tbl[] = { 89 { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne }, 90 { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le }, 91 { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le }, 92 { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le }, 93 { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be }, 94 { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be }, 95 { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be }, 96 { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne }, 97 { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le }, 98 { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le }, 99 { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le }, 100 { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be }, 101 { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be }, 102 { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be }, 103 }; 104 105 #define FVOL_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) 106 #define FVOL_INFOIDX(m) (((m) >> 4) & 0x1f) 107 #define FVOL_CHANNELS(m) ((m) & 0xf) 108 109 static int 110 feed_volume_init(struct pcm_feeder *f) 111 { 112 int i, channels; 113 114 if (f->desc->in != f->desc->out) 115 return (EINVAL); 116 117 /* For now, this is mandatory! */ 118 if (!(f->desc->out & AFMT_STEREO)) 119 return (EINVAL); 120 121 channels = 2; 122 123 for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]); 124 i++) { 125 if ((f->desc->out & ~AFMT_STEREO) == 126 feed_volume_tbl[i].format) { 127 f->data = (void *)FVOL_DATA(i, channels); 128 return (0); 129 } 130 } 131 132 return (-1); 133 } 134 135 static int 136 feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 137 uint32_t count, void *source) 138 { 139 struct feed_volume_info *info; 140 int vol[2]; 141 int k, smpsz; 142 143 vol[0] = FVOL_LEFT(c->volume); 144 vol[1] = FVOL_RIGHT(c->volume); 145 146 if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) 147 return (FEEDER_FEED(f->source, c, b, count, source)); 148 149 info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)]; 150 smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data); 151 if (count < smpsz) 152 return (0); 153 154 k = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source); 155 if (k < smpsz) 156 return (0); 157 158 k -= k % smpsz; 159 return (info->filter(b, vol, k)); 160 } 161 162 static struct pcm_feederdesc feeder_volume_desc[] = { 163 {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, 164 {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 165 {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, 166 {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, 167 {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, 168 {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, 169 {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, 170 {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, 171 {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, 172 {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, 173 {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, 174 {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, 175 {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, 176 {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, 177 {0, 0, 0, 0}, 178 }; 179 static kobj_method_t feeder_volume_methods[] = { 180 KOBJMETHOD(feeder_init, feed_volume_init), 181 KOBJMETHOD(feeder_feed, feed_volume), 182 {0, 0} 183 }; 184 FEEDER_DECLARE(feeder_volume, 2, NULL); 185