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