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 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* 29 * Purpose: Virtual mixing audio input routines 30 * 31 * This file contains the actual mixing and resampling engine for input. 32 */ 33 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/sysmacros.h> 37 #include "audio_impl.h" 38 39 #define DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT) \ 40 void \ 41 auimpl_import_##NAME(audio_engine_t *eng, audio_stream_t *sp) \ 42 { \ 43 int nch = eng->e_nchan; \ 44 int32_t *out; \ 45 TYPE *in; \ 46 int ch; \ 47 void *data; \ 48 int vol; \ 49 \ 50 data = sp->s_cnv_src; \ 51 ch = 0; \ 52 in = (void *)(eng->e_data + (eng->e_tidx * eng->e_framesz)); \ 53 out = data; \ 54 vol = sp->s_gain_eff; \ 55 \ 56 do { /* for each channel */ \ 57 TYPE *ip; \ 58 int32_t *op; \ 59 int i; \ 60 \ 61 /* get value and adjust next channel offset */ \ 62 op = out++; \ 63 ip = in++; \ 64 \ 65 i = eng->e_fragfr; \ 66 \ 67 do { /* for each frame */ \ 68 int32_t sample = (TYPE)SWAP(*ip); \ 69 int32_t scaled = sample SHIFT; \ 70 \ 71 scaled *= vol; \ 72 scaled /= AUDIO_VOL_SCALE; \ 73 \ 74 *op = scaled; \ 75 ip += nch; \ 76 op += nch; \ 77 \ 78 } while (--i); \ 79 ch++; \ 80 } while (ch < nch); \ 81 } 82 83 DECL_AUDIO_IMPORT(16ne, int16_t, /* nop */, << 8) 84 DECL_AUDIO_IMPORT(16oe, int16_t, ddi_swap16, << 8) 85 DECL_AUDIO_IMPORT(32ne, int32_t, /* nop */, >> 8) 86 DECL_AUDIO_IMPORT(32oe, int32_t, ddi_swap32, >> 8) 87 DECL_AUDIO_IMPORT(24ne, int32_t, /* nop */, /* nop */) 88 DECL_AUDIO_IMPORT(24oe, int32_t, ddi_swap32, /* nop */) 89 90 /* 91 * Produce a fragment's worth of data. This is called when the data in 92 * the conversion buffer is exhausted, and we need to refill it from the 93 * source buffer. We always consume data from the client in quantities of 94 * a fragment at a time (assuming that a fragment is available.) 95 */ 96 static void 97 auimpl_produce_fragment(audio_stream_t *sp, unsigned count) 98 { 99 unsigned nframes; 100 unsigned framesz; 101 caddr_t cnvsrc; 102 caddr_t data; 103 104 nframes = sp->s_nframes; 105 framesz = sp->s_framesz; 106 107 ASSERT(sp->s_head >= sp->s_tail); 108 ASSERT(sp->s_hidx < nframes); 109 ASSERT(sp->s_tidx < nframes); 110 111 /* 112 * Copy data. We deal properly with wraps. Done as a 113 * do...while to minimize the number of tests. 114 */ 115 cnvsrc = sp->s_cnv_src; 116 data = sp->s_data + (sp->s_hidx * framesz); 117 do { 118 unsigned nf; 119 unsigned nb; 120 121 ASSERT(sp->s_hidx < nframes); 122 nf = min(nframes - sp->s_hidx, count); 123 nb = nf * framesz; 124 125 bcopy(cnvsrc, data, nb); 126 data += nb; 127 cnvsrc += nb; 128 sp->s_hidx += nf; 129 sp->s_head += nf; 130 count -= nf; 131 sp->s_samples += nf; 132 if (sp->s_hidx >= nframes) { 133 sp->s_hidx -= nframes; 134 data -= sp->s_nbytes; 135 } 136 } while (count); 137 138 ASSERT(sp->s_tail <= sp->s_head); 139 ASSERT(sp->s_hidx < nframes); 140 ASSERT(sp->s_tail <= sp->s_head); 141 ASSERT(sp->s_hidx < nframes); 142 } 143 144 void 145 auimpl_input_callback(audio_engine_t *eng) 146 { 147 int fragfr = eng->e_fragfr; 148 boolean_t overrun; 149 audio_client_t *c; 150 151 /* consume all fragments in the buffer */ 152 while ((eng->e_head - eng->e_tail) > fragfr) { 153 154 /* 155 * Consider doing the SYNC outside of the lock. 156 */ 157 ENG_SYNC(eng, fragfr); 158 159 for (audio_stream_t *sp = list_head(&eng->e_streams); 160 sp != NULL; 161 sp = list_next(&eng->e_streams, sp)) { 162 int space; 163 int count; 164 165 c = sp->s_client; 166 167 mutex_enter(&sp->s_lock); 168 /* skip over streams paused or not running */ 169 if (sp->s_paused || (!sp->s_running) || 170 eng->e_suspended) { 171 mutex_exit(&sp->s_lock); 172 continue; 173 } 174 sp->s_cnv_src = sp->s_cnv_buf0; 175 sp->s_cnv_dst = sp->s_cnv_buf1; 176 eng->e_import(eng, sp); 177 178 /* 179 * Optionally convert fragment to requested sample 180 * format and rate. 181 */ 182 if (sp->s_converter != NULL) { 183 count = sp->s_converter(sp, fragfr); 184 } else { 185 count = fragfr; 186 } 187 188 space = sp->s_nframes - (sp->s_head - sp->s_tail); 189 if (count > space) { 190 eng->e_stream_overruns++; 191 eng->e_errors++; 192 sp->s_errors += count - space; 193 count = space; 194 overrun = B_TRUE; 195 } else { 196 overrun = B_FALSE; 197 } 198 199 auimpl_produce_fragment(sp, count); 200 201 /* wake blocked threads (blocking reads, etc.) */ 202 cv_broadcast(&sp->s_cv); 203 204 mutex_exit(&sp->s_lock); 205 206 mutex_enter(&c->c_lock); 207 if (overrun) { 208 c->c_do_notify = B_TRUE; 209 } 210 c->c_do_input = B_TRUE; 211 cv_broadcast(&c->c_cv); 212 mutex_exit(&c->c_lock); 213 } 214 215 /* 216 * Update the tail pointer, and the data pointer. 217 */ 218 eng->e_tail += fragfr; 219 eng->e_tidx += fragfr; 220 if (eng->e_tidx >= eng->e_nframes) { 221 eng->e_tidx -= eng->e_nframes; 222 } 223 } 224 } 225