2 * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
26 * feeder_volume, a long 'Lost Technology' rather than a new feature.
29 #include <dev/sound/pcm/sound.h>
30 #include "feeder_if.h"
32 SND_DECLARE_FILE("$FreeBSD$");
34 MALLOC_DEFINE(M_VOLUMEFEEDER, "volumefeed", "pcm volume feeder");
36 #define FVOL_TRACE(x...) /* device_printf(c->dev, x) */
37 #define FVOL_TEST(x, y...) /* if (x) FVOL_TRACE(y) */
39 #define FVOL_RESOLUTION 6 /* 6bit volume resolution */
40 #define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / 100)
41 #define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f)
42 #define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8)
43 #define FVOL_MAX (1 << FVOL_RESOLUTION)
44 #define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION)
46 struct feed_volume_info;
48 typedef uint32_t (*feed_volume_filter)(struct feed_volume_info *,
49 uint8_t *, int *, uint32_t);
51 struct feed_volume_info {
52 uint32_t bps, channels;
53 feed_volume_filter filter;
56 #define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
58 feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(struct feed_volume_info *info, \
59 uint8_t *b, int *vol, uint32_t count) \
71 j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \
72 j = FVOL_CALC((VOL_INTCAST)j, vol[(i / bps) & 1]); \
73 PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \
78 FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne)
79 FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le)
80 FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le)
81 FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le)
82 FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be)
83 FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be)
84 FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be)
86 FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne)
87 FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le)
88 FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le)
89 FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le)
90 FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be)
91 FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be)
92 FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be)
95 feed_volume_setup(struct pcm_feeder *f)
97 struct feed_volume_info *info = f->data;
99 uint32_t format; /* pcm / audio format */
100 uint32_t bps; /* bytes-per-sample, regardless of
102 feed_volume_filter filter;
104 { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne },
105 { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le },
106 { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le },
107 { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le },
108 { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be },
109 { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be },
110 { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be },
112 { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne },
113 { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le },
114 { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le },
115 { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le },
116 { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be },
117 { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be },
118 { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be },
123 for (i = 0; i < sizeof(voltbl) / sizeof(*voltbl); i++) {
124 if (voltbl[i].format == 0)
126 if ((f->desc->out & ~AFMT_STEREO) == voltbl[i].format) {
127 info->bps = voltbl[i].bps;
128 info->filter = voltbl[i].filter;
133 /* For now, this is mandatory! */
140 feed_volume_init(struct pcm_feeder *f)
142 struct feed_volume_info *info;
144 if (f->desc->in != f->desc->out)
148 if (!(f->desc->out & AFMT_STEREO))
151 info = malloc(sizeof(*info), M_VOLUMEFEEDER, M_NOWAIT | M_ZERO);
155 return feed_volume_setup(f);
159 feed_volume_free(struct pcm_feeder *f)
161 struct feed_volume_info *info = f->data;
164 free(info, M_VOLUMEFEEDER);
170 feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
171 uint32_t count, void *source)
173 struct feed_volume_info *info = f->data;
177 vol[0] = FVOL_LEFT(c->volume);
178 vol[1] = FVOL_RIGHT(c->volume);
180 if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX)
181 return FEEDER_FEED(f->source, c, b, count, source);
183 smpsz = info->bps * info->channels;
186 count -= count % smpsz;
187 k = FEEDER_FEED(f->source, c, b, count, source);
189 FVOL_TRACE("%s: Not enough data (Got: %u bytes)\n",
193 FVOL_TEST(k % smpsz, "%s: Bytes not %dbit (stereo) aligned.\n",
194 __func__, info->bps << 3);
196 return info->filter(info, b, vol, k);
199 static struct pcm_feederdesc feeder_volume_desc[] = {
200 {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
201 {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
202 {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
203 {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
204 {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
205 {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
206 {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
208 {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
209 {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
210 {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
211 {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
212 {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
213 {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
214 {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
217 static kobj_method_t feeder_volume_methods[] = {
218 KOBJMETHOD(feeder_init, feed_volume_init),
219 KOBJMETHOD(feeder_free, feed_volume_free),
220 KOBJMETHOD(feeder_feed, feed_volume),
223 FEEDER_DECLARE(feeder_volume, 2, NULL);