audio_output.c revision 88447a05f537aabe9a1bc3d5313f22581ec992a7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (C) 4Front Technologies 1996-2008.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Purpose: Virtual mixing audio output routines
*
* This file contains the actual mixing and resampling engine for output.
*/
#include <sys/sysmacros.h>
#include "audio_impl.h"
#ifdef DEBUG
int audio_underruns = 0;
#endif
void \
{ \
int ch = 0; \
\
do { /* for each channel */ \
int i; \
\
/* get value and adjust next channel offset */ \
\
i = fragfr; \
\
do { /* for each frame */ \
\
ip++; \
\
} while (--i); \
\
ch++; \
}
/*
* Simple limiter to prevent overflows when using fixed point computations
*/
static void
{
int k, t;
for (t = 0; t < fragfr; t++) {
for (k = 1; k < nchan; k++) {
}
amp >>= 8;
q = 0x10000;
if (amp > 0x7FFF)
q = 0x7FFF0000 / amp;
if (statevar > q) {
statevar = q;
} else {
q = statevar;
/*
* Simplier (linear) tracking algo
* (gives less distortion, but more pumping)
*/
statevar += 2;
if (statevar > 0x10000)
statevar = 0x10000;
/*
* Classic tracking algo
* gives more distortion with no-lookahead
* statevar=0x10000-((0x10000-statevar)*0xFFF4>>16);
*/
}
for (k = 0; k < nchan; k++) {
unsigned int p;
if (in >= 0) {
p = in;
p = ((p & 0xFFFF) * (q >> 4) >> 12) +
(p >> 16) * q;
out = p;
} else {
p = -in;
p = ((p & 0xFFFF) * (q >> 4) >> 12) +
(p >> 16) * q;
out = -p;
}
/* safety code */
/*
* if output after limiter is clamped, then it
* can be dropped
*/
if (out > 0x7FFFFF)
out = 0x7FFFFF;
else if (out < -0x7FFFFF)
out = -0x7FFFFF;
}
}
}
/*
* Output mixing function. Assumption: all work is done in 24-bit native PCM.
*/
static void
{
int choffs;
int nch;
int vol;
/*
* Initial setup.
*/
/*
* Do the mixing. We de-interleave the source stream at the
* same time.
*/
for (int i = nfr; i; i--) {
samp /= AUDIO_VOL_SCALE;
op++;
}
}
}
/*
* Consume a fragment's worth of data. This is called when the data in
* the conversion buffer is exhausted, and we need to refill it from the
* source buffer. We always consume data from the client in quantities of
* a fragment at a time (assuming that a fragment is available.)
*/
static void
{
unsigned count;
unsigned avail;
unsigned nframes;
unsigned fragfr;
unsigned framesz;
/*
* Copy data. We deal properly with wraps. Done as a
* do...while to minimize the number of tests.
*/
do {
unsigned n;
unsigned nbytes;
count -= n;
} while (count);
/* Note: data conversion is optional! */
} else {
}
}
static void
{
/* clear any preexisting mix results */
int need;
int avail;
int used;
int offset;
/*
* less than that available, it will cause a client
* underrun in auimpl_consume_fragment, but in such a
* case we should get silence bytes. Assignments done
* ahead of the lock to minimize lock contention.
*/
offset = 0;
/* skip over streams not running or paused */
continue;
}
do {
/* make sure we have data to chew on */
}
/*
* We might have got more data than we need
* right now. (E.g. 8kHz expanding to 48kHz.)
* Take only what we need.
*/
/*
* Mix the results, as much data as we can use
* this round.
*/
/*
* Save the offset for the next round, so we don't
* remix into the same location.
*/
/*
* Okay, we mixed some data, but it might not
* have been all we need. This can happen
* either because we just mixed up some
* client has a fragment size which expands to
* less than a full fragment for us. (Such as
* a client wanting to operate at a higher
* data rate than the engine.)
*/
if (avail == 0) {
/* underrun or end of data */
if (sp->s_draining) {
if (sp->s_drain_idx == 0) {
}
sp->s_drain_idx = 0;
/*
* After draining, stop the
* stream cleanly. This
* prevents underrun errors.
*
* (Stream will auto-start if
* client submits more data to
* it.)
*
* AC3: When an AC3 stream
* drains we should probably
* stop the actual hardware
* engine.
*/
}
} else {
#ifdef DEBUG
#endif
}
}
/* wake threads waiting for stream (blocking writes, etc.) */
/*
* Asynchronously notify clients. We do as much as
* possible of this outside of the lock, it avoids
* s_lock and c_lock contention and eliminates any
* chance of deadlock.
*/
mutex_enter(&c->c_lock);
c->c_do_output = B_TRUE;
if (underrun)
c->c_do_notify = B_TRUE;
if (drained)
c->c_do_drain = B_TRUE;
cv_broadcast(&c->c_cv);
mutex_exit(&c->c_lock);
}
/*
* Deal with 24-bit overflows (from mixing) gracefully.
*/
/*
* Export the data (a whole fragment) to the device.
*/
/*
* Update the head and offset. The head counts without
* wrapping, whereas the offset wraps. Note that the test +
* subtraction is faster for dealing with wrap than modulo.
*/
/*
* Consider doing the SYNC outside of the lock.
*/
}
/*
* Outer loop attempts to keep playing until we hit maximum playahead.
*/
void
{
unsigned fragfr;
/* stay a bit ahead */
}
}