FreeBSD kernel sound device code
feeder_volume.c
Go to the documentation of this file.
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
42SND_DECLARE_FILE("$FreeBSD$");
43#endif
44
45typedef 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) \
54static void \
55feed_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)
77FEEDVOLUME_DECLARE(S, 16, LE)
78FEEDVOLUME_DECLARE(S, 32, LE)
79#endif
80#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
81FEEDVOLUME_DECLARE(S, 16, BE)
82FEEDVOLUME_DECLARE(S, 32, BE)
83#endif
84#ifdef SND_FEEDER_MULTIFORMAT
85FEEDVOLUME_DECLARE(S, 8, NE)
86FEEDVOLUME_DECLARE(S, 24, LE)
87FEEDVOLUME_DECLARE(S, 24, BE)
88FEEDVOLUME_DECLARE(U, 8, NE)
89FEEDVOLUME_DECLARE(U, 16, LE)
90FEEDVOLUME_DECLARE(U, 24, LE)
91FEEDVOLUME_DECLARE(U, 32, LE)
92FEEDVOLUME_DECLARE(U, 16, BE)
93FEEDVOLUME_DECLARE(U, 24, BE)
94FEEDVOLUME_DECLARE(U, 32, BE)
95#endif
96
98 uint32_t bps, channels;
101 int state;
103};
104
105#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \
106 { \
107 AFMT_##SIGN##BIT##_##ENDIAN, \
108 feed_volume_##SIGN##BIT##ENDIAN \
109 }
110
111static const struct {
112 uint32_t format;
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
136
137#define FEEDVOLUME_TAB_SIZE ((int32_t) \
138 (sizeof(feed_volume_info_tab) / \
139 sizeof(feed_volume_info_tab[0])))
140
141static int
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 ||
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;
165 info->state = FEEDVOLUME_ENABLE;
166
167 f->data = info;
169 if (m == NULL) {
170 free(info, M_DEVBUF);
171 return (EINVAL);
172 }
173
175 if (ret != 0)
176 free(info, M_DEVBUF);
177
178 return (ret);
179 }
180 }
181
182 return (EINVAL);
183}
184
185static int
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
199static int
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;
216 if (value < SND_CHN_MIN || value > SND_CHN_MAX)
217 return (EINVAL);
219 if (m == NULL)
220 return (EINVAL);
222 break;
223 case FEEDVOLUME_STATE:
225 return (EINVAL);
226 info->state = value;
227 break;
228 default:
229 return (EINVAL);
230 break;
231 }
232
233 return (ret);
234}
235
236static int
237feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
238 uint32_t count, void *source)
239{
240 int temp_vol[SND_CHN_T_VOL_MAX];
241 struct feed_volume_info *info;
242 uint32_t j, align;
243 int i, *matrix;
244 uint8_t *dst;
245 const int16_t *vol;
246 const int8_t *muted;
247
248 /*
249 * Fetch filter data operation.
250 */
251 info = f->data;
252
253 if (info->state == FEEDVOLUME_BYPASS)
254 return (FEEDER_FEED(f->source, c, b, count, source));
255
256 vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
257 muted = c->muted[SND_VOL_C_VAL(info->volume_class)];
258 matrix = info->matrix;
259
260 /*
261 * First, let see if we really need to apply gain at all.
262 */
263 j = 0;
264 i = info->channels;
265 while (i--) {
266 if (vol[matrix[i]] != SND_VOL_FLAT ||
267 muted[matrix[i]] != 0) {
268 j = 1;
269 break;
270 }
271 }
272
273 /* Nope, just bypass entirely. */
274 if (j == 0)
275 return (FEEDER_FEED(f->source, c, b, count, source));
276
277 /* Check if any controls are muted. */
278 for (j = 0; j != SND_CHN_T_VOL_MAX; j++)
279 temp_vol[j] = muted[j] ? 0 : vol[j];
280
281 dst = b;
282 align = info->bps * info->channels;
283
284 do {
285 if (count < align)
286 break;
287
288 j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
289 align);
290 if (j == 0)
291 break;
292
293 info->apply(temp_vol, matrix, info->channels, dst, j);
294
295 j *= align;
296 dst += j;
297 count -= j;
298
299 } while (count != 0);
300
301 return (dst - b);
302}
303
305 { FEEDER_VOLUME, 0, 0, 0, 0 },
306 { 0, 0, 0, 0, 0 }
307};
308
309static kobj_method_t feeder_volume_methods[] = {
310 KOBJMETHOD(feeder_init, feed_volume_init),
311 KOBJMETHOD(feeder_free, feed_volume_free),
312 KOBJMETHOD(feeder_set, feed_volume_set),
313 KOBJMETHOD(feeder_feed, feed_volume_feed),
315};
316
317FEEDER_DECLARE(feeder_volume, NULL);
318
319/* Extern */
320
321/*
322 * feeder_volume_apply_matrix(): For given matrix map, apply its configuration
323 * to feeder_volume matrix structure. There are
324 * possibilites that feeder_volume be inserted
325 * before or after feeder_matrix, which in this
326 * case feeder_volume must be in a good terms
327 * with _current_ matrix.
328 */
329int
331{
332 struct feed_volume_info *info;
333 uint32_t i;
334
335 if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
336 f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
338 return (EINVAL);
339
340 info = f->data;
341
342 for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
343 if (i < m->channels)
344 info->matrix[i] = m->map[i].type;
345 else
346 info->matrix[i] = SND_CHN_T_FL;
347 }
348
349 info->channels = m->channels;
350
351 return (0);
352}
struct pcm_channel * c
Definition: channel_if.m:106
METHOD int free
Definition: channel_if.m:110
struct pcmchan_matrix * m
Definition: channel_if.m:232
struct snd_dbuf * b
Definition: channel_if.m:105
@ FEEDER_VOLUME
Definition: feeder.h:87
struct pcmchan_matrix * feeder_matrix_default_channel_map(uint32_t)
@ FEEDVOLUME_ENABLE
Definition: feeder.h:145
@ FEEDVOLUME_CLASS
Definition: feeder.h:142
@ FEEDVOLUME_BYPASS
Definition: feeder.h:146
@ FEEDVOLUME_STATE
Definition: feeder.h:144
@ FEEDVOLUME_CHANNELS
Definition: feeder.h:143
u_int32_t count
Definition: feeder_if.m:86
void * source
Definition: feeder_if.m:87
int what
Definition: feeder_if.m:73
static struct pcm_feederdesc feeder_volume_desc[]
static kobj_method_t feeder_volume_methods[]
#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN)
Definition: feeder_volume.c:53
static int feed_volume_init(struct pcm_feeder *f)
#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN)
uint32_t format
SND_DECLARE_FILE("$FreeBSD$")
static int feed_volume_free(struct pcm_feeder *f)
static int feed_volume_set(struct pcm_feeder *f, int what, int value)
void(* feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t)
Definition: feeder_volume.c:45
static int feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source)
FEEDER_DECLARE(feeder_volume, NULL)
feed_volume_t apply
static const struct @48 feed_volume_info_tab[]
#define FEEDVOLUME_TAB_SIZE
int feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
uint32_t value
Definition: hdaa.c:58
#define SND_CHN_T_FL
Definition: matrix.h:42
#define SND_CHN_MIN
Definition: matrix.h:180
#define SND_VOL_C_VAL(x)
Definition: matrix.h:203
#define SND_CHN_MAX
Definition: matrix.h:183
#define SND_CHN_T_VOL_MAX
Definition: matrix.h:175
#define SND_VOL_C_END
Definition: matrix.h:200
#define SND_VOL_C_PCM
Definition: matrix.h:195
#define SND_VOL_FLAT
Definition: matrix.h:212
#define KOBJMETHOD_END
Definition: midi.c:76
#define AFMT_ENCODING(v)
Definition: sound.h:222
#define AFMT_BPS(v)
Definition: sound.h:235
#define AFMT_CHANNEL(v)
Definition: sound.h:227
int matrix[SND_CHN_MAX]
feed_volume_t apply
Definition: feeder_volume.c:99
Definition: hdaa.c:240
int8_t muted[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]
Definition: channel.h:170
int16_t volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]
Definition: channel.h:169
struct pcm_feeder * source
Definition: feeder.h:51
void * data
Definition: feeder.h:49
struct pcm_feederdesc * desc
Definition: feeder.h:48
u_int32_t out
Definition: feeder.h:34
u_int32_t in
Definition: feeder.h:34
u_int32_t type
Definition: feeder.h:33
struct pcmchan_matrix::@26 map[SND_CHN_T_MAX+1]
uint8_t channels
Definition: channel.h:41