audio_input.c revision 9484:fbd5ddc28e96
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
40int	audio_overruns = 0;
41#endif
42
43#define	DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT)			\
44void									\
45auimpl_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
87DECL_AUDIO_IMPORT(16ne, int16_t, /* nop */, << 8)
88DECL_AUDIO_IMPORT(16oe, int16_t, ddi_swap16, << 8)
89DECL_AUDIO_IMPORT(32ne, int32_t, /* nop */, >> 8)
90DECL_AUDIO_IMPORT(32oe, int32_t, ddi_swap32, >> 8)
91DECL_AUDIO_IMPORT(24ne, int32_t, /* nop */, /* nop */)
92DECL_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 */
100static void
101auimpl_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
148void
149auimpl_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