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.
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.
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]
22 * Copyright (C) 4Front Technologies 1996-2008.
24 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Purpose: Virtual mixing audio input routines
30 * This file contains the actual mixing and resampling engine for input.
34 #include <sys/sunddi.h>
35 #include <sys/sysmacros.h>
37 #include "audio_impl.h"
39 #define DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT) \
41 auimpl_import_##NAME(audio_engine_t *e, uint_t nfr, audio_stream_t *sp) \
43 int nch = e->e_nchan; \
44 int32_t *out = (void *)sp->s_cnv_src; \
45 TYPE *in = (void *)e->e_data; \
47 int vol = sp->s_gain_eff; \
49 do { /* for each channel */ \
53 int incr = e->e_chincr[ch]; \
54 uint_t tidx = e->e_tidx; \
56 /* get value and adjust next channel offset */ \
58 ip = in + e->e_choffs[ch] + (tidx * incr); \
62 do { /* for each frame */ \
63 int32_t sample = (TYPE)SWAP(*ip); \
64 int32_t scaled = sample SHIFT; \
67 scaled /= AUDIO_VOL_SCALE; \
73 if (++tidx == e->e_nframes) { \
75 ip = in + e->e_choffs[ch]; \
82 DECL_AUDIO_IMPORT(16ne
, int16_t, /* nop */, << 8)
83 DECL_AUDIO_IMPORT(16oe
, int16_t, ddi_swap16
, << 8)
84 DECL_AUDIO_IMPORT(32ne
, int32_t, /* nop */, >> 8)
85 DECL_AUDIO_IMPORT(32oe
, int32_t, ddi_swap32
, >> 8)
86 DECL_AUDIO_IMPORT(24ne
, int32_t, /* nop */, /* nop */)
87 DECL_AUDIO_IMPORT(24oe
, int32_t, ddi_swap32
, /* nop */)
90 * Produce capture data. This takes data from the conversion buffer
91 * and copies it into the stream data buffer.
94 auimpl_produce_data(audio_stream_t
*sp
, uint_t count
)
101 nframes
= sp
->s_nframes
;
102 framesz
= sp
->s_framesz
;
104 ASSERT(sp
->s_head
>= sp
->s_tail
);
105 ASSERT(sp
->s_hidx
< nframes
);
106 ASSERT(sp
->s_tidx
< nframes
);
109 * Copy data. We deal properly with wraps. Done as a
110 * do...while to minimize the number of tests.
112 cnvsrc
= sp
->s_cnv_src
;
113 data
= sp
->s_data
+ (sp
->s_hidx
* framesz
);
118 nf
= min(nframes
- sp
->s_hidx
, count
);
121 bcopy(cnvsrc
, data
, nb
);
128 if (sp
->s_hidx
== nframes
) {
134 ASSERT(sp
->s_tail
<= sp
->s_head
);
135 ASSERT(sp
->s_hidx
< nframes
);
139 auimpl_input_callback(void *arg
)
141 audio_engine_t
*e
= arg
;
142 uint_t fragfr
= e
->e_fragfr
;
145 audio_client_t
*clist
= NULL
;
146 list_t
*l
= &e
->e_streams
;
149 mutex_enter(&e
->e_lock
);
151 if (e
->e_suspended
|| e
->e_failed
|| !e
->e_periodic
) {
152 mutex_exit(&e
->e_lock
);
156 if (e
->e_need_start
) {
158 if ((rv
= ENG_START(e
)) != 0) {
159 e
->e_failed
= B_TRUE
;
160 mutex_exit(&e
->e_lock
);
161 audio_dev_warn(e
->e_dev
,
162 "failed starting input, rv = %d", rv
);
165 e
->e_need_start
= B_FALSE
;
169 ASSERT(h
>= e
->e_head
);
172 * This is a sign of a serious bug. We should
173 * probably offline the device via FMA, if we ever
174 * support FMA for audio devices.
176 e
->e_failed
= B_TRUE
;
178 mutex_exit(&e
->e_lock
);
179 audio_dev_warn(e
->e_dev
,
180 "device malfunction: broken capture sample counter");
184 ASSERT(e
->e_head
>= e
->e_tail
);
186 if ((e
->e_head
- e
->e_tail
) > e
->e_nframes
) {
187 /* no room for data, not much we can do */
192 /* consume all fragments in the buffer */
193 while ((e
->e_head
- e
->e_tail
) > fragfr
) {
196 * Consider doing the SYNC outside of the lock.
200 for (sp
= list_head(l
); sp
!= NULL
; sp
= list_next(l
, sp
)) {
204 mutex_enter(&sp
->s_lock
);
205 /* skip over streams paused or not running */
206 if (sp
->s_paused
|| !sp
->s_running
) {
207 mutex_exit(&sp
->s_lock
);
210 sp
->s_cnv_src
= sp
->s_cnv_buf0
;
211 sp
->s_cnv_dst
= sp
->s_cnv_buf1
;
213 e
->e_import(e
, fragfr
, sp
);
216 * Optionally convert fragment to requested sample
219 if (sp
->s_converter
!= NULL
) {
220 count
= sp
->s_converter(sp
, fragfr
);
225 ASSERT(sp
->s_head
>= sp
->s_tail
);
226 space
= sp
->s_nframes
- (sp
->s_head
- sp
->s_tail
);
228 e
->e_stream_overruns
++;
230 sp
->s_errors
+= count
- space
;
234 auimpl_produce_data(sp
, count
);
236 /* wake blocked threads (blocking reads, etc.) */
237 cv_broadcast(&sp
->s_cv
);
239 mutex_exit(&sp
->s_lock
);
242 * Add client to notification list. We'll
243 * process it after dropping the lock.
247 if ((c
->c_input
!= NULL
) &&
248 (c
->c_next_input
== NULL
)) {
250 c
->c_next_input
= clist
;
256 * Update the tail pointer, and the data pointer.
260 if (e
->e_tidx
>= e
->e_nframes
) {
261 e
->e_tidx
-= e
->e_nframes
;
265 mutex_exit(&e
->e_lock
);
268 * Notify client personalities.
271 while ((c
= clist
) != NULL
) {
272 clist
= c
->c_next_input
;
273 c
->c_next_input
= NULL
;