/*
* 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.
*
*/
/*
* 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"
void \
{ \
int ch = 0; \
\
do { /* for each channel */ \
int i; \
\
/* get value and adjust next channel offset */ \
\
i = nfr; \
\
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++) {
uint_t 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
{
/*
* Copy data. We deal properly with wraps. Done as a
* do...while to minimize the number of tests.
*/
do {
uint_t n;
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 {
}
}
/* 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.
*/
/*
* NB: The only lock we are holding now is the engine
* lock. But the client can't go away because the
* closer would have to get the engine lock to remove
* the client's stream from engine. So we're safe.
*/
(c->c_next_output == NULL)) {
auclnt_hold(c);
c->c_next_output = *output;
*output = c;
}
(c->c_next_drain == NULL)) {
auclnt_hold(c);
c->c_next_drain = *drain;
*drain = c;
}
}
/*
* Deal with 24-bit overflows (from mixing) gracefully.
*/
/*
* Export the data (a whole fragment) to the device. Deal
* properly with wraps. Note that the test and subtraction is
* faster for dealing with wrap than modulo.
*/
do {
} while (resid);
/*
* Consider doing the SYNC outside of the lock.
*/
}
/*
* Outer loop attempts to keep playing until we hit maximum playahead.
*/
void
{
audio_engine_t *e = arg;
audio_client_t *c;
uint64_t t;
mutex_enter(&e->e_lock);
mutex_exit(&e->e_lock);
return;
}
if (e->e_need_start) {
int rv;
mutex_exit(&e->e_lock);
audio_dev_warn(e->e_dev,
"failed starting output, rv = %d", rv);
return;
}
e->e_need_start = B_FALSE;
}
t = ENG_COUNT(e);
if (t < e->e_tail) {
/*
* This is a sign of a serious bug. We should
* probably offline the device via FMA, if we ever
* support FMA for audio devices.
*/
ENG_STOP(e);
mutex_exit(&e->e_lock);
audio_dev_warn(e->e_dev,
"device malfunction: broken play back sample counter");
return;
}
e->e_tail = t;
/* want more than we have */
e->e_errors++;
e->e_underruns++;
}
/* stay a bit ahead */
while (cnt < e->e_playahead) {
}
mutex_exit(&e->e_lock);
/*
* Notify client personalities.
*/
output = c->c_next_output;
c->c_next_output = NULL;
c->c_output(c);
auclnt_release(c);
}
drain = c->c_next_drain;
c->c_next_drain = NULL;
c->c_drain(c);
auclnt_release(c);
}
}
void
{
/* want more than we have */
e->e_errors++;
e->e_underruns++;
}
/* stay a bit ahead */
while (cnt < e->e_playahead) {
}
}