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