xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_output.c (revision eb633035c80613ec93d62f90482837adaaf21a0a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (C) 4Front Technologies 1996-2008.
23  *
24  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
25  */
26 
27 /*
28  * Purpose: Virtual mixing audio output routines
29  *
30  * This file contains the actual mixing and resampling engine for output.
31  */
32 
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/sysmacros.h>
36 #include "audio_impl.h"
37 
38 #define	DECL_AUDIO_EXPORT(NAME, TYPE, SAMPLE)				\
39 void									\
40 auimpl_export_##NAME(audio_engine_t *eng, uint_t nfr, uint_t froff)	\
41 {									\
42 	int		nch = eng->e_nchan;				\
43 	uint_t		hidx = eng->e_hidx;				\
44 	TYPE		*out = (void *)eng->e_data;			\
45 	int		ch = 0;						\
46 									\
47 	do {	/* for each channel */					\
48 		int32_t *ip;						\
49 		TYPE	*op;						\
50 		int	i;						\
51 		int	incr = eng->e_chincr[ch];			\
52 									\
53 		/* get value and adjust next channel offset */		\
54 		op = out + eng->e_choffs[ch] + (hidx * incr);		\
55 		ip = eng->e_chbufs[ch];					\
56 		ip += froff;						\
57 									\
58 		i = nfr;						\
59 									\
60 		do {	/* for each frame */				\
61 			int32_t sample = *ip;				\
62 									\
63 			*op = SAMPLE;					\
64 			op += incr;					\
65 			ip++;						\
66 									\
67 		} while (--i);						\
68 									\
69 		ch++;							\
70 	} while (ch < nch);						\
71 }
72 
73 DECL_AUDIO_EXPORT(16ne, int16_t, sample >> 8)
74 DECL_AUDIO_EXPORT(16oe, int16_t, ddi_swap16(sample >> 8))
75 DECL_AUDIO_EXPORT(32ne, int32_t, sample << 8)
76 DECL_AUDIO_EXPORT(32oe, int32_t, ddi_swap32(sample << 8))
77 DECL_AUDIO_EXPORT(24ne, int32_t, sample)
78 DECL_AUDIO_EXPORT(24oe, int32_t, ddi_swap32(sample))
79 
80 /*
81  * Simple limiter to prevent overflows when using fixed point computations
82  */
83 static void
84 auimpl_output_limiter(audio_engine_t *eng)
85 {
86 	int k, t;
87 	uint_t q, amp, amp2;
88 	int nchan = eng->e_nchan;
89 	uint_t fragfr = eng->e_fragfr;
90 	int32_t **chbufs = eng->e_chbufs;
91 	uint_t statevar = eng->e_limiter_state;
92 
93 	for (t = 0; t < fragfr; t++) {
94 
95 		amp = (uint_t)ABS(chbufs[0][t]);
96 
97 		for (k = 1; k < nchan; k++)	{
98 			amp2 = (uint_t)ABS(chbufs[k][t]);
99 			if (amp2 > amp)
100 				amp = amp2;
101 		}
102 
103 		amp >>= 8;
104 		q = 0x10000;
105 
106 		if (amp > 0x7FFF)
107 			q = 0x7FFF0000 / amp;
108 
109 		if (statevar > q) {
110 			statevar = q;
111 		} else {
112 			q = statevar;
113 
114 			/*
115 			 * Simplier (linear) tracking algo
116 			 * (gives less distortion, but more pumping)
117 			 */
118 			statevar += 2;
119 			if (statevar > 0x10000)
120 				statevar = 0x10000;
121 
122 			/*
123 			 * Classic tracking algo
124 			 * gives more distortion with no-lookahead
125 			 * statevar=0x10000-((0x10000-statevar)*0xFFF4>>16);
126 			 */
127 		}
128 
129 		for (k = 0; k < nchan; k++) {
130 			int32_t in = chbufs[k][t];
131 			int32_t out = 0;
132 			uint_t p;
133 
134 			if (in >= 0) {
135 				p = in;
136 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
137 				    (p >> 16) * q;
138 				out = p;
139 			} else {
140 				p = -in;
141 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
142 				    (p >> 16) * q;
143 				out = -p;
144 			}
145 			/* safety code */
146 			/*
147 			 * if output after limiter is clamped, then it
148 			 * can be dropped
149 			 */
150 			if (out > 0x7FFFFF)
151 				out = 0x7FFFFF;
152 			else if (out < -0x7FFFFF)
153 				out = -0x7FFFFF;
154 
155 			chbufs[k][t] = out;
156 		}
157 	}
158 
159 	eng->e_limiter_state = statevar;
160 }
161 
162 /*
163  * Output mixing function.  Assumption: all work is done in 24-bit native PCM.
164  */
165 static void
166 auimpl_output_mix(audio_stream_t *sp, int offset, int nfr)
167 {
168 	audio_engine_t *eng = sp->s_engine;
169 	const int32_t *src;
170 	int choffs;
171 	int nch;
172 	int vol;
173 
174 	/*
175 	 * Initial setup.
176 	 */
177 
178 	src = sp->s_cnv_ptr;
179 	choffs = sp->s_choffs;
180 	nch = sp->s_cnv_dst_nchan;
181 	vol = sp->s_gain_eff;
182 
183 	/*
184 	 * Do the mixing.  We de-interleave the source stream at the
185 	 * same time.
186 	 */
187 	for (int ch = 0; ch < nch; ch++) {
188 		int32_t *op;
189 		const int32_t *ip;
190 
191 
192 		ip = src + ch;
193 		op = eng->e_chbufs[ch + choffs];
194 		op += offset;
195 
196 		for (int i = nfr; i; i--) {
197 
198 			int64_t	samp;
199 
200 			samp = *ip;
201 			samp *= vol;
202 			samp /= AUDIO_VOL_SCALE;
203 
204 			ip += nch;
205 			*op += (int32_t)samp;
206 			op++;
207 		}
208 	}
209 
210 	sp->s_cnv_cnt -= nfr;
211 	sp->s_cnv_ptr += (nch * nfr);
212 }
213 
214 /*
215  * Consume a fragment's worth of data.  This is called when the data in
216  * the conversion buffer is exhausted, and we need to refill it from the
217  * source buffer.  We always consume data from the client in quantities of
218  * a fragment at a time (assuming that a fragment is available.)
219  */
220 static void
221 auimpl_consume_fragment(audio_stream_t *sp)
222 {
223 	uint_t	count;
224 	uint_t	avail;
225 	uint_t	nframes;
226 	uint_t	fragfr;
227 	uint_t	framesz;
228 	caddr_t	cnvbuf;
229 
230 	sp->s_cnv_src = sp->s_cnv_buf0;
231 	sp->s_cnv_dst = sp->s_cnv_buf1;
232 
233 	fragfr = sp->s_fragfr;
234 	nframes = sp->s_nframes;
235 	framesz = sp->s_framesz;
236 
237 	ASSERT(sp->s_head >= sp->s_tail);
238 
239 	avail = sp->s_head - sp->s_tail;
240 	cnvbuf = sp->s_cnv_src;
241 
242 	count = min(avail, fragfr);
243 
244 	/*
245 	 * Copy data.  We deal properly with wraps.  Done as a
246 	 * do...while to minimize the number of tests.
247 	 */
248 	do {
249 		uint_t n;
250 		uint_t nbytes;
251 
252 		n = min(nframes - sp->s_tidx, count);
253 		nbytes = framesz * n;
254 		bcopy(sp->s_data + (sp->s_tidx * framesz), cnvbuf, nbytes);
255 		cnvbuf += nbytes;
256 		count -= n;
257 		sp->s_samples += n;
258 		sp->s_tail += n;
259 		sp->s_tidx += n;
260 		if (sp->s_tidx >= nframes)
261 			sp->s_tidx -= nframes;
262 	} while (count);
263 
264 	/* Note: data conversion is optional! */
265 	count = min(avail, fragfr);
266 	if (sp->s_converter != NULL) {
267 		sp->s_cnv_cnt = sp->s_converter(sp, count);
268 	} else {
269 		sp->s_cnv_cnt = count;
270 	}
271 }
272 
273 static void
274 auimpl_output_callback_impl(audio_engine_t *eng, audio_client_t **output,
275     audio_client_t **drain)
276 {
277 	uint_t	fragfr = eng->e_fragfr;
278 	uint_t	resid;
279 
280 	/* clear any preexisting mix results */
281 	for (int i = 0; i < eng->e_nchan; i++)
282 		bzero(eng->e_chbufs[i], AUDIO_CHBUFS * sizeof (int32_t));
283 
284 	for (audio_stream_t *sp = list_head(&eng->e_streams);
285 	    sp != NULL;
286 	    sp = list_next(&eng->e_streams, sp)) {
287 
288 		int		need;
289 		int		avail;
290 		int		used;
291 		int		offset;
292 		boolean_t	drained = B_FALSE;
293 		audio_client_t	*c = sp->s_client;
294 
295 		/*
296 		 * We need/want a full fragment.  If the client has
297 		 * less than that available, it will cause a client
298 		 * underrun in auimpl_consume_fragment, but in such a
299 		 * case we should get silence bytes.  Assignments done
300 		 * ahead of the lock to minimize lock contention.
301 		 */
302 		need = fragfr;
303 		offset = 0;
304 
305 		mutex_enter(&sp->s_lock);
306 		/* skip over streams not running or paused */
307 		if ((!sp->s_running) || (sp->s_paused)) {
308 			mutex_exit(&sp->s_lock);
309 			continue;
310 		}
311 
312 		do {
313 			/* make sure we have data to chew on */
314 			if ((avail = sp->s_cnv_cnt) == 0) {
315 				auimpl_consume_fragment(sp);
316 				sp->s_cnv_ptr = sp->s_cnv_src;
317 				avail = sp->s_cnv_cnt;
318 			}
319 
320 			/*
321 			 * We might have got more data than we need
322 			 * right now.  (E.g. 8kHz expanding to 48kHz.)
323 			 * Take only what we need.
324 			 */
325 			used = min(avail, need);
326 
327 			/*
328 			 * Mix the results, as much data as we can use
329 			 * this round.
330 			 */
331 			auimpl_output_mix(sp, offset, used);
332 
333 			/*
334 			 * Save the offset for the next round, so we don't
335 			 * remix into the same location.
336 			 */
337 			offset += used;
338 
339 			/*
340 			 * Okay, we mixed some data, but it might not
341 			 * have been all we need.  This can happen
342 			 * either because we just mixed up some
343 			 * partial/residual data, or because the
344 			 * client has a fragment size which expands to
345 			 * less than a full fragment for us. (Such as
346 			 * a client wanting to operate at a higher
347 			 * data rate than the engine.)
348 			 */
349 			need -= used;
350 
351 		} while (need && avail);
352 
353 		if (avail == 0) {
354 			/* underrun or end of data */
355 			if (sp->s_draining) {
356 				if (sp->s_drain_idx == 0) {
357 					sp->s_drain_idx = eng->e_head;
358 				}
359 				if (eng->e_tail >= sp->s_drain_idx) {
360 					sp->s_drain_idx = 0;
361 					sp->s_draining = B_FALSE;
362 					/*
363 					 * After draining, stop the
364 					 * stream cleanly.  This
365 					 * prevents underrun errors.
366 					 *
367 					 * (Stream will auto-start if
368 					 * client submits more data to
369 					 * it.)
370 					 *
371 					 * AC3: When an AC3 stream
372 					 * drains we should probably
373 					 * stop the actual hardware
374 					 * engine.
375 					 */
376 					ASSERT(mutex_owned(&eng->e_lock));
377 					sp->s_running = B_FALSE;
378 					drained = B_TRUE;
379 				}
380 			} else {
381 				sp->s_errors += need;
382 				eng->e_stream_underruns++;
383 			}
384 		}
385 
386 		/* wake threads waiting for stream (blocking writes, etc.) */
387 		cv_broadcast(&sp->s_cv);
388 
389 		mutex_exit(&sp->s_lock);
390 
391 
392 		/*
393 		 * Asynchronously notify clients.  We do as much as
394 		 * possible of this outside of the lock, it avoids
395 		 * s_lock and c_lock contention and eliminates any
396 		 * chance of deadlock.
397 		 */
398 
399 		/*
400 		 * NB: The only lock we are holding now is the engine
401 		 * lock.  But the client can't go away because the
402 		 * closer would have to get the engine lock to remove
403 		 * the client's stream from engine.  So we're safe.
404 		 */
405 
406 		if (output && (c->c_output != NULL) &&
407 		    (c->c_next_output == NULL)) {
408 			auclnt_hold(c);
409 			c->c_next_output = *output;
410 			*output = c;
411 		}
412 
413 		if (drain && drained && (c->c_drain != NULL) &&
414 		    (c->c_next_drain == NULL)) {
415 			auclnt_hold(c);
416 			c->c_next_drain = *drain;
417 			*drain = c;
418 		}
419 	}
420 
421 	/*
422 	 * Deal with 24-bit overflows (from mixing) gracefully.
423 	 */
424 	auimpl_output_limiter(eng);
425 
426 	/*
427 	 * Export the data (a whole fragment) to the device.  Deal
428 	 * properly with wraps.  Note that the test and subtraction is
429 	 * faster for dealing with wrap than modulo.
430 	 */
431 	resid = fragfr;
432 	do {
433 		uint_t part = min(resid, eng->e_nframes - eng->e_hidx);
434 		eng->e_export(eng, part, fragfr - resid);
435 		eng->e_head += part;
436 		eng->e_hidx += part;
437 		if (eng->e_hidx == eng->e_nframes)
438 			eng->e_hidx = 0;
439 		resid -= part;
440 	} while (resid);
441 
442 	/*
443 	 * Consider doing the SYNC outside of the lock.
444 	 */
445 	ENG_SYNC(eng, fragfr);
446 }
447 
448 /*
449  * Outer loop attempts to keep playing until we hit maximum playahead.
450  */
451 
452 void
453 auimpl_output_callback(void *arg)
454 {
455 	audio_engine_t	*e = arg;
456 	int64_t		cnt;
457 	audio_client_t	*c;
458 	audio_client_t	*output = NULL;
459 	audio_client_t	*drain = NULL;
460 	uint64_t	t;
461 
462 	mutex_enter(&e->e_lock);
463 
464 	if (e->e_suspended || e->e_failed || !e->e_periodic) {
465 		mutex_exit(&e->e_lock);
466 		return;
467 	}
468 
469 	if (e->e_need_start) {
470 		int rv;
471 		if ((rv = ENG_START(e)) != 0) {
472 			e->e_failed = B_TRUE;
473 			mutex_exit(&e->e_lock);
474 			audio_dev_warn(e->e_dev,
475 			    "failed starting output, rv = %d", rv);
476 			return;
477 		}
478 		e->e_need_start = B_FALSE;
479 	}
480 
481 	t = ENG_COUNT(e);
482 	if (t < e->e_tail) {
483 		/*
484 		 * This is a sign of a serious bug.  We should
485 		 * probably offline the device via FMA, if we ever
486 		 * support FMA for audio devices.
487 		 */
488 		e->e_failed = B_TRUE;
489 		ENG_STOP(e);
490 		mutex_exit(&e->e_lock);
491 		audio_dev_warn(e->e_dev,
492 		    "device malfunction: broken play back sample counter");
493 		return;
494 
495 	}
496 	e->e_tail = t;
497 
498 	if (e->e_tail > e->e_head) {
499 		/* want more than we have */
500 		e->e_errors++;
501 		e->e_underruns++;
502 	}
503 
504 	cnt = e->e_head - e->e_tail;
505 
506 	/* stay a bit ahead */
507 	while (cnt < e->e_playahead) {
508 		auimpl_output_callback_impl(e, &output, &drain);
509 		cnt = e->e_head - e->e_tail;
510 	}
511 	mutex_exit(&e->e_lock);
512 
513 	/*
514 	 * Notify client personalities.
515 	 */
516 	while ((c = output) != NULL) {
517 
518 		output = c->c_next_output;
519 		c->c_next_output = NULL;
520 		c->c_output(c);
521 		auclnt_release(c);
522 	}
523 
524 	while ((c = drain) != NULL) {
525 
526 		drain = c->c_next_drain;
527 		c->c_next_drain = NULL;
528 		c->c_drain(c);
529 		auclnt_release(c);
530 	}
531 
532 }
533 
534 void
535 auimpl_output_preload(audio_engine_t *e)
536 {
537 	int64_t	cnt;
538 
539 	ASSERT(mutex_owned(&e->e_lock));
540 
541 	if (e->e_tail > e->e_head) {
542 		/* want more than we have */
543 		e->e_errors++;
544 		e->e_underruns++;
545 		e->e_tail = e->e_head;
546 	}
547 	cnt = e->e_head - e->e_tail;
548 
549 	/* stay a bit ahead */
550 	while (cnt < e->e_playahead) {
551 		auimpl_output_callback_impl(e, NULL, NULL);
552 		cnt = e->e_head - e->e_tail;
553 	}
554 }
555