xref: /freebsd/sys/dev/sound/pcm/buffer.c (revision bfe691b2f75de2224c7ceb304ebcdef2b42d4179)
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@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 #include <dev/sound/pcm/sound.h>
28 
29 #include "feeder_if.h"
30 
31 SND_DECLARE_FILE("$FreeBSD$");
32 
33 struct snd_dbuf *
34 sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel)
35 {
36 	struct snd_dbuf *b;
37 
38 	b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO);
39 	snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc);
40 	b->dev = dev;
41 	b->channel = channel;
42 
43 	return b;
44 }
45 
46 void
47 sndbuf_destroy(struct snd_dbuf *b)
48 {
49 	sndbuf_free(b);
50 	free(b, M_DEVBUF);
51 }
52 
53 bus_addr_t
54 sndbuf_getbufaddr(struct snd_dbuf *buf)
55 {
56 	return (buf->buf_addr);
57 }
58 
59 static void
60 sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
61 {
62 	struct snd_dbuf *b = (struct snd_dbuf *)arg;
63 
64 	if (bootverbose) {
65 		device_printf(b->dev, "sndbuf_setmap %lx, %lx; ",
66 		    (u_long)segs[0].ds_addr, (u_long)segs[0].ds_len);
67 		printf("%p -> %lx\n", b->buf, (u_long)segs[0].ds_addr);
68 	}
69 	if (error == 0)
70 		b->buf_addr = segs[0].ds_addr;
71 	else
72 		b->buf_addr = 0;
73 }
74 
75 /*
76  * Allocate memory for DMA buffer. If the device does not use DMA transfers,
77  * the driver can call malloc(9) and sndbuf_setup() itself.
78  */
79 
80 int
81 sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size)
82 {
83 	int ret;
84 
85 	b->dmatag = dmatag;
86 	b->maxsize = size;
87 	b->bufsize = b->maxsize;
88 	b->buf_addr = 0;
89 	b->flags |= SNDBUF_F_MANAGED;
90 	if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT,
91 	    &b->dmamap)) {
92 		sndbuf_free(b);
93 		return (ENOMEM);
94 	}
95 	if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize,
96 	    sndbuf_setmap, b, 0) != 0 || b->buf_addr == 0) {
97 		sndbuf_free(b);
98 		return (ENOMEM);
99 	}
100 
101 	ret = sndbuf_resize(b, 2, b->maxsize / 2);
102 	if (ret != 0)
103 		sndbuf_free(b);
104 
105 	return (ret);
106 }
107 
108 int
109 sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size)
110 {
111 	b->flags &= ~SNDBUF_F_MANAGED;
112 	if (buf)
113 		b->flags |= SNDBUF_F_MANAGED;
114 	b->buf = buf;
115 	b->maxsize = size;
116 	b->bufsize = b->maxsize;
117 	return sndbuf_resize(b, 2, b->maxsize / 2);
118 }
119 
120 void
121 sndbuf_free(struct snd_dbuf *b)
122 {
123 	if (b->tmpbuf)
124 		free(b->tmpbuf, M_DEVBUF);
125 
126 	if (b->shadbuf)
127 		free(b->shadbuf, M_DEVBUF);
128 
129 	if (b->buf) {
130 		if (b->flags & SNDBUF_F_MANAGED) {
131 			if (b->dmamap)
132 				bus_dmamap_unload(b->dmatag, b->dmamap);
133 			if (b->dmatag)
134 				bus_dmamem_free(b->dmatag, b->buf, b->dmamap);
135 		} else
136 			free(b->buf, M_DEVBUF);
137 	}
138 
139 	b->tmpbuf = NULL;
140 	b->shadbuf = NULL;
141 	b->buf = NULL;
142 	b->sl = 0;
143 	b->dmatag = NULL;
144 	b->dmamap = NULL;
145 }
146 
147 int
148 sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
149 {
150 	u_int8_t *tmpbuf, *f2;
151 
152 	chn_lock(b->channel);
153 	if (b->maxsize == 0)
154 		goto out;
155 	if (blkcnt == 0)
156 		blkcnt = b->blkcnt;
157 	if (blksz == 0)
158 		blksz = b->blksz;
159 	if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) {
160 		chn_unlock(b->channel);
161 		return EINVAL;
162 	}
163 	if (blkcnt == b->blkcnt && blksz == b->blksz)
164 		goto out;
165 
166 	chn_unlock(b->channel);
167 	tmpbuf = malloc(blkcnt * blksz, M_DEVBUF, M_NOWAIT);
168 	if (tmpbuf == NULL)
169 		return ENOMEM;
170 	chn_lock(b->channel);
171 	b->blkcnt = blkcnt;
172 	b->blksz = blksz;
173 	b->bufsize = blkcnt * blksz;
174 	f2 =  b->tmpbuf;
175 	b->tmpbuf = tmpbuf;
176 	sndbuf_reset(b);
177 	chn_unlock(b->channel);
178 	free(f2, M_DEVBUF);
179 	return 0;
180 out:
181 	chn_unlock(b->channel);
182 	return 0;
183 }
184 
185 int
186 sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
187 {
188         u_int8_t *buf, *tmpbuf, *f1, *f2;
189 	u_int8_t *shadbuf, *f3;
190         unsigned int bufsize;
191 	int ret;
192 
193 	if (blkcnt < 2 || blksz < 16)
194 		return EINVAL;
195 
196 	bufsize = blksz * blkcnt;
197 
198 	chn_unlock(b->channel);
199 	buf = malloc(bufsize, M_DEVBUF, M_WAITOK);
200 	tmpbuf = malloc(bufsize, M_DEVBUF, M_WAITOK);
201 	shadbuf = malloc(bufsize, M_DEVBUF, M_WAITOK);
202 
203 	chn_lock(b->channel);
204 
205 	b->blkcnt = blkcnt;
206 	b->blksz = blksz;
207 	b->bufsize = bufsize;
208 	b->maxsize = bufsize;
209 	f1 = b->buf;
210 	f2 = b->tmpbuf;
211 	b->buf = buf;
212 	b->tmpbuf = tmpbuf;
213 	f3 = b->shadbuf;
214 	b->shadbuf = shadbuf;
215 	b->sl = bufsize;
216 
217 	sndbuf_reset(b);
218 
219 	chn_unlock(b->channel);
220       	if (f1)
221 		free(f1, M_DEVBUF);
222       	if (f2)
223 		free(f2, M_DEVBUF);
224 	if (f3)
225 		free(f3, M_DEVBUF);
226 
227 	ret = 0;
228 
229 	chn_lock(b->channel);
230 	return ret;
231 }
232 
233 /**
234  * @brief Zero out space in buffer free area
235  *
236  * This function clears a chunk of @c length bytes in the buffer free area
237  * (i.e., where the next write will be placed).
238  *
239  * @param b		buffer context
240  * @param length	number of bytes to blank
241  */
242 void
243 sndbuf_clear(struct snd_dbuf *b, unsigned int length)
244 {
245 	int i;
246 	u_char data, *p;
247 
248 	if (length == 0)
249 		return;
250 	if (length > b->bufsize)
251 		length = b->bufsize;
252 
253 	data = sndbuf_zerodata(b->fmt);
254 
255 	i = sndbuf_getfreeptr(b);
256 	p = sndbuf_getbuf(b);
257 	while (length > 0) {
258 		p[i] = data;
259 		length--;
260 		i++;
261 		if (i >= b->bufsize)
262 			i = 0;
263 	}
264 }
265 
266 /**
267  * @brief Zap buffer contents, resetting "ready area" fields
268  *
269  * @param b	buffer context
270  */
271 void
272 sndbuf_fillsilence(struct snd_dbuf *b)
273 {
274 	if (b->bufsize > 0)
275 		memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
276 	b->rp = 0;
277 	b->rl = b->bufsize;
278 }
279 
280 /**
281  * @brief Reset buffer w/o flushing statistics
282  *
283  * This function just zeroes out buffer contents and sets the "ready length"
284  * to zero.  This was originally to facilitate minimal playback interruption
285  * (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls.
286  *
287  * @param b	buffer context
288  */
289 void
290 sndbuf_softreset(struct snd_dbuf *b)
291 {
292 	b->rl = 0;
293 	if (b->buf && b->bufsize > 0)
294 		sndbuf_clear(b, b->bufsize);
295 }
296 
297 void
298 sndbuf_reset(struct snd_dbuf *b)
299 {
300 	b->hp = 0;
301 	b->rp = 0;
302 	b->rl = 0;
303 	b->dl = 0;
304 	b->prev_total = 0;
305 	b->total = 0;
306 	b->xrun = 0;
307 	if (b->buf && b->bufsize > 0)
308 		sndbuf_clear(b, b->bufsize);
309 	sndbuf_clearshadow(b);
310 }
311 
312 u_int32_t
313 sndbuf_getfmt(struct snd_dbuf *b)
314 {
315 	return b->fmt;
316 }
317 
318 int
319 sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
320 {
321 	b->fmt = fmt;
322 	b->bps = 1;
323 	b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
324 	if (b->fmt & AFMT_16BIT)
325 		b->bps <<= 1;
326 	else if (b->fmt & AFMT_24BIT)
327 		b->bps *= 3;
328 	else if (b->fmt & AFMT_32BIT)
329 		b->bps <<= 2;
330 	return 0;
331 }
332 
333 unsigned int
334 sndbuf_getspd(struct snd_dbuf *b)
335 {
336 	return b->spd;
337 }
338 
339 void
340 sndbuf_setspd(struct snd_dbuf *b, unsigned int spd)
341 {
342 	b->spd = spd;
343 }
344 
345 unsigned int
346 sndbuf_getalign(struct snd_dbuf *b)
347 {
348 	static int align[] = {0, 1, 1, 2, 2, 2, 2, 3};
349 
350 	return align[b->bps - 1];
351 }
352 
353 unsigned int
354 sndbuf_getblkcnt(struct snd_dbuf *b)
355 {
356 	return b->blkcnt;
357 }
358 
359 void
360 sndbuf_setblkcnt(struct snd_dbuf *b, unsigned int blkcnt)
361 {
362 	b->blkcnt = blkcnt;
363 }
364 
365 unsigned int
366 sndbuf_getblksz(struct snd_dbuf *b)
367 {
368 	return b->blksz;
369 }
370 
371 void
372 sndbuf_setblksz(struct snd_dbuf *b, unsigned int blksz)
373 {
374 	b->blksz = blksz;
375 }
376 
377 unsigned int
378 sndbuf_getbps(struct snd_dbuf *b)
379 {
380 	return b->bps;
381 }
382 
383 void *
384 sndbuf_getbuf(struct snd_dbuf *b)
385 {
386 	return b->buf;
387 }
388 
389 void *
390 sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs)
391 {
392 	KASSERT(ofs < b->bufsize, ("%s: ofs invalid %d", __func__, ofs));
393 
394 	return b->buf + ofs;
395 }
396 
397 unsigned int
398 sndbuf_getsize(struct snd_dbuf *b)
399 {
400 	return b->bufsize;
401 }
402 
403 unsigned int
404 sndbuf_getmaxsize(struct snd_dbuf *b)
405 {
406 	return b->maxsize;
407 }
408 
409 unsigned int
410 sndbuf_runsz(struct snd_dbuf *b)
411 {
412 	return b->dl;
413 }
414 
415 void
416 sndbuf_setrun(struct snd_dbuf *b, int go)
417 {
418 	b->dl = go? b->blksz : 0;
419 }
420 
421 struct selinfo *
422 sndbuf_getsel(struct snd_dbuf *b)
423 {
424 	return &b->sel;
425 }
426 
427 /************************************************************/
428 unsigned int
429 sndbuf_getxrun(struct snd_dbuf *b)
430 {
431 	SNDBUF_LOCKASSERT(b);
432 
433 	return b->xrun;
434 }
435 
436 void
437 sndbuf_setxrun(struct snd_dbuf *b, unsigned int xrun)
438 {
439 	SNDBUF_LOCKASSERT(b);
440 
441 	b->xrun = xrun;
442 }
443 
444 unsigned int
445 sndbuf_gethwptr(struct snd_dbuf *b)
446 {
447 	SNDBUF_LOCKASSERT(b);
448 
449 	return b->hp;
450 }
451 
452 void
453 sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr)
454 {
455 	SNDBUF_LOCKASSERT(b);
456 
457 	b->hp = ptr;
458 }
459 
460 unsigned int
461 sndbuf_getready(struct snd_dbuf *b)
462 {
463 	SNDBUF_LOCKASSERT(b);
464 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
465 
466 	return b->rl;
467 }
468 
469 unsigned int
470 sndbuf_getreadyptr(struct snd_dbuf *b)
471 {
472 	SNDBUF_LOCKASSERT(b);
473 	KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
474 
475 	return b->rp;
476 }
477 
478 unsigned int
479 sndbuf_getfree(struct snd_dbuf *b)
480 {
481 	SNDBUF_LOCKASSERT(b);
482 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
483 
484 	return b->bufsize - b->rl;
485 }
486 
487 unsigned int
488 sndbuf_getfreeptr(struct snd_dbuf *b)
489 {
490 	SNDBUF_LOCKASSERT(b);
491 	KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp));
492 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
493 
494 	return (b->rp + b->rl) % b->bufsize;
495 }
496 
497 unsigned int
498 sndbuf_getblocks(struct snd_dbuf *b)
499 {
500 	SNDBUF_LOCKASSERT(b);
501 
502 	return b->total / b->blksz;
503 }
504 
505 unsigned int
506 sndbuf_getprevblocks(struct snd_dbuf *b)
507 {
508 	SNDBUF_LOCKASSERT(b);
509 
510 	return b->prev_total / b->blksz;
511 }
512 
513 unsigned int
514 sndbuf_gettotal(struct snd_dbuf *b)
515 {
516 	SNDBUF_LOCKASSERT(b);
517 
518 	return b->total;
519 }
520 
521 void
522 sndbuf_updateprevtotal(struct snd_dbuf *b)
523 {
524 	SNDBUF_LOCKASSERT(b);
525 
526 	b->prev_total = b->total;
527 }
528 
529 unsigned int
530 snd_xbytes(unsigned int v, unsigned int from, unsigned int to)
531 {
532 	unsigned int w, x, y;
533 
534 	if (from == to)
535 		return v;
536 
537 	if (from == 0 || to == 0 || v == 0)
538 		return 0;
539 
540 	x = from;
541 	y = to;
542 	while (y != 0) {
543 		w = x % y;
544 		x = y;
545 		y = w;
546 	}
547 	from /= x;
548 	to /= x;
549 
550 	return (unsigned int)(((u_int64_t)v * to) / from);
551 }
552 
553 unsigned int
554 sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
555 {
556 	if (from == NULL || to == NULL || v == 0)
557 		return 0;
558 
559 	return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from),
560 	    sndbuf_getbps(to) * sndbuf_getspd(to));
561 }
562 
563 u_int8_t
564 sndbuf_zerodata(u_int32_t fmt)
565 {
566 	if (fmt & AFMT_SIGNED)
567 		return (0x00);
568 	else if (fmt & AFMT_MU_LAW)
569 		return (0x7f);
570 	else if (fmt & AFMT_A_LAW)
571 		return (0x55);
572 	return (0x80);
573 }
574 
575 /************************************************************/
576 
577 /**
578  * @brief Acquire buffer space to extend ready area
579  *
580  * This function extends the ready area length by @c count bytes, and may
581  * optionally copy samples from another location stored in @c from.  The
582  * counter @c snd_dbuf::total is also incremented by @c count bytes.
583  *
584  * @param b	audio buffer
585  * @param from	sample source (optional)
586  * @param count	number of bytes to acquire
587  *
588  * @retval 0	Unconditional
589  */
590 int
591 sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
592 {
593 	int l;
594 
595 	KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b)));
596 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
597 	b->total += count;
598 	if (from != NULL) {
599 		while (count > 0) {
600 			l = min(count, sndbuf_getsize(b) - sndbuf_getfreeptr(b));
601 			bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l);
602 			from += l;
603 			b->rl += l;
604 			count -= l;
605 		}
606 	} else
607 		b->rl += count;
608 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
609 
610 	return 0;
611 }
612 
613 /**
614  * @brief Dispose samples from channel buffer, increasing size of ready area
615  *
616  * This function discards samples from the supplied buffer by advancing the
617  * ready area start pointer and decrementing the ready area length.  If
618  * @c to is not NULL, then the discard samples will be copied to the location
619  * it points to.
620  *
621  * @param b	PCM channel sound buffer
622  * @param to	destination buffer (optional)
623  * @param count	number of bytes to discard
624  *
625  * @returns 0 unconditionally
626  */
627 int
628 sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
629 {
630 	int l;
631 
632 	KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __func__, count, sndbuf_getready(b)));
633 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl));
634 	if (to != NULL) {
635 		while (count > 0) {
636 			l = min(count, sndbuf_getsize(b) - sndbuf_getreadyptr(b));
637 			bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l);
638 			to += l;
639 			b->rl -= l;
640 			b->rp = (b->rp + l) % b->bufsize;
641 			count -= l;
642 		}
643 	} else {
644 		b->rl -= count;
645 		b->rp = (b->rp + count) % b->bufsize;
646 	}
647 	KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count));
648 
649 	return 0;
650 }
651 
652 /* count is number of bytes we want added to destination buffer */
653 int
654 sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
655 {
656 	unsigned int cnt;
657 
658 	KASSERT(count > 0, ("can't feed 0 bytes"));
659 
660 	if (sndbuf_getfree(to) < count)
661 		return EINVAL;
662 
663 	do {
664 		cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
665 		if (cnt)
666 			sndbuf_acquire(to, to->tmpbuf, cnt);
667 		/* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
668 		count -= cnt;
669 	} while (count && cnt);
670 
671 	return 0;
672 }
673 
674 /************************************************************/
675 
676 void
677 sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what)
678 {
679 	printf("%s: [", s);
680 	if (what & 0x01)
681 		printf(" bufsize: %d, maxsize: %d", b->bufsize, b->maxsize);
682 	if (what & 0x02)
683 		printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp);
684 	if (what & 0x04)
685 		printf(" total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun);
686    	if (what & 0x08)
687 		printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd);
688 	if (what & 0x10)
689 		printf(" blksz: %d, blkcnt: %d, flags: 0x%x", b->blksz, b->blkcnt, b->flags);
690 	printf(" ]\n");
691 }
692 
693 /************************************************************/
694 u_int32_t
695 sndbuf_getflags(struct snd_dbuf *b)
696 {
697 	return b->flags;
698 }
699 
700 void
701 sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on)
702 {
703 	b->flags &= ~flags;
704 	if (on)
705 		b->flags |= flags;
706 }
707 
708 /**
709  * @brief Clear the shadow buffer by filling with samples equal to zero.
710  *
711  * @param b buffer to clear
712  */
713 void
714 sndbuf_clearshadow(struct snd_dbuf *b)
715 {
716 	KASSERT(b != NULL, ("b is a null pointer"));
717 	KASSERT(b->sl >= 0, ("illegal shadow length"));
718 
719 	if ((b->shadbuf != NULL) && (b->sl > 0))
720 		memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl);
721 }
722 
723 #ifdef OSSV4_EXPERIMENT
724 /**
725  * @brief Return peak value from samples in buffer ready area.
726  *
727  * Peak ranges from 0-32767.  If channel is monaural, most significant 16
728  * bits will be zero.  For now, only expects to work with 1-2 channel
729  * buffers.
730  *
731  * @note  Currently only operates with linear PCM formats.
732  *
733  * @param b buffer to analyze
734  * @param lpeak pointer to store left peak value
735  * @param rpeak pointer to store right peak value
736  */
737 void
738 sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp)
739 {
740 	u_int32_t lpeak, rpeak;
741 
742 	lpeak = 0;
743 	rpeak = 0;
744 
745 	/**
746 	 * @todo fill this in later
747 	 */
748 }
749 #endif
750