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