xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_format.c (revision 2c30fa4582c5d6c659e059e719c5f6163f7ef1e3)
188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
24*2c30fa45SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2588447a05SGarrett D'Amore  */
2688447a05SGarrett D'Amore 
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * Purpose: Audio format conversion routines used by audio.c
2988447a05SGarrett D'Amore  */
3088447a05SGarrett D'Amore 
3188447a05SGarrett D'Amore #include <sys/types.h>
3288447a05SGarrett D'Amore #include <sys/ddi.h>
3388447a05SGarrett D'Amore #include <sys/sunddi.h>
3488447a05SGarrett D'Amore #include <sys/audio/g711.h>
3588447a05SGarrett D'Amore 
3688447a05SGarrett D'Amore #include "audio_impl.h"
3788447a05SGarrett D'Amore #include "audio_grc3.h"
3888447a05SGarrett D'Amore 
39*2c30fa45SGarrett D'Amore extern uint_t	audio_intrhz;
4088447a05SGarrett D'Amore 
4188447a05SGarrett D'Amore /*
4288447a05SGarrett D'Amore  * Note: In the function below, the division by the number of channels is
4388447a05SGarrett D'Amore  * probably fairly expensive.  It turns out that we usually deal with stereo
4488447a05SGarrett D'Amore  * or mono data, so perhaps it would be useful to build custom versions of
4588447a05SGarrett D'Amore  * this function that only dealt with stereo or mono.
4688447a05SGarrett D'Amore  */
4788447a05SGarrett D'Amore static int
do_src(audio_stream_t * sp,void * p1,void * p2,int len,int nchan)4888447a05SGarrett D'Amore do_src(audio_stream_t *sp, void *p1, void *p2, int len, int nchan)
4988447a05SGarrett D'Amore {
5088447a05SGarrett D'Amore 	int ch, size;
5188447a05SGarrett D'Amore 
5288447a05SGarrett D'Amore 	/*
5388447a05SGarrett D'Amore 	 * Note that we presume that we are doing sample rate
5488447a05SGarrett D'Amore 	 * conversions on AUDIO_FORMAT_S24_NE, which means that have 4
5588447a05SGarrett D'Amore 	 * byte and 32-bit samples.
5688447a05SGarrett D'Amore 	 */
5788447a05SGarrett D'Amore 	size = sp->s_cnv_max / 4;		/* sample size is 4 */
5888447a05SGarrett D'Amore 	size /= nchan;
5988447a05SGarrett D'Amore 
6088447a05SGarrett D'Amore 	for (ch = 0; ch < nchan; ch++) {
6188447a05SGarrett D'Amore 		grc3_convert(sp->s_src_state[ch], sp->s_src_quality,
6288447a05SGarrett D'Amore 		    p1, p2, len, size, nchan, ch);
6388447a05SGarrett D'Amore 	}
6488447a05SGarrett D'Amore 	return (((grc3state_t *)sp->s_src_state[0])->outsz);
6588447a05SGarrett D'Amore }
6688447a05SGarrett D'Amore 
6788447a05SGarrett D'Amore static void
setup_src(audio_stream_t * sp,int srate,int trate,int sch,int tch)6888447a05SGarrett D'Amore setup_src(audio_stream_t *sp, int srate, int trate, int sch, int tch)
6988447a05SGarrett D'Amore {
7088447a05SGarrett D'Amore 	int ch, nch;
7188447a05SGarrett D'Amore 
7288447a05SGarrett D'Amore 	nch = min(sch, tch);
7388447a05SGarrett D'Amore 
7488447a05SGarrett D'Amore 	ASSERT(nch <= AUDIO_MAX_CHANNELS);
7588447a05SGarrett D'Amore 
7688447a05SGarrett D'Amore 	if (sp->s_src_quality < 1)
7788447a05SGarrett D'Amore 		sp->s_src_quality = 1;
7888447a05SGarrett D'Amore 	if (sp->s_src_quality > 5)
7988447a05SGarrett D'Amore 		sp->s_src_quality = 5;
8088447a05SGarrett D'Amore 
8188447a05SGarrett D'Amore 	for (ch = 0; ch < nch; ch++) {
8288447a05SGarrett D'Amore 		grc3_reset(sp->s_src_state[ch]);
8388447a05SGarrett D'Amore 		grc3_setup(sp->s_src_state[ch], srate, trate);
8488447a05SGarrett D'Amore 	}
8588447a05SGarrett D'Amore }
8688447a05SGarrett D'Amore 
8788447a05SGarrett D'Amore static int
cnv_srconly(audio_stream_t * sp,int len)8888447a05SGarrett D'Amore cnv_srconly(audio_stream_t *sp, int len)
8988447a05SGarrett D'Amore {
9088447a05SGarrett D'Amore 	void *src = sp->s_cnv_src;
9188447a05SGarrett D'Amore 	void *dst = sp->s_cnv_dst;
9288447a05SGarrett D'Amore 
9388447a05SGarrett D'Amore 	/*
9488447a05SGarrett D'Amore 	 * We must be using 24-bit native signed.
9588447a05SGarrett D'Amore 	 */
9688447a05SGarrett D'Amore 	len = do_src(sp, src, dst, len, sp->s_cnv_src_nchan);
9788447a05SGarrett D'Amore 
9888447a05SGarrett D'Amore 	sp->s_cnv_src = dst;
9988447a05SGarrett D'Amore 	sp->s_cnv_dst = src;
10088447a05SGarrett D'Amore 
10188447a05SGarrett D'Amore 	return (len);
10288447a05SGarrett D'Amore }
10388447a05SGarrett D'Amore 
10488447a05SGarrett D'Amore static int
cnv_s24oe(audio_stream_t * sp,int len)10588447a05SGarrett D'Amore cnv_s24oe(audio_stream_t *sp, int len)
10688447a05SGarrett D'Amore {
10788447a05SGarrett D'Amore 	/*
10888447a05SGarrett D'Amore 	 * Endian switch works in both directions.  We do it in place.
10988447a05SGarrett D'Amore 	 */
11088447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
11188447a05SGarrett D'Amore 
11288447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--) {
11388447a05SGarrett D'Amore 		*src = ddi_swap32(*src);
11488447a05SGarrett D'Amore 		src++;
11588447a05SGarrett D'Amore 	}
11688447a05SGarrett D'Amore 
11788447a05SGarrett D'Amore 	return (len);
11888447a05SGarrett D'Amore }
11988447a05SGarrett D'Amore 
12088447a05SGarrett D'Amore 
12188447a05SGarrett D'Amore static int
cnv_from_s8(audio_stream_t * sp,int len)12288447a05SGarrett D'Amore cnv_from_s8(audio_stream_t *sp, int len)
12388447a05SGarrett D'Amore {
12488447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
12588447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
12688447a05SGarrett D'Amore 	int8_t *src = s;
12788447a05SGarrett D'Amore 	int32_t *dst = d;
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
13088447a05SGarrett D'Amore 		*dst++ = (*src++) << 16;
13188447a05SGarrett D'Amore 
13288447a05SGarrett D'Amore 	sp->s_cnv_src = d;
13388447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
13488447a05SGarrett D'Amore 	return (len);
13588447a05SGarrett D'Amore }
13688447a05SGarrett D'Amore 
13788447a05SGarrett D'Amore static int
cnv_from_u8(audio_stream_t * sp,int len)13888447a05SGarrett D'Amore cnv_from_u8(audio_stream_t *sp, int len)
13988447a05SGarrett D'Amore {
14088447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
14188447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
14288447a05SGarrett D'Amore 	uint8_t *src = s;
14388447a05SGarrett D'Amore 	int32_t *dst = d;
14488447a05SGarrett D'Amore 
14588447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
14688447a05SGarrett D'Amore 		*dst++ = (int8_t)((*src++) ^ 0x80) << 16;
14788447a05SGarrett D'Amore 
14888447a05SGarrett D'Amore 	sp->s_cnv_src = d;
14988447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
15088447a05SGarrett D'Amore 	return (len);
15188447a05SGarrett D'Amore }
15288447a05SGarrett D'Amore 
15388447a05SGarrett D'Amore static int
cnv_from_ulaw(audio_stream_t * sp,int len)15488447a05SGarrett D'Amore cnv_from_ulaw(audio_stream_t *sp, int len)
15588447a05SGarrett D'Amore {
15688447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
15788447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
15888447a05SGarrett D'Amore 	uint8_t *src = s;
15988447a05SGarrett D'Amore 	int32_t *dst = d;
16088447a05SGarrett D'Amore 
16188447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--) {
16288447a05SGarrett D'Amore 		*dst++ = _8ulaw2linear16[(*src++)] << 8;
16388447a05SGarrett D'Amore 	}
16488447a05SGarrett D'Amore 	sp->s_cnv_src = d;
16588447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
16688447a05SGarrett D'Amore 	return (len);
16788447a05SGarrett D'Amore }
16888447a05SGarrett D'Amore 
16988447a05SGarrett D'Amore static int
cnv_from_alaw(audio_stream_t * sp,int len)17088447a05SGarrett D'Amore cnv_from_alaw(audio_stream_t *sp, int len)
17188447a05SGarrett D'Amore {
17288447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
17388447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
17488447a05SGarrett D'Amore 	uint8_t *src = s;
17588447a05SGarrett D'Amore 	int32_t *dst = d;
17688447a05SGarrett D'Amore 
17788447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--) {
17888447a05SGarrett D'Amore 		*dst++ = _8alaw2linear16[(*src++)] << 8;
17988447a05SGarrett D'Amore 	}
18088447a05SGarrett D'Amore 	sp->s_cnv_src = d;
18188447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
18288447a05SGarrett D'Amore 	return (len);
18388447a05SGarrett D'Amore }
18488447a05SGarrett D'Amore 
18588447a05SGarrett D'Amore static int
cnv_from_s16ne(audio_stream_t * sp,int len)18688447a05SGarrett D'Amore cnv_from_s16ne(audio_stream_t *sp, int len)
18788447a05SGarrett D'Amore {
18888447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
18988447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
19088447a05SGarrett D'Amore 	int16_t *src = s;
19188447a05SGarrett D'Amore 	int32_t *dst = d;
19288447a05SGarrett D'Amore 
19388447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
19488447a05SGarrett D'Amore 		*dst++ = (*src++) << 8;
19588447a05SGarrett D'Amore 
19688447a05SGarrett D'Amore 	sp->s_cnv_src = d;
19788447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
19888447a05SGarrett D'Amore 	return (len);
19988447a05SGarrett D'Amore }
20088447a05SGarrett D'Amore 
20188447a05SGarrett D'Amore static int
cnv_from_s16oe(audio_stream_t * sp,int len)20288447a05SGarrett D'Amore cnv_from_s16oe(audio_stream_t *sp, int len)
20388447a05SGarrett D'Amore {
20488447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
20588447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
20688447a05SGarrett D'Amore 	int16_t *src = s;
20788447a05SGarrett D'Amore 	int32_t *dst = d;
20888447a05SGarrett D'Amore 
20988447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
21088447a05SGarrett D'Amore 		*dst++ = (int16_t)(ddi_swap16(*src++)) << 8;
21188447a05SGarrett D'Amore 
21288447a05SGarrett D'Amore 	sp->s_cnv_src = d;
21388447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
21488447a05SGarrett D'Amore 	return (len);
21588447a05SGarrett D'Amore }
21688447a05SGarrett D'Amore 
21788447a05SGarrett D'Amore static int
cnv_from_u16ne(audio_stream_t * sp,int len)21888447a05SGarrett D'Amore cnv_from_u16ne(audio_stream_t *sp, int len)
21988447a05SGarrett D'Amore {
22088447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
22188447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
22288447a05SGarrett D'Amore 	uint16_t *src = s;
22388447a05SGarrett D'Amore 	int32_t *dst = d;
22488447a05SGarrett D'Amore 
22588447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
22688447a05SGarrett D'Amore 		*dst++ = (int16_t)((*src++) ^ 0x8000) << 8;
22788447a05SGarrett D'Amore 
22888447a05SGarrett D'Amore 	sp->s_cnv_src = d;
22988447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
23088447a05SGarrett D'Amore 	return (len);
23188447a05SGarrett D'Amore }
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore static int
cnv_from_u16oe(audio_stream_t * sp,int len)23488447a05SGarrett D'Amore cnv_from_u16oe(audio_stream_t *sp, int len)
23588447a05SGarrett D'Amore {
23688447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
23788447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
23888447a05SGarrett D'Amore 	uint16_t *src = s;
23988447a05SGarrett D'Amore 	int32_t *dst = d;
24088447a05SGarrett D'Amore 
24188447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--)
24288447a05SGarrett D'Amore 		*dst++ = (int16_t)(ddi_swap16((*src++) ^ 0x8000)) << 8;
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore 	sp->s_cnv_src = d;
24588447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
24688447a05SGarrett D'Amore 	return (len);
24788447a05SGarrett D'Amore }
24888447a05SGarrett D'Amore 
24988447a05SGarrett D'Amore static int
cnv_from_s24p(audio_stream_t * sp,int len)25088447a05SGarrett D'Amore cnv_from_s24p(audio_stream_t *sp, int len)
25188447a05SGarrett D'Amore {
25288447a05SGarrett D'Amore 	void *s = sp->s_cnv_src;
25388447a05SGarrett D'Amore 	void *d = sp->s_cnv_dst;
25488447a05SGarrett D'Amore 	uint8_t *src = s;
25588447a05SGarrett D'Amore 	int32_t *dst = d;
25688447a05SGarrett D'Amore 	int32_t tmp;
25788447a05SGarrett D'Amore 
25888447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--) {
25988447a05SGarrett D'Amore 		/* NB: this is a little endian format */
26088447a05SGarrett D'Amore 		tmp = (*src++);
26188447a05SGarrett D'Amore 		tmp |= (*src++) << 8;
26288447a05SGarrett D'Amore 		tmp |= (*src++) << 16;
26388447a05SGarrett D'Amore 		*dst++ = tmp;
26488447a05SGarrett D'Amore 	}
26588447a05SGarrett D'Amore 
26688447a05SGarrett D'Amore 	sp->s_cnv_src = d;
26788447a05SGarrett D'Amore 	sp->s_cnv_dst = s;
26888447a05SGarrett D'Amore 	return (len);
26988447a05SGarrett D'Amore }
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore static int
cnv_from_s32ne(audio_stream_t * sp,int len)27288447a05SGarrett D'Amore cnv_from_s32ne(audio_stream_t *sp, int len)
27388447a05SGarrett D'Amore {
27488447a05SGarrett D'Amore 	/* 32-bit conversions can be done in place */
27588447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
27688447a05SGarrett D'Amore 
27788447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--, src++)
27888447a05SGarrett D'Amore 		*src = *src >> 8;
27988447a05SGarrett D'Amore 
28088447a05SGarrett D'Amore 	return (len);
28188447a05SGarrett D'Amore }
28288447a05SGarrett D'Amore 
28388447a05SGarrett D'Amore static int
cnv_from_s32oe(audio_stream_t * sp,int len)28488447a05SGarrett D'Amore cnv_from_s32oe(audio_stream_t *sp, int len)
28588447a05SGarrett D'Amore {
28688447a05SGarrett D'Amore 	/* 32-bit conversions can be done in place */
28788447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
28888447a05SGarrett D'Amore 
28988447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_src_nchan; i; i--, src++)
29088447a05SGarrett D'Amore 		*src = (int32_t)(ddi_swap32(*src)) >> 8;
29188447a05SGarrett D'Amore 
29288447a05SGarrett D'Amore 	return (len);
29388447a05SGarrett D'Amore }
29488447a05SGarrett D'Amore 
29588447a05SGarrett D'Amore /*
29688447a05SGarrett D'Amore  * NB: All the destination format conversions use the same or fewer
29788447a05SGarrett D'Amore  * bytes as the 24-bit unpacked (32-bits used per sample), so we can
29888447a05SGarrett D'Amore  * convert all of them in place.
29988447a05SGarrett D'Amore  */
30088447a05SGarrett D'Amore 
30188447a05SGarrett D'Amore static int
cnv_to_u8(audio_stream_t * sp,int len)30288447a05SGarrett D'Amore cnv_to_u8(audio_stream_t *sp, int len)
30388447a05SGarrett D'Amore {
30488447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
30588447a05SGarrett D'Amore 	uint8_t *dst = (void *)src;
30688447a05SGarrett D'Amore 
30788447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
30888447a05SGarrett D'Amore 		*dst++ = (*src++ >> 16) ^ 0x80;
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 	return (len);
31188447a05SGarrett D'Amore }
31288447a05SGarrett D'Amore 
31388447a05SGarrett D'Amore static int
cnv_to_s8(audio_stream_t * sp,int len)31488447a05SGarrett D'Amore cnv_to_s8(audio_stream_t *sp, int len)
31588447a05SGarrett D'Amore {
31688447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
31788447a05SGarrett D'Amore 	int8_t *dst = (void *)src;
31888447a05SGarrett D'Amore 
31988447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
32088447a05SGarrett D'Amore 		*dst++ = *src++ >> 16;
32188447a05SGarrett D'Amore 
32288447a05SGarrett D'Amore 	return (len);
32388447a05SGarrett D'Amore }
32488447a05SGarrett D'Amore 
32588447a05SGarrett D'Amore static int
cnv_to_ulaw(audio_stream_t * sp,int len)32688447a05SGarrett D'Amore cnv_to_ulaw(audio_stream_t *sp, int len)
32788447a05SGarrett D'Amore {
32888447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
32988447a05SGarrett D'Amore 	uint8_t *dst = (void *)src;
33088447a05SGarrett D'Amore 
33188447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
33288447a05SGarrett D'Amore 		int idx = *src++;
33388447a05SGarrett D'Amore 		idx >>= 10;
33488447a05SGarrett D'Amore 		idx += G711_ULAW_MIDPOINT;
33588447a05SGarrett D'Amore 		idx &= 0x3fff;	/* safety precaution */
33688447a05SGarrett D'Amore 		*dst++ = _14linear2ulaw8[idx];
33788447a05SGarrett D'Amore 	}
33888447a05SGarrett D'Amore 
33988447a05SGarrett D'Amore 	return (len);
34088447a05SGarrett D'Amore }
34188447a05SGarrett D'Amore 
34288447a05SGarrett D'Amore static int
cnv_to_alaw(audio_stream_t * sp,int len)34388447a05SGarrett D'Amore cnv_to_alaw(audio_stream_t *sp, int len)
34488447a05SGarrett D'Amore {
34588447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
34688447a05SGarrett D'Amore 	uint8_t *dst = (void *)src;
34788447a05SGarrett D'Amore 
34888447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
34988447a05SGarrett D'Amore 		int idx = *src++;
35088447a05SGarrett D'Amore 		idx >>= 11;
35188447a05SGarrett D'Amore 		idx += G711_ALAW_MIDPOINT;
35288447a05SGarrett D'Amore 		idx &= 0x1fff;	/* safety precaution */
35388447a05SGarrett D'Amore 		*dst++ = _13linear2alaw8[idx];
35488447a05SGarrett D'Amore 	}
35588447a05SGarrett D'Amore 
35688447a05SGarrett D'Amore 	return (len);
35788447a05SGarrett D'Amore }
35888447a05SGarrett D'Amore 
35988447a05SGarrett D'Amore static int
cnv_to_s16ne(audio_stream_t * sp,int len)36088447a05SGarrett D'Amore cnv_to_s16ne(audio_stream_t *sp, int len)
36188447a05SGarrett D'Amore {
36288447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
36388447a05SGarrett D'Amore 	int16_t *dst = (void *)src;
36488447a05SGarrett D'Amore 
36588447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
36688447a05SGarrett D'Amore 		*dst++ = *src++ >> 8;
36788447a05SGarrett D'Amore 
36888447a05SGarrett D'Amore 	return (len);
36988447a05SGarrett D'Amore }
37088447a05SGarrett D'Amore 
37188447a05SGarrett D'Amore static int
cnv_to_s16oe(audio_stream_t * sp,int len)37288447a05SGarrett D'Amore cnv_to_s16oe(audio_stream_t *sp, int len)
37388447a05SGarrett D'Amore {
37488447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
37588447a05SGarrett D'Amore 	int16_t *dst = (void *)src;
37688447a05SGarrett D'Amore 
37788447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
37888447a05SGarrett D'Amore 		*dst++ = ddi_swap16(*src++ >> 8);
37988447a05SGarrett D'Amore 
38088447a05SGarrett D'Amore 	return (len);
38188447a05SGarrett D'Amore }
38288447a05SGarrett D'Amore 
38388447a05SGarrett D'Amore static int
cnv_to_u16ne(audio_stream_t * sp,int len)38488447a05SGarrett D'Amore cnv_to_u16ne(audio_stream_t *sp, int len)
38588447a05SGarrett D'Amore {
38688447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
38788447a05SGarrett D'Amore 	uint16_t *dst = (void *)src;
38888447a05SGarrett D'Amore 
38988447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
39088447a05SGarrett D'Amore 		*dst++ = (*src++ >> 8) ^ 0x8000;
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore 	return (len);
39388447a05SGarrett D'Amore }
39488447a05SGarrett D'Amore 
39588447a05SGarrett D'Amore static int
cnv_to_u16oe(audio_stream_t * sp,int len)39688447a05SGarrett D'Amore cnv_to_u16oe(audio_stream_t *sp, int len)
39788447a05SGarrett D'Amore {
39888447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
39988447a05SGarrett D'Amore 	uint16_t *dst = (void *)src;
40088447a05SGarrett D'Amore 
40188447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--)
40288447a05SGarrett D'Amore 		*dst++ = ddi_swap16(*src++ >> 8) ^ 0x8000;
40388447a05SGarrett D'Amore 
40488447a05SGarrett D'Amore 	return (len);
40588447a05SGarrett D'Amore }
40688447a05SGarrett D'Amore 
40788447a05SGarrett D'Amore static int
cnv_to_s24p(audio_stream_t * sp,int len)40888447a05SGarrett D'Amore cnv_to_s24p(audio_stream_t *sp, int len)
40988447a05SGarrett D'Amore {
41088447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
41188447a05SGarrett D'Amore 	uint8_t *dst = (void *)src;
41288447a05SGarrett D'Amore 	int32_t d;
41388447a05SGarrett D'Amore 
41488447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
41588447a05SGarrett D'Amore 		/* NB: this is a little endian format */
41688447a05SGarrett D'Amore 		d = *src++;
41788447a05SGarrett D'Amore 		*dst++ = d & 0xff;
41888447a05SGarrett D'Amore 		*dst++ = (d >> 8) & 0xff;
41988447a05SGarrett D'Amore 		*dst++ = (d >> 16) & 0xff;
42088447a05SGarrett D'Amore 	}
42188447a05SGarrett D'Amore 
42288447a05SGarrett D'Amore 	return (len);
42388447a05SGarrett D'Amore }
42488447a05SGarrett D'Amore 
42588447a05SGarrett D'Amore static int
cnv_to_s32ne(audio_stream_t * sp,int len)42688447a05SGarrett D'Amore cnv_to_s32ne(audio_stream_t *sp, int len)
42788447a05SGarrett D'Amore {
42888447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
42988447a05SGarrett D'Amore 
43088447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--, src++)
43188447a05SGarrett D'Amore 		*src = *src << 8;
43288447a05SGarrett D'Amore 
43388447a05SGarrett D'Amore 	return (len);
43488447a05SGarrett D'Amore }
43588447a05SGarrett D'Amore 
43688447a05SGarrett D'Amore static int
cnv_to_s32oe(audio_stream_t * sp,int len)43788447a05SGarrett D'Amore cnv_to_s32oe(audio_stream_t *sp, int len)
43888447a05SGarrett D'Amore {
43988447a05SGarrett D'Amore 	int32_t *src = sp->s_cnv_src;
44088447a05SGarrett D'Amore 
44188447a05SGarrett D'Amore 	for (int i = len * sp->s_cnv_dst_nchan; i; i--, src++)
44288447a05SGarrett D'Amore 		*src = ddi_swap32(*src << 8);
44388447a05SGarrett D'Amore 
44488447a05SGarrett D'Amore 	return (len);
44588447a05SGarrett D'Amore }
44688447a05SGarrett D'Amore 
44788447a05SGarrett D'Amore static int
cnv_default(audio_stream_t * sp,int len)44888447a05SGarrett D'Amore cnv_default(audio_stream_t *sp, int len)
44988447a05SGarrett D'Amore {
45088447a05SGarrett D'Amore 	/*
45188447a05SGarrett D'Amore 	 * Note that the formats were already preverified during
45288447a05SGarrett D'Amore 	 * select_converter, to ensure that only supported formats are
45388447a05SGarrett D'Amore 	 * used.
45488447a05SGarrett D'Amore 	 */
45588447a05SGarrett D'Amore 
45688447a05SGarrett D'Amore 	/*
45788447a05SGarrett D'Amore 	 * Convert samples to 24 bit (32 bit lsb aligned) if
45888447a05SGarrett D'Amore 	 * necessary.
45988447a05SGarrett D'Amore 	 */
46088447a05SGarrett D'Amore 
46188447a05SGarrett D'Amore 	switch (sp->s_cnv_src_format) {
46288447a05SGarrett D'Amore 
46388447a05SGarrett D'Amore 	case AUDIO_FORMAT_U8:
46488447a05SGarrett D'Amore 		len = cnv_from_u8(sp, len);
46588447a05SGarrett D'Amore 		break;
46688447a05SGarrett D'Amore 
46788447a05SGarrett D'Amore 	case AUDIO_FORMAT_S8:
46888447a05SGarrett D'Amore 		len = cnv_from_s8(sp, len);
46988447a05SGarrett D'Amore 		break;
47088447a05SGarrett D'Amore 
47188447a05SGarrett D'Amore 	case AUDIO_FORMAT_ULAW:
47288447a05SGarrett D'Amore 		len = cnv_from_ulaw(sp, len);
47388447a05SGarrett D'Amore 		break;
47488447a05SGarrett D'Amore 
47588447a05SGarrett D'Amore 	case AUDIO_FORMAT_ALAW:
47688447a05SGarrett D'Amore 		len = cnv_from_alaw(sp, len);
47788447a05SGarrett D'Amore 		break;
47888447a05SGarrett D'Amore 
47988447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_NE:
48088447a05SGarrett D'Amore 		len = cnv_from_s16ne(sp, len);
48188447a05SGarrett D'Amore 		break;
48288447a05SGarrett D'Amore 
48388447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_OE:
48488447a05SGarrett D'Amore 		len = cnv_from_s16oe(sp, len);
48588447a05SGarrett D'Amore 		break;
48688447a05SGarrett D'Amore 
48788447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_NE:
48888447a05SGarrett D'Amore 		len = cnv_from_u16ne(sp, len);
48988447a05SGarrett D'Amore 		break;
49088447a05SGarrett D'Amore 
49188447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_OE:
49288447a05SGarrett D'Amore 		len = cnv_from_u16oe(sp, len);
49388447a05SGarrett D'Amore 		break;
49488447a05SGarrett D'Amore 
49588447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_NE:
49688447a05SGarrett D'Amore 		len = cnv_from_s32ne(sp, len);
49788447a05SGarrett D'Amore 		break;
49888447a05SGarrett D'Amore 
49988447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_OE:
50088447a05SGarrett D'Amore 		len = cnv_from_s32oe(sp, len);
50188447a05SGarrett D'Amore 		break;
50288447a05SGarrett D'Amore 
50388447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_OE:
50488447a05SGarrett D'Amore 		len = cnv_s24oe(sp, len);
50588447a05SGarrett D'Amore 		break;
50688447a05SGarrett D'Amore 
50788447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_PACKED:
50888447a05SGarrett D'Amore 		len = cnv_from_s24p(sp, len);
50988447a05SGarrett D'Amore 		break;
51088447a05SGarrett D'Amore 	}
51188447a05SGarrett D'Amore 
51288447a05SGarrett D'Amore 	/*
51388447a05SGarrett D'Amore 	 * If we aren't decreasing the number of channels, then do the
51488447a05SGarrett D'Amore 	 * SRC now.  (We prefer to do SRC on the smaller number of channels.)
51588447a05SGarrett D'Amore 	 */
51688447a05SGarrett D'Amore 	if (sp->s_cnv_src_rate != sp->s_cnv_dst_rate &&
51788447a05SGarrett D'Amore 	    sp->s_cnv_src_nchan <= sp->s_cnv_dst_nchan) {
51888447a05SGarrett D'Amore 		int32_t *src = sp->s_cnv_src;
51988447a05SGarrett D'Amore 		int32_t *dst = sp->s_cnv_dst;
52088447a05SGarrett D'Amore 
52188447a05SGarrett D'Amore 		len = do_src(sp, src, dst, len, sp->s_cnv_src_nchan);
52288447a05SGarrett D'Amore 
52388447a05SGarrett D'Amore 		sp->s_cnv_src = dst;
52488447a05SGarrett D'Amore 		sp->s_cnv_dst = src;
52588447a05SGarrett D'Amore 	}
52688447a05SGarrett D'Amore 
52788447a05SGarrett D'Amore 	/*
52888447a05SGarrett D'Amore 	 * Convert between mono and stereo
52988447a05SGarrett D'Amore 	 */
53088447a05SGarrett D'Amore 
53188447a05SGarrett D'Amore 	if (sp->s_cnv_src_nchan != sp->s_cnv_dst_nchan) {
53288447a05SGarrett D'Amore 		int32_t *src = sp->s_cnv_src;
53388447a05SGarrett D'Amore 		int32_t *dst = sp->s_cnv_dst;
53488447a05SGarrett D'Amore 		int tc = sp->s_cnv_dst_nchan;
53588447a05SGarrett D'Amore 		int sc = sp->s_cnv_src_nchan;
53688447a05SGarrett D'Amore 		int nc;
53788447a05SGarrett D'Amore 		int i;
53888447a05SGarrett D'Amore 
53988447a05SGarrett D'Amore 		sp->s_cnv_src = dst;
54088447a05SGarrett D'Amore 		sp->s_cnv_dst = src;
54188447a05SGarrett D'Amore 
54288447a05SGarrett D'Amore 		if (sc == 1) {
54388447a05SGarrett D'Amore 			/*
54488447a05SGarrett D'Amore 			 * Mono expansion.  We expand into the stereo
54588447a05SGarrett D'Amore 			 * channel, and leave other channels silent.
54688447a05SGarrett D'Amore 			 */
54788447a05SGarrett D'Amore 			for (i = len; i; i--) {
54888447a05SGarrett D'Amore 				*dst++ = *src;
54988447a05SGarrett D'Amore 				*dst++ = *src++;
55088447a05SGarrett D'Amore 				for (int j = tc - 2; j > 0; j--) {
55188447a05SGarrett D'Amore 					*dst++ = 0;
55288447a05SGarrett D'Amore 				}
55388447a05SGarrett D'Amore 
55488447a05SGarrett D'Amore 			}
55588447a05SGarrett D'Amore 
55688447a05SGarrett D'Amore 		} else if (sc == 2 && tc == 1) {
55788447a05SGarrett D'Amore 			/*
55888447a05SGarrett D'Amore 			 * Stereo -> mono.  We do stereo separately to make
55988447a05SGarrett D'Amore 			 * the division fast (div by const 2 is just shift).
56088447a05SGarrett D'Amore 			 */
56188447a05SGarrett D'Amore 			for (i = len; i; i--) {
56288447a05SGarrett D'Amore 				/*
56388447a05SGarrett D'Amore 				 * Take just the left channel sample,
56488447a05SGarrett D'Amore 				 * discard the right channel.
56588447a05SGarrett D'Amore 				 */
56688447a05SGarrett D'Amore 				*dst++ = *src++;	/* left */
56788447a05SGarrett D'Amore 				src++;			/* right */
56888447a05SGarrett D'Amore 			}
56988447a05SGarrett D'Amore 		} else {
57088447a05SGarrett D'Amore 			/*
57188447a05SGarrett D'Amore 			 * Multi channel conversions.  We just copy the
57288447a05SGarrett D'Amore 			 * minimum number of channels.
57388447a05SGarrett D'Amore 			 */
57488447a05SGarrett D'Amore 
57588447a05SGarrett D'Amore 			/* Calculate number of frames */
57688447a05SGarrett D'Amore 
57788447a05SGarrett D'Amore 			nc = min(sc, tc);
57888447a05SGarrett D'Amore 
57988447a05SGarrett D'Amore 			/* Clear destination */
58088447a05SGarrett D'Amore 			bzero(dst, (len * tc * sizeof (int32_t)));
58188447a05SGarrett D'Amore 
58288447a05SGarrett D'Amore 			for (i = len; i; i--) {
58388447a05SGarrett D'Amore 				int c;
58488447a05SGarrett D'Amore 
58588447a05SGarrett D'Amore 				for (c = 0; c < nc; c++)
58688447a05SGarrett D'Amore 					dst[c] = src[c];
58788447a05SGarrett D'Amore 
58888447a05SGarrett D'Amore 				src += sc;
58988447a05SGarrett D'Amore 				dst += tc;
59088447a05SGarrett D'Amore 			}
59188447a05SGarrett D'Amore 		}
59288447a05SGarrett D'Amore 	}
59388447a05SGarrett D'Amore 
59488447a05SGarrett D'Amore 	/*
59588447a05SGarrett D'Amore 	 * If we didn't do SRC pre-conversion, then do it now.
59688447a05SGarrett D'Amore 	 */
59788447a05SGarrett D'Amore 	if (sp->s_cnv_src_rate != sp->s_cnv_dst_rate &&
59888447a05SGarrett D'Amore 	    sp->s_cnv_src_nchan > sp->s_cnv_dst_nchan) {
59988447a05SGarrett D'Amore 
60088447a05SGarrett D'Amore 		int32_t *src = sp->s_cnv_src;
60188447a05SGarrett D'Amore 		int32_t *dst = sp->s_cnv_dst;
60288447a05SGarrett D'Amore 
60388447a05SGarrett D'Amore 		len = do_src(sp, src, dst, len, sp->s_cnv_dst_nchan);
60488447a05SGarrett D'Amore 
60588447a05SGarrett D'Amore 		sp->s_cnv_src = dst;
60688447a05SGarrett D'Amore 		sp->s_cnv_dst = src;
60788447a05SGarrett D'Amore 	}
60888447a05SGarrett D'Amore 
60988447a05SGarrett D'Amore 	/*
61088447a05SGarrett D'Amore 	 * Finally convert samples from internal 24 bit format to target format
61188447a05SGarrett D'Amore 	 */
61288447a05SGarrett D'Amore 
61388447a05SGarrett D'Amore 	switch (sp->s_cnv_dst_format) {
61488447a05SGarrett D'Amore 	case AUDIO_FORMAT_U8:
61588447a05SGarrett D'Amore 		len = cnv_to_u8(sp, len);
61688447a05SGarrett D'Amore 		break;
61788447a05SGarrett D'Amore 
61888447a05SGarrett D'Amore 	case AUDIO_FORMAT_S8:
61988447a05SGarrett D'Amore 		len = cnv_to_s8(sp, len);
62088447a05SGarrett D'Amore 		break;
62188447a05SGarrett D'Amore 
62288447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_NE:
62388447a05SGarrett D'Amore 		len = cnv_to_s16ne(sp, len);
62488447a05SGarrett D'Amore 		break;
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_OE:
62788447a05SGarrett D'Amore 		len = cnv_to_s16oe(sp, len);
62888447a05SGarrett D'Amore 		break;
62988447a05SGarrett D'Amore 
63088447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_NE:
63188447a05SGarrett D'Amore 		len = cnv_to_u16ne(sp, len);
63288447a05SGarrett D'Amore 		break;
63388447a05SGarrett D'Amore 
63488447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_OE:
63588447a05SGarrett D'Amore 		len = cnv_to_u16oe(sp, len);
63688447a05SGarrett D'Amore 		break;
63788447a05SGarrett D'Amore 
63888447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_OE:
63988447a05SGarrett D'Amore 		len = cnv_s24oe(sp, len);
64088447a05SGarrett D'Amore 		break;
64188447a05SGarrett D'Amore 
64288447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_PACKED:
64388447a05SGarrett D'Amore 		len = cnv_to_s24p(sp, len);
64488447a05SGarrett D'Amore 		break;
64588447a05SGarrett D'Amore 
64688447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_NE:
64788447a05SGarrett D'Amore 		len = cnv_to_s32ne(sp, len);
64888447a05SGarrett D'Amore 		break;
64988447a05SGarrett D'Amore 
65088447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_OE:
65188447a05SGarrett D'Amore 		len = cnv_to_s32oe(sp, len);
65288447a05SGarrett D'Amore 		break;
65388447a05SGarrett D'Amore 
65488447a05SGarrett D'Amore 	case AUDIO_FORMAT_ULAW:
65588447a05SGarrett D'Amore 		len = cnv_to_ulaw(sp, len);
65688447a05SGarrett D'Amore 		break;
65788447a05SGarrett D'Amore 
65888447a05SGarrett D'Amore 	case AUDIO_FORMAT_ALAW:
65988447a05SGarrett D'Amore 		len = cnv_to_alaw(sp, len);
66088447a05SGarrett D'Amore 		break;
66188447a05SGarrett D'Amore 	}
66288447a05SGarrett D'Amore 
66388447a05SGarrett D'Amore 	return (len);
66488447a05SGarrett D'Amore }
66588447a05SGarrett D'Amore 
66688447a05SGarrett D'Amore static const struct audio_format_info {
66788447a05SGarrett D'Amore 	unsigned		format;
66888447a05SGarrett D'Amore 	int			sampsize;
66988447a05SGarrett D'Amore 	audio_cnv_func_t	from;
67088447a05SGarrett D'Amore 	audio_cnv_func_t	to;
67188447a05SGarrett D'Amore } audio_format_info[] = {
67288447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S8,	1,	cnv_from_s8,		cnv_to_s8 },
67388447a05SGarrett D'Amore 	{ AUDIO_FORMAT_U8,	1,	cnv_from_u8,		cnv_to_u8 },
67488447a05SGarrett D'Amore 	{ AUDIO_FORMAT_ULAW,	1,	cnv_from_ulaw,		cnv_to_ulaw },
67588447a05SGarrett D'Amore 	{ AUDIO_FORMAT_ALAW,	1,	cnv_from_alaw,		cnv_to_alaw },
67688447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S16_NE,	2,	cnv_from_s16ne,		cnv_to_s16ne },
67788447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S16_OE,	2,	cnv_from_s16oe,		cnv_to_s16oe },
67888447a05SGarrett D'Amore 	{ AUDIO_FORMAT_U16_NE,	2,	cnv_from_u16ne,		cnv_to_u16ne },
67988447a05SGarrett D'Amore 	{ AUDIO_FORMAT_U16_OE,	2,	cnv_from_u16oe,		cnv_to_u16oe },
68088447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S32_NE,	4,	cnv_from_s32ne,		cnv_to_s32ne },
68188447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S32_OE,	4,	cnv_from_s32oe,		cnv_to_s32oe },
68288447a05SGarrett D'Amore 
68388447a05SGarrett D'Amore 	/* 24-bit formats are "special" */
68488447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S24_NE,	4,	NULL,			NULL },
68588447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S24_OE,	4,	cnv_s24oe,		cnv_s24oe },
68688447a05SGarrett D'Amore 	{ AUDIO_FORMAT_S24_PACKED, 3,	cnv_from_s24p,		cnv_to_s24p },
68788447a05SGarrett D'Amore 
68888447a05SGarrett D'Amore 	/* sentinel */
68988447a05SGarrett D'Amore 	{ AUDIO_FORMAT_NONE,	0,	NULL,			NULL }
69088447a05SGarrett D'Amore };
69188447a05SGarrett D'Amore 
69288447a05SGarrett D'Amore int
auimpl_format_setup(audio_stream_t * sp,audio_parms_t * parms,uint_t mask)693*2c30fa45SGarrett D'Amore auimpl_format_setup(audio_stream_t *sp, audio_parms_t *parms, uint_t mask)
69488447a05SGarrett D'Amore {
695*2c30fa45SGarrett D'Amore 	audio_parms_t			source;
696*2c30fa45SGarrett D'Amore 	audio_parms_t			target;
697*2c30fa45SGarrett D'Amore 	audio_parms_t			*uparms;
69888447a05SGarrett D'Amore 	audio_cnv_func_t		converter = NULL;
69988447a05SGarrett D'Amore 	const struct audio_format_info	*info;
70088447a05SGarrett D'Amore 	int				expand = AUDIO_UNIT_EXPAND;
70188447a05SGarrett D'Amore 	unsigned			cnv_sampsz = sizeof (uint32_t);
70288447a05SGarrett D'Amore 	unsigned			cnv_max;
703*2c30fa45SGarrett D'Amore 	boolean_t			needsrc = B_FALSE;
70488447a05SGarrett D'Amore 
705*2c30fa45SGarrett D'Amore 	uint_t				framesz;
706*2c30fa45SGarrett D'Amore 	uint_t				fragfr;
707*2c30fa45SGarrett D'Amore 	uint_t				fragbytes;
708*2c30fa45SGarrett D'Amore 	uint_t				nfrags;
70988447a05SGarrett D'Amore 
71088447a05SGarrett D'Amore 	ASSERT(mutex_owned(&sp->s_lock));
71188447a05SGarrett D'Amore 
712*2c30fa45SGarrett D'Amore 	source = sp->s_cnv_src_parms;
713*2c30fa45SGarrett D'Amore 	target = sp->s_cnv_dst_parms;
714*2c30fa45SGarrett D'Amore 
715*2c30fa45SGarrett D'Amore 	if (sp == &sp->s_client->c_ostream) {
716*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_FMT)
717*2c30fa45SGarrett D'Amore 			source.p_format = parms->p_format;
718*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_RATE)
719*2c30fa45SGarrett D'Amore 			source.p_rate = parms->p_rate;
720*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_CHAN)
721*2c30fa45SGarrett D'Amore 			source.p_nchan = parms->p_nchan;
722*2c30fa45SGarrett D'Amore 		uparms = &source;
723*2c30fa45SGarrett D'Amore 	} else {
724*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_FMT)
725*2c30fa45SGarrett D'Amore 			target.p_format = parms->p_format;
726*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_RATE)
727*2c30fa45SGarrett D'Amore 			target.p_rate = parms->p_rate;
728*2c30fa45SGarrett D'Amore 		if (mask & FORMAT_MSK_CHAN)
729*2c30fa45SGarrett D'Amore 			target.p_nchan = parms->p_nchan;
730*2c30fa45SGarrett D'Amore 		uparms = &target;
731*2c30fa45SGarrett D'Amore 	}
732*2c30fa45SGarrett D'Amore 
73388447a05SGarrett D'Amore 	/*
73488447a05SGarrett D'Amore 	 * At least one of the source or target are S24_NE.
73588447a05SGarrett D'Amore 	 *
73688447a05SGarrett D'Amore 	 * If we have a signed/native endian format, then pick an
73788447a05SGarrett D'Amore 	 * optimized converter.  While at it, ensure that a valid
73888447a05SGarrett D'Amore 	 * format is selected.
739*2c30fa45SGarrett D'Amore 	 *
740*2c30fa45SGarrett D'Amore 	 * After this function executes, "info" will point to the
741*2c30fa45SGarrett D'Amore 	 * format information for the user parameters.
74288447a05SGarrett D'Amore 	 */
743*2c30fa45SGarrett D'Amore 	if (source.p_format != AUDIO_FORMAT_S24_NE) {
74488447a05SGarrett D'Amore 		for (info = &audio_format_info[0]; info->sampsize; info++) {
745*2c30fa45SGarrett D'Amore 			if (source.p_format == info->format) {
74688447a05SGarrett D'Amore 				converter = info->from;
74788447a05SGarrett D'Amore 				expand *= sizeof (int32_t);
74888447a05SGarrett D'Amore 				expand /= info->sampsize;
74988447a05SGarrett D'Amore 				/* save source frame size */
75088447a05SGarrett D'Amore 				cnv_sampsz = info->sampsize;
75188447a05SGarrett D'Amore 				break;
75288447a05SGarrett D'Amore 			}
75388447a05SGarrett D'Amore 		}
754*2c30fa45SGarrett D'Amore 	} else {
755*2c30fa45SGarrett D'Amore 		/*
756*2c30fa45SGarrett D'Amore 		 * Target format.  Note that this case is also taken
757*2c30fa45SGarrett D'Amore 		 * if we're operating on S24_NE data.  In that case
758*2c30fa45SGarrett D'Amore 		 * the converter will be NULL and expand will not be
759*2c30fa45SGarrett D'Amore 		 * altered.
760*2c30fa45SGarrett D'Amore 		 */
76188447a05SGarrett D'Amore 		for (info = &audio_format_info[0]; info->sampsize; info++) {
762*2c30fa45SGarrett D'Amore 			if (target.p_format == info->format) {
76388447a05SGarrett D'Amore 				converter = info->to;
76488447a05SGarrett D'Amore 				expand *= info->sampsize;
76588447a05SGarrett D'Amore 				expand /= sizeof (int32_t);
76688447a05SGarrett D'Amore 				break;
76788447a05SGarrett D'Amore 			}
76888447a05SGarrett D'Amore 		}
769*2c30fa45SGarrett D'Amore 	}
77088447a05SGarrett D'Amore 	if (info->format == AUDIO_FORMAT_NONE) {
771*2c30fa45SGarrett D'Amore 		audio_dev_warn(sp->s_client->c_dev, "invalid format selected");
77288447a05SGarrett D'Amore 		return (EINVAL);
77388447a05SGarrett D'Amore 	}
77488447a05SGarrett D'Amore 
775*2c30fa45SGarrett D'Amore 
776*2c30fa45SGarrett D'Amore 	ASSERT(info->sampsize);
777*2c30fa45SGarrett D'Amore 
778*2c30fa45SGarrett D'Amore 	if (source.p_nchan != target.p_nchan) {
77988447a05SGarrett D'Amore 		/*
78088447a05SGarrett D'Amore 		 * if channels need conversion, then we must use the
78188447a05SGarrett D'Amore 		 * default.
78288447a05SGarrett D'Amore 		 */
78388447a05SGarrett D'Amore 		converter = cnv_default;
784*2c30fa45SGarrett D'Amore 		expand *= target.p_nchan;
785*2c30fa45SGarrett D'Amore 		expand /= source.p_nchan;
78688447a05SGarrett D'Amore 	}
78788447a05SGarrett D'Amore 
788*2c30fa45SGarrett D'Amore 	if (source.p_rate != target.p_rate) {
789*2c30fa45SGarrett D'Amore 		needsrc = B_TRUE;
79088447a05SGarrett D'Amore 		converter = (converter == NULL) ? cnv_srconly : cnv_default;
79188447a05SGarrett D'Amore 
792*2c30fa45SGarrett D'Amore 		expand *= target.p_rate;
793*2c30fa45SGarrett D'Amore 		expand /= source.p_rate;
79488447a05SGarrett D'Amore 	}
79588447a05SGarrett D'Amore 
79688447a05SGarrett D'Amore 	/*
79788447a05SGarrett D'Amore 	 * Figure out the size of the conversion buffer we need.  We
79888447a05SGarrett D'Amore 	 * assume room for two full source fragments, which ought to
79988447a05SGarrett D'Amore 	 * be enough, even with rounding errors.
80088447a05SGarrett D'Amore 	 */
801*2c30fa45SGarrett D'Amore 	cnv_max = 2 * (source.p_rate / audio_intrhz) *
802*2c30fa45SGarrett D'Amore 	    cnv_sampsz * source.p_nchan;
80388447a05SGarrett D'Amore 
80488447a05SGarrett D'Amore 	/*
80588447a05SGarrett D'Amore 	 * If the conversion will cause us to expand fragments, then
80688447a05SGarrett D'Amore 	 * we need to increase cnv_max.  Scale by AUDIO_UNIT_EXPAND to
80788447a05SGarrett D'Amore 	 * avoid rouding errors or losing bits when doing reducing
80888447a05SGarrett D'Amore 	 * conversions.
80988447a05SGarrett D'Amore 	 */
81088447a05SGarrett D'Amore 	if (expand > AUDIO_UNIT_EXPAND) {
81188447a05SGarrett D'Amore 		cnv_max *= expand;
81288447a05SGarrett D'Amore 		cnv_max /= AUDIO_UNIT_EXPAND;
81388447a05SGarrett D'Amore 	}
81488447a05SGarrett D'Amore 
815*2c30fa45SGarrett D'Amore 	framesz = info->sampsize * uparms->p_nchan;
816*2c30fa45SGarrett D'Amore 	fragfr = (uparms->p_rate / audio_intrhz);
817*2c30fa45SGarrett D'Amore 	fragbytes = fragfr * framesz;
818*2c30fa45SGarrett D'Amore 
819*2c30fa45SGarrett D'Amore 	/*
820*2c30fa45SGarrett D'Amore 	 * We need to "tune" the buffer and fragment counts for some
821*2c30fa45SGarrett D'Amore 	 * uses...  OSS applications may like to configure a low
822*2c30fa45SGarrett D'Amore 	 * latency, and they rely upon write() to block to prevent too
823*2c30fa45SGarrett D'Amore 	 * much data from being queued up.
824*2c30fa45SGarrett D'Amore 	 */
825*2c30fa45SGarrett D'Amore 	if (sp->s_hintsz) {
826*2c30fa45SGarrett D'Amore 		nfrags = sp->s_hintsz / fragbytes;
827*2c30fa45SGarrett D'Amore 	} else if (sp->s_hintfrags) {
828*2c30fa45SGarrett D'Amore 		nfrags = sp->s_hintfrags;
829*2c30fa45SGarrett D'Amore 	} else {
830*2c30fa45SGarrett D'Amore 		nfrags = sp->s_allocsz / fragbytes;
831*2c30fa45SGarrett D'Amore 	}
832*2c30fa45SGarrett D'Amore 
833*2c30fa45SGarrett D'Amore 	/*
834*2c30fa45SGarrett D'Amore 	 * Now make sure that the hint works -- we need at least 2 fragments,
835*2c30fa45SGarrett D'Amore 	 * and we need to fit within the room allocated to us.
836*2c30fa45SGarrett D'Amore 	 */
837*2c30fa45SGarrett D'Amore 	if (nfrags < 2) {
838*2c30fa45SGarrett D'Amore 		nfrags = 2;
839*2c30fa45SGarrett D'Amore 	}
840*2c30fa45SGarrett D'Amore 	while ((nfrags * fragbytes) > sp->s_allocsz) {
841*2c30fa45SGarrett D'Amore 		nfrags--;
842*2c30fa45SGarrett D'Amore 	}
843*2c30fa45SGarrett D'Amore 	/* if the resulting configuration is invalid, note it */
844*2c30fa45SGarrett D'Amore 	if (nfrags < 2) {
845*2c30fa45SGarrett D'Amore 		return (EINVAL);
846*2c30fa45SGarrett D'Amore 	}
847*2c30fa45SGarrett D'Amore 
84888447a05SGarrett D'Amore 	/*
84988447a05SGarrett D'Amore 	 * Now we need to allocate space.
850*2c30fa45SGarrett D'Amore 	 *
851*2c30fa45SGarrett D'Amore 	 * NB: Once the allocation succeeds, we must not fail.  We are
852*2c30fa45SGarrett D'Amore 	 * modifying the the stream settings and these changes must be
853*2c30fa45SGarrett D'Amore 	 * made atomically.
85488447a05SGarrett D'Amore 	 */
85588447a05SGarrett D'Amore 	if (sp->s_cnv_max < cnv_max) {
85688447a05SGarrett D'Amore 		uint32_t *buf0, *buf1;
85788447a05SGarrett D'Amore 
85888447a05SGarrett D'Amore 		buf0 = kmem_alloc(cnv_max, KM_NOSLEEP);
85988447a05SGarrett D'Amore 		buf1 = kmem_alloc(cnv_max, KM_NOSLEEP);
86088447a05SGarrett D'Amore 		if ((buf0 == NULL) || (buf1 == NULL)) {
86188447a05SGarrett D'Amore 			audio_dev_warn(sp->s_client->c_dev,
86288447a05SGarrett D'Amore 			    "failed to allocate audio conversion buffer "
86388447a05SGarrett D'Amore 			    "(%u bytes)", cnv_max);
86488447a05SGarrett D'Amore 			if (buf0)
86588447a05SGarrett D'Amore 				kmem_free(buf0, cnv_max);
86688447a05SGarrett D'Amore 			if (buf1)
86788447a05SGarrett D'Amore 				kmem_free(buf1, cnv_max);
86888447a05SGarrett D'Amore 			return (ENOMEM);
86988447a05SGarrett D'Amore 		}
87088447a05SGarrett D'Amore 
87188447a05SGarrett D'Amore 		if (sp->s_cnv_buf0)
87288447a05SGarrett D'Amore 			kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
87388447a05SGarrett D'Amore 		if (sp->s_cnv_buf1)
87488447a05SGarrett D'Amore 			kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
87588447a05SGarrett D'Amore 
87688447a05SGarrett D'Amore 		sp->s_cnv_buf0 = buf0;
87788447a05SGarrett D'Amore 		sp->s_cnv_buf1 = buf1;
87888447a05SGarrett D'Amore 		sp->s_cnv_max = cnv_max;
87988447a05SGarrett D'Amore 	}
88088447a05SGarrett D'Amore 
881*2c30fa45SGarrett D'Amore 	/* Set up the SRC state if we will be using SRC. */
882*2c30fa45SGarrett D'Amore 	if (needsrc) {
883*2c30fa45SGarrett D'Amore 		setup_src(sp, source.p_rate, target.p_rate,
884*2c30fa45SGarrett D'Amore 		    source.p_nchan, target.p_nchan);
885a702341cSGarrett D'Amore 	}
886a702341cSGarrett D'Amore 
887a702341cSGarrett D'Amore 
888*2c30fa45SGarrett D'Amore 	sp->s_framesz = framesz;
889*2c30fa45SGarrett D'Amore 	sp->s_fragfr = fragfr;
890*2c30fa45SGarrett D'Amore 	sp->s_fragbytes = fragbytes;
891*2c30fa45SGarrett D'Amore 	sp->s_nfrags = nfrags;
892*2c30fa45SGarrett D'Amore 	sp->s_nframes = nfrags * fragfr;
893*2c30fa45SGarrett D'Amore 	sp->s_nbytes = sp->s_nframes * framesz;
89488447a05SGarrett D'Amore 	*sp->s_user_parms = *uparms;
89588447a05SGarrett D'Amore 	sp->s_converter = converter;
89688447a05SGarrett D'Amore 
89788447a05SGarrett D'Amore 	/*
89888447a05SGarrett D'Amore 	 * Ensure that we toss any stale data -- probably wrong format.
89988447a05SGarrett D'Amore 	 * Note that as a consequence of this, all of the offsets and
90088447a05SGarrett D'Amore 	 * counters get reset.  Clients should not rely on these values
90188447a05SGarrett D'Amore 	 * being preserved when changing formats.
90288447a05SGarrett D'Amore 	 *
90388447a05SGarrett D'Amore 	 * Its critical that we reset the indices, in particular,
90488447a05SGarrett D'Amore 	 * because not only will the data be the wrong format, but the
90588447a05SGarrett D'Amore 	 * indices themselves are quite possibly going to be invalid.
90688447a05SGarrett D'Amore 	 */
90788447a05SGarrett D'Amore 	sp->s_cnv_cnt = 0;
90888447a05SGarrett D'Amore 	sp->s_tail = sp->s_head = 0;
90988447a05SGarrett D'Amore 	sp->s_tidx = sp->s_hidx = 0;
91088447a05SGarrett D'Amore 
91188447a05SGarrett D'Amore 	return (0);
91288447a05SGarrett D'Amore }
91388447a05SGarrett D'Amore 
91488447a05SGarrett D'Amore int
auimpl_format_alloc(audio_stream_t * sp)91588447a05SGarrett D'Amore auimpl_format_alloc(audio_stream_t *sp)
91688447a05SGarrett D'Amore {
91788447a05SGarrett D'Amore 	int	i;
91888447a05SGarrett D'Amore 
91988447a05SGarrett D'Amore 	ASSERT(mutex_owned(&sp->s_lock));
92088447a05SGarrett D'Amore 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
92188447a05SGarrett D'Amore 		sp->s_src_state[i] =
92288447a05SGarrett D'Amore 		    kmem_zalloc(sizeof (grc3state_t), KM_NOSLEEP);
92388447a05SGarrett D'Amore 		if (sp->s_src_state[i] == NULL) {
92488447a05SGarrett D'Amore 			audio_dev_warn(sp->s_client->c_dev,
92588447a05SGarrett D'Amore 			    "unable to allocate SRC state structures");
92688447a05SGarrett D'Amore 			return (ENOMEM);
92788447a05SGarrett D'Amore 		}
92888447a05SGarrett D'Amore 	}
92988447a05SGarrett D'Amore 	return (0);
93088447a05SGarrett D'Amore }
93188447a05SGarrett D'Amore 
93288447a05SGarrett D'Amore void
auimpl_format_free(audio_stream_t * sp)93388447a05SGarrett D'Amore auimpl_format_free(audio_stream_t *sp)
93488447a05SGarrett D'Amore {
93588447a05SGarrett D'Amore 	int	i;
93688447a05SGarrett D'Amore 
93788447a05SGarrett D'Amore 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
93888447a05SGarrett D'Amore 		if (sp->s_src_state[i] != NULL) {
93988447a05SGarrett D'Amore 			kmem_free(sp->s_src_state[i], sizeof (grc3state_t));
94088447a05SGarrett D'Amore 			sp->s_src_state[i] = NULL;
94188447a05SGarrett D'Amore 		}
94288447a05SGarrett D'Amore 	}
94388447a05SGarrett D'Amore }
944