audio_input.c revision 11936:54dc8a89ba0d
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 2010 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 <sys/sdt.h>
38#include "audio_impl.h"
39
40#define	DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT)			\
41void									\
42auimpl_import_##NAME(audio_engine_t *e, uint_t nfr, audio_stream_t *sp)	\
43{									\
44	int		nch = e->e_nchan;				\
45	int32_t		*out = (void *)sp->s_cnv_src;			\
46	TYPE		*in = (void *)e->e_data;			\
47	int		ch = 0;						\
48	int		vol = sp->s_gain_eff;				\
49									\
50	do {	/* for each channel */					\
51		TYPE 	*ip;						\
52		int32_t *op;						\
53		int 	i;						\
54		int 	incr = e->e_chincr[ch];				\
55		uint_t	tidx = e->e_tidx;				\
56									\
57		/* get value and adjust next channel offset */		\
58		op = out++;						\
59		ip = in + e->e_choffs[ch] + (tidx * incr);		\
60									\
61		i = nfr;						\
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			op += nch;					\
72									\
73			ip += incr;					\
74			if (++tidx == e->e_nframes) {			\
75				tidx = 0;				\
76				ip = in + e->e_choffs[ch];		\
77			}						\
78		} while (--i);						\
79		ch++;							\
80	} while (ch < nch);						\
81}
82
83DECL_AUDIO_IMPORT(16ne, int16_t, /* nop */, << 8)
84DECL_AUDIO_IMPORT(16oe, int16_t, ddi_swap16, << 8)
85DECL_AUDIO_IMPORT(32ne, int32_t, /* nop */, >> 8)
86DECL_AUDIO_IMPORT(32oe, int32_t, ddi_swap32, >> 8)
87DECL_AUDIO_IMPORT(24ne, int32_t, /* nop */, /* nop */)
88DECL_AUDIO_IMPORT(24oe, int32_t, ddi_swap32, /* nop */)
89
90/*
91 * Produce capture data.  This takes data from the conversion buffer
92 * and copies it into the stream data buffer.
93 */
94static void
95auimpl_produce_data(audio_stream_t *sp, uint_t count)
96{
97	uint_t	nframes;
98	uint_t	framesz;
99	caddr_t	cnvsrc;
100	caddr_t	data;
101
102	nframes = sp->s_nframes;
103	framesz = sp->s_framesz;
104
105	ASSERT(sp->s_head >= sp->s_tail);
106	ASSERT(sp->s_hidx < nframes);
107	ASSERT(sp->s_tidx < nframes);
108
109	/*
110	 * Copy data.  We deal properly with wraps.  Done as a
111	 * do...while to minimize the number of tests.
112	 */
113	cnvsrc = sp->s_cnv_src;
114	data = sp->s_data + (sp->s_hidx * framesz);
115	do {
116		unsigned nf;
117		unsigned nb;
118
119		nf = min(nframes - sp->s_hidx, count);
120		nb = nf * framesz;
121
122		bcopy(cnvsrc, data, nb);
123		data += nb;
124		cnvsrc += nb;
125		sp->s_hidx += nf;
126		sp->s_head += nf;
127		count -= nf;
128		sp->s_samples += nf;
129		if (sp->s_hidx == nframes) {
130			sp->s_hidx = 0;
131			data = sp->s_data;
132		}
133	} while (count);
134
135	ASSERT(sp->s_tail <= sp->s_head);
136	ASSERT(sp->s_hidx < nframes);
137}
138
139void
140auimpl_input_callback(void *arg)
141{
142	audio_engine_t	*e = arg;
143	uint_t		fragfr = e->e_fragfr;
144	audio_stream_t	*sp;
145	audio_client_t	*c;
146	audio_client_t	*clist = NULL;
147	list_t		*l = &e->e_streams;
148	uint64_t	h;
149
150	mutex_enter(&e->e_lock);
151
152	if (e->e_suspended || e->e_failed) {
153		mutex_exit(&e->e_lock);
154		return;
155	}
156
157	if (e->e_need_start) {
158		int rv;
159		if ((rv = ENG_START(e)) != 0) {
160			e->e_failed = B_TRUE;
161			mutex_exit(&e->e_lock);
162			audio_dev_warn(e->e_dev,
163			    "failed starting input, rv = %d", rv);
164			return;
165		}
166		e->e_need_start = B_FALSE;
167	}
168
169	h = ENG_COUNT(e);
170	ASSERT(h >= e->e_head);
171	if (h < e->e_head) {
172		/*
173		 * This is a sign of a serious bug.  We should
174		 * probably offline the device via FMA, if we ever
175		 * support FMA for audio devices.
176		 */
177		e->e_failed = B_TRUE;
178		ENG_STOP(e);
179		mutex_exit(&e->e_lock);
180		audio_dev_warn(e->e_dev,
181		    "device malfunction: broken capture sample counter");
182		return;
183	}
184	e->e_head = h;
185	ASSERT(e->e_head >= e->e_tail);
186
187	if ((e->e_head - e->e_tail) > e->e_nframes) {
188		/* no room for data, not much we can do */
189		e->e_errors++;
190		e->e_overruns++;
191	}
192
193	/* consume all fragments in the buffer */
194	while ((e->e_head - e->e_tail) > fragfr) {
195
196		/*
197		 * Consider doing the SYNC outside of the lock.
198		 */
199		ENG_SYNC(e, fragfr);
200
201		for (sp = list_head(l); sp != NULL; sp = list_next(l, sp)) {
202			int space;
203			int count;
204
205			mutex_enter(&sp->s_lock);
206			/* skip over streams paused or not running */
207			if (sp->s_paused || !sp->s_running) {
208				mutex_exit(&sp->s_lock);
209				continue;
210			}
211			sp->s_cnv_src = sp->s_cnv_buf0;
212			sp->s_cnv_dst = sp->s_cnv_buf1;
213
214			e->e_import(e, fragfr, sp);
215
216			/*
217			 * Optionally convert fragment to requested sample
218			 * format and rate.
219			 */
220			if (sp->s_converter != NULL) {
221				count = sp->s_converter(sp, fragfr);
222			} else {
223				count = fragfr;
224			}
225
226			ASSERT(sp->s_head >= sp->s_tail);
227			space = sp->s_nframes - (sp->s_head - sp->s_tail);
228			if (count > space) {
229				e->e_stream_overruns++;
230				e->e_errors++;
231				sp->s_errors += count - space;
232				count = space;
233			}
234
235			auimpl_produce_data(sp, count);
236
237			/* wake blocked threads (blocking reads, etc.) */
238			cv_broadcast(&sp->s_cv);
239
240			mutex_exit(&sp->s_lock);
241
242			/*
243			 * Add client to notification list.  We'll
244			 * process it after dropping the lock.
245			 */
246			c = sp->s_client;
247
248			if ((c->c_input != NULL) &&
249			    (c->c_next_input == NULL)) {
250				auclnt_hold(c);
251				c->c_next_input = clist;
252				clist = c;
253			}
254		}
255
256		/*
257		 * Update the tail pointer, and the data pointer.
258		 */
259		e->e_tail += fragfr;
260		e->e_tidx += fragfr;
261		if (e->e_tidx >= e->e_nframes) {
262			e->e_tidx -= e->e_nframes;
263		}
264	}
265
266	mutex_exit(&e->e_lock);
267
268	/*
269	 * Notify client personalities.
270	 */
271
272	while ((c = clist) != NULL) {
273		clist = c->c_next_input;
274		c->c_next_input = NULL;
275		c->c_input(c);
276		auclnt_release(c);
277	}
278}
279