xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_input.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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