188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
24*2c30fa45SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2588447a05SGarrett D'Amore  */
2688447a05SGarrett D'Amore 
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * Purpose: Virtual mixing audio output routines
2988447a05SGarrett D'Amore  *
3088447a05SGarrett D'Amore  * This file contains the actual mixing and resampling engine for output.
3188447a05SGarrett D'Amore  */
3288447a05SGarrett D'Amore 
3388447a05SGarrett D'Amore #include <sys/ddi.h>
3488447a05SGarrett D'Amore #include <sys/sunddi.h>
3588447a05SGarrett D'Amore #include <sys/sysmacros.h>
3688447a05SGarrett D'Amore #include "audio_impl.h"
3788447a05SGarrett D'Amore 
3888447a05SGarrett D'Amore #define	DECL_AUDIO_EXPORT(NAME, TYPE, SAMPLE)				\
3988447a05SGarrett D'Amore void									\
4068c47f65SGarrett D'Amore auimpl_export_##NAME(audio_engine_t *eng, uint_t nfr, uint_t froff)	\
4188447a05SGarrett D'Amore {									\
4288447a05SGarrett D'Amore 	int		nch = eng->e_nchan;				\
4368c47f65SGarrett D'Amore 	uint_t		hidx = eng->e_hidx;				\
4488447a05SGarrett D'Amore 	TYPE		*out = (void *)eng->e_data;			\
4588447a05SGarrett D'Amore 	int		ch = 0;						\
4688447a05SGarrett D'Amore 									\
4788447a05SGarrett D'Amore 	do {	/* for each channel */					\
4888447a05SGarrett D'Amore 		int32_t *ip;						\
4988447a05SGarrett D'Amore 		TYPE	*op;						\
5088447a05SGarrett D'Amore 		int	i;						\
5188447a05SGarrett D'Amore 		int	incr = eng->e_chincr[ch];			\
5288447a05SGarrett D'Amore 									\
5388447a05SGarrett D'Amore 		/* get value and adjust next channel offset */		\
5488447a05SGarrett D'Amore 		op = out + eng->e_choffs[ch] + (hidx * incr);		\
5588447a05SGarrett D'Amore 		ip = eng->e_chbufs[ch];					\
5668c47f65SGarrett D'Amore 		ip += froff;						\
5788447a05SGarrett D'Amore 									\
5868c47f65SGarrett D'Amore 		i = nfr;						\
5988447a05SGarrett D'Amore 									\
6088447a05SGarrett D'Amore 		do {	/* for each frame */				\
6188447a05SGarrett D'Amore 			int32_t sample = *ip;				\
6288447a05SGarrett D'Amore 									\
6388447a05SGarrett D'Amore 			*op = SAMPLE;					\
6488447a05SGarrett D'Amore 			op += incr;					\
6588447a05SGarrett D'Amore 			ip++;						\
6688447a05SGarrett D'Amore 									\
6788447a05SGarrett D'Amore 		} while (--i);						\
6888447a05SGarrett D'Amore 									\
6988447a05SGarrett D'Amore 		ch++;							\
7088447a05SGarrett D'Amore 	} while (ch < nch);						\
7188447a05SGarrett D'Amore }
7288447a05SGarrett D'Amore 
7388447a05SGarrett D'Amore DECL_AUDIO_EXPORT(16ne, int16_t, sample >> 8)
7488447a05SGarrett D'Amore DECL_AUDIO_EXPORT(16oe, int16_t, ddi_swap16(sample >> 8))
7588447a05SGarrett D'Amore DECL_AUDIO_EXPORT(32ne, int32_t, sample << 8)
7688447a05SGarrett D'Amore DECL_AUDIO_EXPORT(32oe, int32_t, ddi_swap32(sample << 8))
7788447a05SGarrett D'Amore DECL_AUDIO_EXPORT(24ne, int32_t, sample)
7888447a05SGarrett D'Amore DECL_AUDIO_EXPORT(24oe, int32_t, ddi_swap32(sample))
7988447a05SGarrett D'Amore 
8088447a05SGarrett D'Amore /*
8188447a05SGarrett D'Amore  * Simple limiter to prevent overflows when using fixed point computations
8288447a05SGarrett D'Amore  */
8388447a05SGarrett D'Amore static void
auimpl_output_limiter(audio_engine_t * eng)8488447a05SGarrett D'Amore auimpl_output_limiter(audio_engine_t *eng)
8588447a05SGarrett D'Amore {
8688447a05SGarrett D'Amore 	int k, t;
8768c47f65SGarrett D'Amore 	uint_t q, amp, amp2;
8888447a05SGarrett D'Amore 	int nchan = eng->e_nchan;
8968c47f65SGarrett D'Amore 	uint_t fragfr = eng->e_fragfr;
9088447a05SGarrett D'Amore 	int32_t **chbufs = eng->e_chbufs;
9168c47f65SGarrett D'Amore 	uint_t statevar = eng->e_limiter_state;
9288447a05SGarrett D'Amore 
9388447a05SGarrett D'Amore 	for (t = 0; t < fragfr; t++) {
9488447a05SGarrett D'Amore 
9568c47f65SGarrett D'Amore 		amp = (uint_t)ABS(chbufs[0][t]);
9688447a05SGarrett D'Amore 
9788447a05SGarrett D'Amore 		for (k = 1; k < nchan; k++)	{
9868c47f65SGarrett D'Amore 			amp2 = (uint_t)ABS(chbufs[k][t]);
9988447a05SGarrett D'Amore 			if (amp2 > amp)
10088447a05SGarrett D'Amore 				amp = amp2;
10188447a05SGarrett D'Amore 		}
10288447a05SGarrett D'Amore 
10388447a05SGarrett D'Amore 		amp >>= 8;
10488447a05SGarrett D'Amore 		q = 0x10000;
10588447a05SGarrett D'Amore 
10688447a05SGarrett D'Amore 		if (amp > 0x7FFF)
10788447a05SGarrett D'Amore 			q = 0x7FFF0000 / amp;
10888447a05SGarrett D'Amore 
10988447a05SGarrett D'Amore 		if (statevar > q) {
11088447a05SGarrett D'Amore 			statevar = q;
11188447a05SGarrett D'Amore 		} else {
11288447a05SGarrett D'Amore 			q = statevar;
11388447a05SGarrett D'Amore 
11488447a05SGarrett D'Amore 			/*
11588447a05SGarrett D'Amore 			 * Simplier (linear) tracking algo
11688447a05SGarrett D'Amore 			 * (gives less distortion, but more pumping)
11788447a05SGarrett D'Amore 			 */
11888447a05SGarrett D'Amore 			statevar += 2;
11988447a05SGarrett D'Amore 			if (statevar > 0x10000)
12088447a05SGarrett D'Amore 				statevar = 0x10000;
12188447a05SGarrett D'Amore 
12288447a05SGarrett D'Amore 			/*
12388447a05SGarrett D'Amore 			 * Classic tracking algo
12488447a05SGarrett D'Amore 			 * gives more distortion with no-lookahead
12588447a05SGarrett D'Amore 			 * statevar=0x10000-((0x10000-statevar)*0xFFF4>>16);
12688447a05SGarrett D'Amore 			 */
12788447a05SGarrett D'Amore 		}
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore 		for (k = 0; k < nchan; k++) {
13088447a05SGarrett D'Amore 			int32_t in = chbufs[k][t];
13188447a05SGarrett D'Amore 			int32_t out = 0;
13268c47f65SGarrett D'Amore 			uint_t p;
13388447a05SGarrett D'Amore 
13488447a05SGarrett D'Amore 			if (in >= 0) {
13588447a05SGarrett D'Amore 				p = in;
13688447a05SGarrett D'Amore 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
13788447a05SGarrett D'Amore 				    (p >> 16) * q;
13888447a05SGarrett D'Amore 				out = p;
13988447a05SGarrett D'Amore 			} else {
14088447a05SGarrett D'Amore 				p = -in;
14188447a05SGarrett D'Amore 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
14288447a05SGarrett D'Amore 				    (p >> 16) * q;
14388447a05SGarrett D'Amore 				out = -p;
14488447a05SGarrett D'Amore 			}
14588447a05SGarrett D'Amore 			/* safety code */
14688447a05SGarrett D'Amore 			/*
14788447a05SGarrett D'Amore 			 * if output after limiter is clamped, then it
14888447a05SGarrett D'Amore 			 * can be dropped
14988447a05SGarrett D'Amore 			 */
15088447a05SGarrett D'Amore 			if (out > 0x7FFFFF)
15188447a05SGarrett D'Amore 				out = 0x7FFFFF;
15288447a05SGarrett D'Amore 			else if (out < -0x7FFFFF)
15388447a05SGarrett D'Amore 				out = -0x7FFFFF;
15488447a05SGarrett D'Amore 
15588447a05SGarrett D'Amore 			chbufs[k][t] = out;
15688447a05SGarrett D'Amore 		}
15788447a05SGarrett D'Amore 	}
15888447a05SGarrett D'Amore 
15988447a05SGarrett D'Amore 	eng->e_limiter_state = statevar;
16088447a05SGarrett D'Amore }
16188447a05SGarrett D'Amore 
16288447a05SGarrett D'Amore /*
16388447a05SGarrett D'Amore  * Output mixing function.  Assumption: all work is done in 24-bit native PCM.
16488447a05SGarrett D'Amore  */
16588447a05SGarrett D'Amore static void
auimpl_output_mix(audio_stream_t * sp,int offset,int nfr)16688447a05SGarrett D'Amore auimpl_output_mix(audio_stream_t *sp, int offset, int nfr)
16788447a05SGarrett D'Amore {
16888447a05SGarrett D'Amore 	audio_engine_t *eng = sp->s_engine;
16988447a05SGarrett D'Amore 	const int32_t *src;
17088447a05SGarrett D'Amore 	int choffs;
17188447a05SGarrett D'Amore 	int nch;
17288447a05SGarrett D'Amore 	int vol;
17388447a05SGarrett D'Amore 
17488447a05SGarrett D'Amore 	/*
17588447a05SGarrett D'Amore 	 * Initial setup.
17688447a05SGarrett D'Amore 	 */
17788447a05SGarrett D'Amore 
17888447a05SGarrett D'Amore 	src = sp->s_cnv_ptr;
17988447a05SGarrett D'Amore 	choffs = sp->s_choffs;
18088447a05SGarrett D'Amore 	nch = sp->s_cnv_dst_nchan;
18188447a05SGarrett D'Amore 	vol = sp->s_gain_eff;
18288447a05SGarrett D'Amore 
18388447a05SGarrett D'Amore 	/*
18488447a05SGarrett D'Amore 	 * Do the mixing.  We de-interleave the source stream at the
18588447a05SGarrett D'Amore 	 * same time.
18688447a05SGarrett D'Amore 	 */
18788447a05SGarrett D'Amore 	for (int ch = 0; ch < nch; ch++) {
18888447a05SGarrett D'Amore 		int32_t *op;
18988447a05SGarrett D'Amore 		const int32_t *ip;
19088447a05SGarrett D'Amore 
19188447a05SGarrett D'Amore 
19288447a05SGarrett D'Amore 		ip = src + ch;
19388447a05SGarrett D'Amore 		op = eng->e_chbufs[ch + choffs];
19488447a05SGarrett D'Amore 		op += offset;
19588447a05SGarrett D'Amore 
19688447a05SGarrett D'Amore 		for (int i = nfr; i; i--) {
19788447a05SGarrett D'Amore 
19888447a05SGarrett D'Amore 			int64_t	samp;
19988447a05SGarrett D'Amore 
20088447a05SGarrett D'Amore 			samp = *ip;
20188447a05SGarrett D'Amore 			samp *= vol;
20288447a05SGarrett D'Amore 			samp /= AUDIO_VOL_SCALE;
20388447a05SGarrett D'Amore 
20488447a05SGarrett D'Amore 			ip += nch;
20588447a05SGarrett D'Amore 			*op += (int32_t)samp;
20688447a05SGarrett D'Amore 			op++;
20788447a05SGarrett D'Amore 		}
20888447a05SGarrett D'Amore 	}
20988447a05SGarrett D'Amore 
21088447a05SGarrett D'Amore 	sp->s_cnv_cnt -= nfr;
21188447a05SGarrett D'Amore 	sp->s_cnv_ptr += (nch * nfr);
21288447a05SGarrett D'Amore }
21388447a05SGarrett D'Amore 
21488447a05SGarrett D'Amore /*
21588447a05SGarrett D'Amore  * Consume a fragment's worth of data.  This is called when the data in
21688447a05SGarrett D'Amore  * the conversion buffer is exhausted, and we need to refill it from the
21788447a05SGarrett D'Amore  * source buffer.  We always consume data from the client in quantities of
21888447a05SGarrett D'Amore  * a fragment at a time (assuming that a fragment is available.)
21988447a05SGarrett D'Amore  */
22088447a05SGarrett D'Amore static void
auimpl_consume_fragment(audio_stream_t * sp)22188447a05SGarrett D'Amore auimpl_consume_fragment(audio_stream_t *sp)
22288447a05SGarrett D'Amore {
22368c47f65SGarrett D'Amore 	uint_t	count;
22468c47f65SGarrett D'Amore 	uint_t	avail;
22568c47f65SGarrett D'Amore 	uint_t	nframes;
22668c47f65SGarrett D'Amore 	uint_t	fragfr;
22768c47f65SGarrett D'Amore 	uint_t	framesz;
22868c47f65SGarrett D'Amore 	caddr_t	cnvbuf;
22988447a05SGarrett D'Amore 
23088447a05SGarrett D'Amore 	sp->s_cnv_src = sp->s_cnv_buf0;
23188447a05SGarrett D'Amore 	sp->s_cnv_dst = sp->s_cnv_buf1;
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 	fragfr = sp->s_fragfr;
23488447a05SGarrett D'Amore 	nframes = sp->s_nframes;
23588447a05SGarrett D'Amore 	framesz = sp->s_framesz;
23688447a05SGarrett D'Amore 
23788447a05SGarrett D'Amore 	ASSERT(sp->s_head >= sp->s_tail);
23888447a05SGarrett D'Amore 
23988447a05SGarrett D'Amore 	avail = sp->s_head - sp->s_tail;
24088447a05SGarrett D'Amore 	cnvbuf = sp->s_cnv_src;
24188447a05SGarrett D'Amore 
24288447a05SGarrett D'Amore 	count = min(avail, fragfr);
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore 	/*
24588447a05SGarrett D'Amore 	 * Copy data.  We deal properly with wraps.  Done as a
24688447a05SGarrett D'Amore 	 * do...while to minimize the number of tests.
24788447a05SGarrett D'Amore 	 */
24888447a05SGarrett D'Amore 	do {
24968c47f65SGarrett D'Amore 		uint_t n;
25068c47f65SGarrett D'Amore 		uint_t nbytes;
25188447a05SGarrett D'Amore 
25288447a05SGarrett D'Amore 		n = min(nframes - sp->s_tidx, count);
25388447a05SGarrett D'Amore 		nbytes = framesz * n;
25488447a05SGarrett D'Amore 		bcopy(sp->s_data + (sp->s_tidx * framesz), cnvbuf, nbytes);
25588447a05SGarrett D'Amore 		cnvbuf += nbytes;
25688447a05SGarrett D'Amore 		count -= n;
25788447a05SGarrett D'Amore 		sp->s_samples += n;
25888447a05SGarrett D'Amore 		sp->s_tail += n;
25988447a05SGarrett D'Amore 		sp->s_tidx += n;
26088447a05SGarrett D'Amore 		if (sp->s_tidx >= nframes)
26188447a05SGarrett D'Amore 			sp->s_tidx -= nframes;
26288447a05SGarrett D'Amore 	} while (count);
26388447a05SGarrett D'Amore 
26488447a05SGarrett D'Amore 	/* Note: data conversion is optional! */
26588447a05SGarrett D'Amore 	count = min(avail, fragfr);
26688447a05SGarrett D'Amore 	if (sp->s_converter != NULL) {
26788447a05SGarrett D'Amore 		sp->s_cnv_cnt = sp->s_converter(sp, count);
26888447a05SGarrett D'Amore 	} else {
26988447a05SGarrett D'Amore 		sp->s_cnv_cnt = count;
27088447a05SGarrett D'Amore 	}
27188447a05SGarrett D'Amore }
27288447a05SGarrett D'Amore 
27388447a05SGarrett D'Amore static void
auimpl_output_callback_impl(audio_engine_t * eng,audio_client_t ** output,audio_client_t ** drain)27468c47f65SGarrett D'Amore auimpl_output_callback_impl(audio_engine_t *eng, audio_client_t **output,
27568c47f65SGarrett D'Amore     audio_client_t **drain)
27688447a05SGarrett D'Amore {
27768c47f65SGarrett D'Amore 	uint_t	fragfr = eng->e_fragfr;
27868c47f65SGarrett D'Amore 	uint_t	resid;
27988447a05SGarrett D'Amore 
28088447a05SGarrett D'Amore 	/* clear any preexisting mix results */
28188447a05SGarrett D'Amore 	for (int i = 0; i < eng->e_nchan; i++)
28288447a05SGarrett D'Amore 		bzero(eng->e_chbufs[i], AUDIO_CHBUFS * sizeof (int32_t));
28388447a05SGarrett D'Amore 
28488447a05SGarrett D'Amore 	for (audio_stream_t *sp = list_head(&eng->e_streams);
28588447a05SGarrett D'Amore 	    sp != NULL;
28688447a05SGarrett D'Amore 	    sp = list_next(&eng->e_streams, sp)) {
28788447a05SGarrett D'Amore 
28888447a05SGarrett D'Amore 		int		need;
28988447a05SGarrett D'Amore 		int		avail;
29088447a05SGarrett D'Amore 		int		used;
29188447a05SGarrett D'Amore 		int		offset;
29288447a05SGarrett D'Amore 		boolean_t	drained = B_FALSE;
29388447a05SGarrett D'Amore 		audio_client_t	*c = sp->s_client;
29488447a05SGarrett D'Amore 
29588447a05SGarrett D'Amore 		/*
29688447a05SGarrett D'Amore 		 * We need/want a full fragment.  If the client has
29788447a05SGarrett D'Amore 		 * less than that available, it will cause a client
29888447a05SGarrett D'Amore 		 * underrun in auimpl_consume_fragment, but in such a
29988447a05SGarrett D'Amore 		 * case we should get silence bytes.  Assignments done
30088447a05SGarrett D'Amore 		 * ahead of the lock to minimize lock contention.
30188447a05SGarrett D'Amore 		 */
30288447a05SGarrett D'Amore 		need = fragfr;
30388447a05SGarrett D'Amore 		offset = 0;
30488447a05SGarrett D'Amore 
30588447a05SGarrett D'Amore 		mutex_enter(&sp->s_lock);
30688447a05SGarrett D'Amore 		/* skip over streams not running or paused */
30768c47f65SGarrett D'Amore 		if ((!sp->s_running) || (sp->s_paused)) {
30888447a05SGarrett D'Amore 			mutex_exit(&sp->s_lock);
30988447a05SGarrett D'Amore 			continue;
31088447a05SGarrett D'Amore 		}
31188447a05SGarrett D'Amore 
31288447a05SGarrett D'Amore 		do {
31388447a05SGarrett D'Amore 			/* make sure we have data to chew on */
31488447a05SGarrett D'Amore 			if ((avail = sp->s_cnv_cnt) == 0) {
31588447a05SGarrett D'Amore 				auimpl_consume_fragment(sp);
31688447a05SGarrett D'Amore 				sp->s_cnv_ptr = sp->s_cnv_src;
31788447a05SGarrett D'Amore 				avail = sp->s_cnv_cnt;
31888447a05SGarrett D'Amore 			}
31988447a05SGarrett D'Amore 
32088447a05SGarrett D'Amore 			/*
32188447a05SGarrett D'Amore 			 * We might have got more data than we need
32288447a05SGarrett D'Amore 			 * right now.  (E.g. 8kHz expanding to 48kHz.)
32388447a05SGarrett D'Amore 			 * Take only what we need.
32488447a05SGarrett D'Amore 			 */
32588447a05SGarrett D'Amore 			used = min(avail, need);
32688447a05SGarrett D'Amore 
32788447a05SGarrett D'Amore 			/*
32888447a05SGarrett D'Amore 			 * Mix the results, as much data as we can use
32988447a05SGarrett D'Amore 			 * this round.
33088447a05SGarrett D'Amore 			 */
33188447a05SGarrett D'Amore 			auimpl_output_mix(sp, offset, used);
33288447a05SGarrett D'Amore 
33388447a05SGarrett D'Amore 			/*
33488447a05SGarrett D'Amore 			 * Save the offset for the next round, so we don't
33588447a05SGarrett D'Amore 			 * remix into the same location.
33688447a05SGarrett D'Amore 			 */
33788447a05SGarrett D'Amore 			offset += used;
33888447a05SGarrett D'Amore 
33988447a05SGarrett D'Amore 			/*
34088447a05SGarrett D'Amore 			 * Okay, we mixed some data, but it might not
34188447a05SGarrett D'Amore 			 * have been all we need.  This can happen
34288447a05SGarrett D'Amore 			 * either because we just mixed up some
34388447a05SGarrett D'Amore 			 * partial/residual data, or because the
34488447a05SGarrett D'Amore 			 * client has a fragment size which expands to
34588447a05SGarrett D'Amore 			 * less than a full fragment for us. (Such as
34688447a05SGarrett D'Amore 			 * a client wanting to operate at a higher
34788447a05SGarrett D'Amore 			 * data rate than the engine.)
34888447a05SGarrett D'Amore 			 */
34988447a05SGarrett D'Amore 			need -= used;
35088447a05SGarrett D'Amore 
35188447a05SGarrett D'Amore 		} while (need && avail);
35288447a05SGarrett D'Amore 
35388447a05SGarrett D'Amore 		if (avail == 0) {
35488447a05SGarrett D'Amore 			/* underrun or end of data */
35588447a05SGarrett D'Amore 			if (sp->s_draining) {
35688447a05SGarrett D'Amore 				if (sp->s_drain_idx == 0) {
35788447a05SGarrett D'Amore 					sp->s_drain_idx = eng->e_head;
35888447a05SGarrett D'Amore 				}
35988447a05SGarrett D'Amore 				if (eng->e_tail >= sp->s_drain_idx) {
36088447a05SGarrett D'Amore 					sp->s_drain_idx = 0;
36188447a05SGarrett D'Amore 					sp->s_draining = B_FALSE;
36288447a05SGarrett D'Amore 					/*
36388447a05SGarrett D'Amore 					 * After draining, stop the
36488447a05SGarrett D'Amore 					 * stream cleanly.  This
36588447a05SGarrett D'Amore 					 * prevents underrun errors.
36688447a05SGarrett D'Amore 					 *
36788447a05SGarrett D'Amore 					 * (Stream will auto-start if
36888447a05SGarrett D'Amore 					 * client submits more data to
36988447a05SGarrett D'Amore 					 * it.)
37088447a05SGarrett D'Amore 					 *
37188447a05SGarrett D'Amore 					 * AC3: When an AC3 stream
37288447a05SGarrett D'Amore 					 * drains we should probably
37388447a05SGarrett D'Amore 					 * stop the actual hardware
37488447a05SGarrett D'Amore 					 * engine.
37588447a05SGarrett D'Amore 					 */
37688447a05SGarrett D'Amore 					ASSERT(mutex_owned(&eng->e_lock));
37788447a05SGarrett D'Amore 					sp->s_running = B_FALSE;
37888447a05SGarrett D'Amore 					drained = B_TRUE;
37988447a05SGarrett D'Amore 				}
38088447a05SGarrett D'Amore 			} else {
38188447a05SGarrett D'Amore 				sp->s_errors += need;
382a19bb1faSGarrett D'Amore 				eng->e_stream_underruns++;
38388447a05SGarrett D'Amore 			}
38488447a05SGarrett D'Amore 		}
38588447a05SGarrett D'Amore 
38688447a05SGarrett D'Amore 		/* wake threads waiting for stream (blocking writes, etc.) */
38788447a05SGarrett D'Amore 		cv_broadcast(&sp->s_cv);
38888447a05SGarrett D'Amore 
38988447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
39088447a05SGarrett D'Amore 
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore 		/*
39388447a05SGarrett D'Amore 		 * Asynchronously notify clients.  We do as much as
39488447a05SGarrett D'Amore 		 * possible of this outside of the lock, it avoids
39588447a05SGarrett D'Amore 		 * s_lock and c_lock contention and eliminates any
39688447a05SGarrett D'Amore 		 * chance of deadlock.
39788447a05SGarrett D'Amore 		 */
39888447a05SGarrett D'Amore 
399682cb104SGarrett D'Amore 		/*
400682cb104SGarrett D'Amore 		 * NB: The only lock we are holding now is the engine
401682cb104SGarrett D'Amore 		 * lock.  But the client can't go away because the
402682cb104SGarrett D'Amore 		 * closer would have to get the engine lock to remove
403682cb104SGarrett D'Amore 		 * the client's stream from engine.  So we're safe.
404682cb104SGarrett D'Amore 		 */
40588447a05SGarrett D'Amore 
40668c47f65SGarrett D'Amore 		if (output && (c->c_output != NULL) &&
40768c47f65SGarrett D'Amore 		    (c->c_next_output == NULL)) {
40868c47f65SGarrett D'Amore 			auclnt_hold(c);
40968c47f65SGarrett D'Amore 			c->c_next_output = *output;
41068c47f65SGarrett D'Amore 			*output = c;
411682cb104SGarrett D'Amore 		}
41288447a05SGarrett D'Amore 
41368c47f65SGarrett D'Amore 		if (drain && drained && (c->c_drain != NULL) &&
41468c47f65SGarrett D'Amore 		    (c->c_next_drain == NULL)) {
41568c47f65SGarrett D'Amore 			auclnt_hold(c);
41668c47f65SGarrett D'Amore 			c->c_next_drain = *drain;
41768c47f65SGarrett D'Amore 			*drain = c;
418682cb104SGarrett D'Amore 		}
41988447a05SGarrett D'Amore 	}
42088447a05SGarrett D'Amore 
42188447a05SGarrett D'Amore 	/*
42288447a05SGarrett D'Amore 	 * Deal with 24-bit overflows (from mixing) gracefully.
42388447a05SGarrett D'Amore 	 */
42488447a05SGarrett D'Amore 	auimpl_output_limiter(eng);
42588447a05SGarrett D'Amore 
42688447a05SGarrett D'Amore 	/*
42768c47f65SGarrett D'Amore 	 * Export the data (a whole fragment) to the device.  Deal
42868c47f65SGarrett D'Amore 	 * properly with wraps.  Note that the test and subtraction is
42968c47f65SGarrett D'Amore 	 * faster for dealing with wrap than modulo.
43088447a05SGarrett D'Amore 	 */
43168c47f65SGarrett D'Amore 	resid = fragfr;
43268c47f65SGarrett D'Amore 	do {
43368c47f65SGarrett D'Amore 		uint_t part = min(resid, eng->e_nframes - eng->e_hidx);
43468c47f65SGarrett D'Amore 		eng->e_export(eng, part, fragfr - resid);
43568c47f65SGarrett D'Amore 		eng->e_head += part;
43668c47f65SGarrett D'Amore 		eng->e_hidx += part;
43768c47f65SGarrett D'Amore 		if (eng->e_hidx == eng->e_nframes)
43868c47f65SGarrett D'Amore 			eng->e_hidx = 0;
43968c47f65SGarrett D'Amore 		resid -= part;
44068c47f65SGarrett D'Amore 	} while (resid);
44188447a05SGarrett D'Amore 
44288447a05SGarrett D'Amore 	/*
44388447a05SGarrett D'Amore 	 * Consider doing the SYNC outside of the lock.
44488447a05SGarrett D'Amore 	 */
44588447a05SGarrett D'Amore 	ENG_SYNC(eng, fragfr);
44688447a05SGarrett D'Amore }
44788447a05SGarrett D'Amore 
44888447a05SGarrett D'Amore /*
44988447a05SGarrett D'Amore  * Outer loop attempts to keep playing until we hit maximum playahead.
45088447a05SGarrett D'Amore  */
45188447a05SGarrett D'Amore 
45288447a05SGarrett D'Amore void
auimpl_output_callback(void * arg)45368c47f65SGarrett D'Amore auimpl_output_callback(void *arg)
45468c47f65SGarrett D'Amore {
45568c47f65SGarrett D'Amore 	audio_engine_t	*e = arg;
45668c47f65SGarrett D'Amore 	int64_t		cnt;
45768c47f65SGarrett D'Amore 	audio_client_t	*c;
45868c47f65SGarrett D'Amore 	audio_client_t	*output = NULL;
45968c47f65SGarrett D'Amore 	audio_client_t	*drain = NULL;
46068c47f65SGarrett D'Amore 	uint64_t	t;
46168c47f65SGarrett D'Amore 
46268c47f65SGarrett D'Amore 	mutex_enter(&e->e_lock);
46368c47f65SGarrett D'Amore 
464*2c30fa45SGarrett D'Amore 	if (e->e_suspended || e->e_failed || !e->e_periodic) {
46568c47f65SGarrett D'Amore 		mutex_exit(&e->e_lock);
46668c47f65SGarrett D'Amore 		return;
46768c47f65SGarrett D'Amore 	}
46868c47f65SGarrett D'Amore 
46968c47f65SGarrett D'Amore 	if (e->e_need_start) {
47068c47f65SGarrett D'Amore 		int rv;
47168c47f65SGarrett D'Amore 		if ((rv = ENG_START(e)) != 0) {
47268c47f65SGarrett D'Amore 			e->e_failed = B_TRUE;
47368c47f65SGarrett D'Amore 			mutex_exit(&e->e_lock);
47468c47f65SGarrett D'Amore 			audio_dev_warn(e->e_dev,
47568c47f65SGarrett D'Amore 			    "failed starting output, rv = %d", rv);
47668c47f65SGarrett D'Amore 			return;
47768c47f65SGarrett D'Amore 		}
47868c47f65SGarrett D'Amore 		e->e_need_start = B_FALSE;
47968c47f65SGarrett D'Amore 	}
48068c47f65SGarrett D'Amore 
48168c47f65SGarrett D'Amore 	t = ENG_COUNT(e);
48268c47f65SGarrett D'Amore 	if (t < e->e_tail) {
48368c47f65SGarrett D'Amore 		/*
48468c47f65SGarrett D'Amore 		 * This is a sign of a serious bug.  We should
48568c47f65SGarrett D'Amore 		 * probably offline the device via FMA, if we ever
48668c47f65SGarrett D'Amore 		 * support FMA for audio devices.
48768c47f65SGarrett D'Amore 		 */
48868c47f65SGarrett D'Amore 		e->e_failed = B_TRUE;
48968c47f65SGarrett D'Amore 		ENG_STOP(e);
49068c47f65SGarrett D'Amore 		mutex_exit(&e->e_lock);
49168c47f65SGarrett D'Amore 		audio_dev_warn(e->e_dev,
49268c47f65SGarrett D'Amore 		    "device malfunction: broken play back sample counter");
49368c47f65SGarrett D'Amore 		return;
49468c47f65SGarrett D'Amore 
49568c47f65SGarrett D'Amore 	}
49668c47f65SGarrett D'Amore 	e->e_tail = t;
49768c47f65SGarrett D'Amore 
49868c47f65SGarrett D'Amore 	if (e->e_tail > e->e_head) {
49968c47f65SGarrett D'Amore 		/* want more than we have */
50068c47f65SGarrett D'Amore 		e->e_errors++;
50168c47f65SGarrett D'Amore 		e->e_underruns++;
50268c47f65SGarrett D'Amore 	}
50368c47f65SGarrett D'Amore 
50468c47f65SGarrett D'Amore 	cnt = e->e_head - e->e_tail;
50568c47f65SGarrett D'Amore 
50668c47f65SGarrett D'Amore 	/* stay a bit ahead */
50768c47f65SGarrett D'Amore 	while (cnt < e->e_playahead) {
50868c47f65SGarrett D'Amore 		auimpl_output_callback_impl(e, &output, &drain);
50968c47f65SGarrett D'Amore 		cnt = e->e_head - e->e_tail;
51068c47f65SGarrett D'Amore 	}
51168c47f65SGarrett D'Amore 	mutex_exit(&e->e_lock);
51268c47f65SGarrett D'Amore 
51368c47f65SGarrett D'Amore 	/*
51468c47f65SGarrett D'Amore 	 * Notify client personalities.
51568c47f65SGarrett D'Amore 	 */
51668c47f65SGarrett D'Amore 	while ((c = output) != NULL) {
51768c47f65SGarrett D'Amore 
51868c47f65SGarrett D'Amore 		output = c->c_next_output;
51968c47f65SGarrett D'Amore 		c->c_next_output = NULL;
52068c47f65SGarrett D'Amore 		c->c_output(c);
52168c47f65SGarrett D'Amore 		auclnt_release(c);
52268c47f65SGarrett D'Amore 	}
52368c47f65SGarrett D'Amore 
52468c47f65SGarrett D'Amore 	while ((c = drain) != NULL) {
52568c47f65SGarrett D'Amore 
52668c47f65SGarrett D'Amore 		drain = c->c_next_drain;
52768c47f65SGarrett D'Amore 		c->c_next_drain = NULL;
52868c47f65SGarrett D'Amore 		c->c_drain(c);
52968c47f65SGarrett D'Amore 		auclnt_release(c);
53068c47f65SGarrett D'Amore 	}
53168c47f65SGarrett D'Amore 
53268c47f65SGarrett D'Amore }
53368c47f65SGarrett D'Amore 
53468c47f65SGarrett D'Amore void
auimpl_output_preload(audio_engine_t * e)53568c47f65SGarrett D'Amore auimpl_output_preload(audio_engine_t *e)
53688447a05SGarrett D'Amore {
53768c47f65SGarrett D'Amore 	int64_t	cnt;
53888447a05SGarrett D'Amore 
53968c47f65SGarrett D'Amore 	ASSERT(mutex_owned(&e->e_lock));
54068c47f65SGarrett D'Amore 
54168c47f65SGarrett D'Amore 	if (e->e_tail > e->e_head) {
54268c47f65SGarrett D'Amore 		/* want more than we have */
54368c47f65SGarrett D'Amore 		e->e_errors++;
54468c47f65SGarrett D'Amore 		e->e_underruns++;
54568c47f65SGarrett D'Amore 		e->e_tail = e->e_head;
54668c47f65SGarrett D'Amore 	}
54768c47f65SGarrett D'Amore 	cnt = e->e_head - e->e_tail;
54888447a05SGarrett D'Amore 
54988447a05SGarrett D'Amore 	/* stay a bit ahead */
55068c47f65SGarrett D'Amore 	while (cnt < e->e_playahead) {
55168c47f65SGarrett D'Amore 		auimpl_output_callback_impl(e, NULL, NULL);
55268c47f65SGarrett D'Amore 		cnt = e->e_head - e->e_tail;
55388447a05SGarrett D'Amore 	}
55488447a05SGarrett D'Amore }
555