xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_output.c (revision ef2504f26d1ea5859db9838255bb63f488f1b050)
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 output routines
30  *
31  * This file contains the actual mixing and resampling engine for output.
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_underruns = 0;
41 #endif
42 
43 #define	DECL_AUDIO_EXPORT(NAME, TYPE, SAMPLE)				\
44 void									\
45 auimpl_export_##NAME(audio_engine_t *eng)				\
46 {									\
47 	int		fragfr = eng->e_fragfr;				\
48 	int		nch = eng->e_nchan;				\
49 	unsigned	hidx = eng->e_hidx;				\
50 	TYPE		*out = (void *)eng->e_data;			\
51 	int		ch = 0;						\
52 									\
53 	do {	/* for each channel */					\
54 		int32_t *ip;						\
55 		TYPE	*op;						\
56 		int	i;						\
57 		int	incr = eng->e_chincr[ch];			\
58 									\
59 		/* get value and adjust next channel offset */		\
60 		op = out + eng->e_choffs[ch] + (hidx * incr);		\
61 		ip = eng->e_chbufs[ch];					\
62 									\
63 		i = fragfr;						\
64 									\
65 		do {	/* for each frame */				\
66 			int32_t sample = *ip;				\
67 									\
68 			*op = SAMPLE;					\
69 			op += incr;					\
70 			ip++;						\
71 									\
72 		} while (--i);						\
73 									\
74 		ch++;							\
75 	} while (ch < nch);						\
76 }
77 
78 DECL_AUDIO_EXPORT(16ne, int16_t, sample >> 8)
79 DECL_AUDIO_EXPORT(16oe, int16_t, ddi_swap16(sample >> 8))
80 DECL_AUDIO_EXPORT(32ne, int32_t, sample << 8)
81 DECL_AUDIO_EXPORT(32oe, int32_t, ddi_swap32(sample << 8))
82 DECL_AUDIO_EXPORT(24ne, int32_t, sample)
83 DECL_AUDIO_EXPORT(24oe, int32_t, ddi_swap32(sample))
84 
85 /*
86  * Simple limiter to prevent overflows when using fixed point computations
87  */
88 static void
89 auimpl_output_limiter(audio_engine_t *eng)
90 {
91 	int k, t;
92 	unsigned int q, amp, amp2;
93 	int nchan = eng->e_nchan;
94 	int fragfr = eng->e_fragfr;
95 	int32_t **chbufs = eng->e_chbufs;
96 	unsigned int statevar = eng->e_limiter_state;
97 
98 	for (t = 0; t < fragfr; t++) {
99 
100 		amp = (unsigned)ABS(chbufs[0][t]);
101 
102 		for (k = 1; k < nchan; k++)	{
103 			amp2 = (unsigned)ABS(chbufs[k][t]);
104 			if (amp2 > amp)
105 				amp = amp2;
106 		}
107 
108 		amp >>= 8;
109 		q = 0x10000;
110 
111 		if (amp > 0x7FFF)
112 			q = 0x7FFF0000 / amp;
113 
114 		if (statevar > q) {
115 			statevar = q;
116 		} else {
117 			q = statevar;
118 
119 			/*
120 			 * Simplier (linear) tracking algo
121 			 * (gives less distortion, but more pumping)
122 			 */
123 			statevar += 2;
124 			if (statevar > 0x10000)
125 				statevar = 0x10000;
126 
127 			/*
128 			 * Classic tracking algo
129 			 * gives more distortion with no-lookahead
130 			 * statevar=0x10000-((0x10000-statevar)*0xFFF4>>16);
131 			 */
132 		}
133 
134 		for (k = 0; k < nchan; k++) {
135 			int32_t in = chbufs[k][t];
136 			int32_t out = 0;
137 			unsigned int p;
138 
139 			if (in >= 0) {
140 				p = in;
141 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
142 				    (p >> 16) * q;
143 				out = p;
144 			} else {
145 				p = -in;
146 				p = ((p & 0xFFFF) * (q >> 4) >> 12) +
147 				    (p >> 16) * q;
148 				out = -p;
149 			}
150 			/* safety code */
151 			/*
152 			 * if output after limiter is clamped, then it
153 			 * can be dropped
154 			 */
155 			if (out > 0x7FFFFF)
156 				out = 0x7FFFFF;
157 			else if (out < -0x7FFFFF)
158 				out = -0x7FFFFF;
159 
160 			chbufs[k][t] = out;
161 		}
162 	}
163 
164 	eng->e_limiter_state = statevar;
165 }
166 
167 /*
168  * Output mixing function.  Assumption: all work is done in 24-bit native PCM.
169  */
170 static void
171 auimpl_output_mix(audio_stream_t *sp, int offset, int nfr)
172 {
173 	audio_engine_t *eng = sp->s_engine;
174 	const int32_t *src;
175 	int choffs;
176 	int nch;
177 	int vol;
178 
179 	/*
180 	 * Initial setup.
181 	 */
182 
183 	src = sp->s_cnv_ptr;
184 	choffs = sp->s_choffs;
185 	nch = sp->s_cnv_dst_nchan;
186 	vol = sp->s_gain_eff;
187 
188 	/*
189 	 * Do the mixing.  We de-interleave the source stream at the
190 	 * same time.
191 	 */
192 	for (int ch = 0; ch < nch; ch++) {
193 		int32_t *op;
194 		const int32_t *ip;
195 
196 
197 		ip = src + ch;
198 		op = eng->e_chbufs[ch + choffs];
199 		op += offset;
200 
201 		for (int i = nfr; i; i--) {
202 
203 			int64_t	samp;
204 
205 			samp = *ip;
206 			samp *= vol;
207 			samp /= AUDIO_VOL_SCALE;
208 
209 			ip += nch;
210 			*op += (int32_t)samp;
211 			op++;
212 		}
213 	}
214 
215 	sp->s_cnv_cnt -= nfr;
216 	sp->s_cnv_ptr += (nch * nfr);
217 }
218 
219 /*
220  * Consume a fragment's worth of data.  This is called when the data in
221  * the conversion buffer is exhausted, and we need to refill it from the
222  * source buffer.  We always consume data from the client in quantities of
223  * a fragment at a time (assuming that a fragment is available.)
224  */
225 static void
226 auimpl_consume_fragment(audio_stream_t *sp)
227 {
228 	unsigned	count;
229 	unsigned	avail;
230 	unsigned	nframes;
231 	unsigned	fragfr;
232 	unsigned	framesz;
233 	caddr_t		cnvbuf;
234 
235 	sp->s_cnv_src = sp->s_cnv_buf0;
236 	sp->s_cnv_dst = sp->s_cnv_buf1;
237 
238 	fragfr = sp->s_fragfr;
239 	nframes = sp->s_nframes;
240 	framesz = sp->s_framesz;
241 
242 	ASSERT(sp->s_head >= sp->s_tail);
243 
244 	avail = sp->s_head - sp->s_tail;
245 	cnvbuf = sp->s_cnv_src;
246 
247 	count = min(avail, fragfr);
248 
249 	/*
250 	 * Copy data.  We deal properly with wraps.  Done as a
251 	 * do...while to minimize the number of tests.
252 	 */
253 	do {
254 		unsigned n;
255 		unsigned nbytes;
256 
257 		n = min(nframes - sp->s_tidx, count);
258 		nbytes = framesz * n;
259 		bcopy(sp->s_data + (sp->s_tidx * framesz), cnvbuf, nbytes);
260 		cnvbuf += nbytes;
261 		count -= n;
262 		sp->s_samples += n;
263 		sp->s_tail += n;
264 		sp->s_tidx += n;
265 		if (sp->s_tidx >= nframes)
266 			sp->s_tidx -= nframes;
267 	} while (count);
268 
269 	/* Note: data conversion is optional! */
270 	count = min(avail, fragfr);
271 	if (sp->s_converter != NULL) {
272 		sp->s_cnv_cnt = sp->s_converter(sp, count);
273 	} else {
274 		sp->s_cnv_cnt = count;
275 	}
276 }
277 
278 static void
279 auimpl_output_callback_impl(audio_engine_t *eng)
280 {
281 	int		fragfr = eng->e_fragfr;
282 
283 	/* clear any preexisting mix results */
284 	for (int i = 0; i < eng->e_nchan; i++)
285 		bzero(eng->e_chbufs[i], AUDIO_CHBUFS * sizeof (int32_t));
286 
287 	for (audio_stream_t *sp = list_head(&eng->e_streams);
288 	    sp != NULL;
289 	    sp = list_next(&eng->e_streams, sp)) {
290 
291 		int		need;
292 		int		avail;
293 		int		used;
294 		int		offset;
295 		boolean_t	underrun = B_FALSE;
296 		boolean_t	drained = B_FALSE;
297 		audio_client_t	*c = sp->s_client;
298 
299 		/*
300 		 * We need/want a full fragment.  If the client has
301 		 * less than that available, it will cause a client
302 		 * underrun in auimpl_consume_fragment, but in such a
303 		 * case we should get silence bytes.  Assignments done
304 		 * ahead of the lock to minimize lock contention.
305 		 */
306 		need = fragfr;
307 		offset = 0;
308 
309 		mutex_enter(&sp->s_lock);
310 		/* skip over streams not running or paused */
311 		if ((!sp->s_running) || (sp->s_paused) || eng->e_suspended) {
312 			mutex_exit(&sp->s_lock);
313 			continue;
314 		}
315 
316 		do {
317 			/* make sure we have data to chew on */
318 			if ((avail = sp->s_cnv_cnt) == 0) {
319 				auimpl_consume_fragment(sp);
320 				sp->s_cnv_ptr = sp->s_cnv_src;
321 				avail = sp->s_cnv_cnt;
322 			}
323 
324 			/*
325 			 * We might have got more data than we need
326 			 * right now.  (E.g. 8kHz expanding to 48kHz.)
327 			 * Take only what we need.
328 			 */
329 			used = min(avail, need);
330 
331 			/*
332 			 * Mix the results, as much data as we can use
333 			 * this round.
334 			 */
335 			auimpl_output_mix(sp, offset, used);
336 
337 			/*
338 			 * Save the offset for the next round, so we don't
339 			 * remix into the same location.
340 			 */
341 			offset += used;
342 
343 			/*
344 			 * Okay, we mixed some data, but it might not
345 			 * have been all we need.  This can happen
346 			 * either because we just mixed up some
347 			 * partial/residual data, or because the
348 			 * client has a fragment size which expands to
349 			 * less than a full fragment for us. (Such as
350 			 * a client wanting to operate at a higher
351 			 * data rate than the engine.)
352 			 */
353 			need -= used;
354 
355 		} while (need && avail);
356 
357 		if (avail == 0) {
358 			/* underrun or end of data */
359 			if (sp->s_draining) {
360 				if (sp->s_drain_idx == 0) {
361 					sp->s_drain_idx = eng->e_head;
362 				}
363 				if (eng->e_tail >= sp->s_drain_idx) {
364 					sp->s_drain_idx = 0;
365 					sp->s_draining = B_FALSE;
366 					/*
367 					 * After draining, stop the
368 					 * stream cleanly.  This
369 					 * prevents underrun errors.
370 					 *
371 					 * (Stream will auto-start if
372 					 * client submits more data to
373 					 * it.)
374 					 *
375 					 * AC3: When an AC3 stream
376 					 * drains we should probably
377 					 * stop the actual hardware
378 					 * engine.
379 					 */
380 					ASSERT(mutex_owned(&eng->e_lock));
381 					sp->s_running = B_FALSE;
382 					drained = B_TRUE;
383 				}
384 			} else {
385 #ifdef	DEBUG
386 				audio_underruns++;
387 #endif
388 				underrun = B_TRUE;
389 				sp->s_errors += need;
390 			}
391 		}
392 
393 		/* wake threads waiting for stream (blocking writes, etc.) */
394 		cv_broadcast(&sp->s_cv);
395 
396 		mutex_exit(&sp->s_lock);
397 
398 
399 		/*
400 		 * Asynchronously notify clients.  We do as much as
401 		 * possible of this outside of the lock, it avoids
402 		 * s_lock and c_lock contention and eliminates any
403 		 * chance of deadlock.
404 		 */
405 
406 		mutex_enter(&c->c_lock);
407 		c->c_do_output = B_TRUE;
408 
409 		if (underrun)
410 			c->c_do_notify = B_TRUE;
411 
412 		if (drained)
413 			c->c_do_drain = B_TRUE;
414 
415 		cv_broadcast(&c->c_cv);
416 		mutex_exit(&c->c_lock);
417 	}
418 
419 	/*
420 	 * Deal with 24-bit overflows (from mixing) gracefully.
421 	 */
422 	auimpl_output_limiter(eng);
423 
424 	/*
425 	 * Export the data (a whole fragment) to the device.
426 	 */
427 	eng->e_export(eng);
428 
429 	/*
430 	 * Update the head and offset.  The head counts without
431 	 * wrapping, whereas the offset wraps.  Note that the test +
432 	 * subtraction is faster for dealing with wrap than modulo.
433 	 */
434 	eng->e_head += fragfr;
435 	eng->e_hidx += fragfr;
436 	if (eng->e_hidx >= eng->e_nframes)
437 		eng->e_hidx -= eng->e_nframes;
438 
439 	/*
440 	 * Consider doing the SYNC outside of the lock.
441 	 */
442 	ENG_SYNC(eng, fragfr);
443 }
444 
445 /*
446  * Outer loop attempts to keep playing until we hit maximum playahead.
447  */
448 
449 void
450 auimpl_output_callback(audio_engine_t *eng)
451 {
452 	unsigned fragfr;
453 	int64_t cnt;
454 
455 	fragfr = eng->e_fragfr;
456 	cnt = eng->e_head - eng->e_tail;
457 
458 	/* stay a bit ahead */
459 	while (cnt < (fragfr * 4)) {
460 		auimpl_output_callback_impl(eng);
461 		cnt = eng->e_head - eng->e_tail;
462 	}
463 }
464