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