xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_input.c (revision 88ecc943b4eb72f7c4fbbd8435997b85ef171fc3)
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 	boolean_t	overrun;
145 	audio_client_t	*c;
146 
147 	/* consume all fragments in the buffer */
148 	while ((eng->e_head - eng->e_tail) > fragfr) {
149 
150 		/*
151 		 * Consider doing the SYNC outside of the lock.
152 		 */
153 		ENG_SYNC(eng, fragfr);
154 
155 		for (audio_stream_t *sp = list_head(&eng->e_streams);
156 		    sp != NULL;
157 		    sp = list_next(&eng->e_streams, sp)) {
158 			int space;
159 			int count;
160 
161 			c = sp->s_client;
162 
163 			mutex_enter(&sp->s_lock);
164 			/* skip over streams paused or not running */
165 			if (sp->s_paused || (!sp->s_running) ||
166 			    eng->e_suspended) {
167 				mutex_exit(&sp->s_lock);
168 				continue;
169 			}
170 			sp->s_cnv_src = sp->s_cnv_buf0;
171 			sp->s_cnv_dst = sp->s_cnv_buf1;
172 			eng->e_import(eng, sp);
173 
174 			/*
175 			 * Optionally convert fragment to requested sample
176 			 * format and rate.
177 			 */
178 			if (sp->s_converter != NULL) {
179 				count = sp->s_converter(sp, fragfr);
180 			} else {
181 				count = fragfr;
182 			}
183 
184 			space = sp->s_nframes - (sp->s_head - sp->s_tail);
185 			if (count > space) {
186 				eng->e_stream_overruns++;
187 				eng->e_errors++;
188 				sp->s_errors += count - space;
189 				count = space;
190 				overrun = B_TRUE;
191 			} else {
192 				overrun = B_FALSE;
193 			}
194 
195 			auimpl_produce_fragment(sp, count);
196 
197 			/* wake blocked threads (blocking reads, etc.) */
198 			cv_broadcast(&sp->s_cv);
199 
200 			mutex_exit(&sp->s_lock);
201 
202 			mutex_enter(&c->c_lock);
203 			if (overrun) {
204 				c->c_do_notify = B_TRUE;
205 			}
206 			c->c_do_input = B_TRUE;
207 			cv_broadcast(&c->c_cv);
208 			mutex_exit(&c->c_lock);
209 		}
210 
211 		/*
212 		 * Update the tail pointer, and the data pointer.
213 		 */
214 		eng->e_tail += fragfr;
215 		eng->e_tidx += fragfr;
216 		if (eng->e_tidx >= eng->e_nframes) {
217 			eng->e_tidx -= eng->e_nframes;
218 		}
219 	}
220 }
221