19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM * CDDL HEADER START
39484Sgarrett.damore@Sun.COM *
49484Sgarrett.damore@Sun.COM * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM *
89484Sgarrett.damore@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM * and limitations under the License.
129484Sgarrett.damore@Sun.COM *
139484Sgarrett.damore@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM *
199484Sgarrett.damore@Sun.COM * CDDL HEADER END
209484Sgarrett.damore@Sun.COM */
219484Sgarrett.damore@Sun.COM /*
229484Sgarrett.damore@Sun.COM * Copyright (C) 4Front Technologies 1996-2008.
239484Sgarrett.damore@Sun.COM *
24*12165Sgdamore@opensolaris.org * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
259484Sgarrett.damore@Sun.COM */
269484Sgarrett.damore@Sun.COM
279484Sgarrett.damore@Sun.COM /*
289484Sgarrett.damore@Sun.COM * Purpose: Audio format conversion routines used by audio.c
299484Sgarrett.damore@Sun.COM */
309484Sgarrett.damore@Sun.COM
319484Sgarrett.damore@Sun.COM #include <sys/types.h>
329484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
339484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
349484Sgarrett.damore@Sun.COM #include <sys/audio/g711.h>
359484Sgarrett.damore@Sun.COM
369484Sgarrett.damore@Sun.COM #include "audio_impl.h"
379484Sgarrett.damore@Sun.COM #include "audio_grc3.h"
389484Sgarrett.damore@Sun.COM
39*12165Sgdamore@opensolaris.org extern uint_t audio_intrhz;
409484Sgarrett.damore@Sun.COM
419484Sgarrett.damore@Sun.COM /*
429484Sgarrett.damore@Sun.COM * Note: In the function below, the division by the number of channels is
439484Sgarrett.damore@Sun.COM * probably fairly expensive. It turns out that we usually deal with stereo
449484Sgarrett.damore@Sun.COM * or mono data, so perhaps it would be useful to build custom versions of
459484Sgarrett.damore@Sun.COM * this function that only dealt with stereo or mono.
469484Sgarrett.damore@Sun.COM */
479484Sgarrett.damore@Sun.COM static int
do_src(audio_stream_t * sp,void * p1,void * p2,int len,int nchan)489484Sgarrett.damore@Sun.COM do_src(audio_stream_t *sp, void *p1, void *p2, int len, int nchan)
499484Sgarrett.damore@Sun.COM {
509484Sgarrett.damore@Sun.COM int ch, size;
519484Sgarrett.damore@Sun.COM
529484Sgarrett.damore@Sun.COM /*
539484Sgarrett.damore@Sun.COM * Note that we presume that we are doing sample rate
549484Sgarrett.damore@Sun.COM * conversions on AUDIO_FORMAT_S24_NE, which means that have 4
559484Sgarrett.damore@Sun.COM * byte and 32-bit samples.
569484Sgarrett.damore@Sun.COM */
579484Sgarrett.damore@Sun.COM size = sp->s_cnv_max / 4; /* sample size is 4 */
589484Sgarrett.damore@Sun.COM size /= nchan;
599484Sgarrett.damore@Sun.COM
609484Sgarrett.damore@Sun.COM for (ch = 0; ch < nchan; ch++) {
619484Sgarrett.damore@Sun.COM grc3_convert(sp->s_src_state[ch], sp->s_src_quality,
629484Sgarrett.damore@Sun.COM p1, p2, len, size, nchan, ch);
639484Sgarrett.damore@Sun.COM }
649484Sgarrett.damore@Sun.COM return (((grc3state_t *)sp->s_src_state[0])->outsz);
659484Sgarrett.damore@Sun.COM }
669484Sgarrett.damore@Sun.COM
679484Sgarrett.damore@Sun.COM static void
setup_src(audio_stream_t * sp,int srate,int trate,int sch,int tch)689484Sgarrett.damore@Sun.COM setup_src(audio_stream_t *sp, int srate, int trate, int sch, int tch)
699484Sgarrett.damore@Sun.COM {
709484Sgarrett.damore@Sun.COM int ch, nch;
719484Sgarrett.damore@Sun.COM
729484Sgarrett.damore@Sun.COM nch = min(sch, tch);
739484Sgarrett.damore@Sun.COM
749484Sgarrett.damore@Sun.COM ASSERT(nch <= AUDIO_MAX_CHANNELS);
759484Sgarrett.damore@Sun.COM
769484Sgarrett.damore@Sun.COM if (sp->s_src_quality < 1)
779484Sgarrett.damore@Sun.COM sp->s_src_quality = 1;
789484Sgarrett.damore@Sun.COM if (sp->s_src_quality > 5)
799484Sgarrett.damore@Sun.COM sp->s_src_quality = 5;
809484Sgarrett.damore@Sun.COM
819484Sgarrett.damore@Sun.COM for (ch = 0; ch < nch; ch++) {
829484Sgarrett.damore@Sun.COM grc3_reset(sp->s_src_state[ch]);
839484Sgarrett.damore@Sun.COM grc3_setup(sp->s_src_state[ch], srate, trate);
849484Sgarrett.damore@Sun.COM }
859484Sgarrett.damore@Sun.COM }
869484Sgarrett.damore@Sun.COM
879484Sgarrett.damore@Sun.COM static int
cnv_srconly(audio_stream_t * sp,int len)889484Sgarrett.damore@Sun.COM cnv_srconly(audio_stream_t *sp, int len)
899484Sgarrett.damore@Sun.COM {
909484Sgarrett.damore@Sun.COM void *src = sp->s_cnv_src;
919484Sgarrett.damore@Sun.COM void *dst = sp->s_cnv_dst;
929484Sgarrett.damore@Sun.COM
939484Sgarrett.damore@Sun.COM /*
949484Sgarrett.damore@Sun.COM * We must be using 24-bit native signed.
959484Sgarrett.damore@Sun.COM */
969484Sgarrett.damore@Sun.COM len = do_src(sp, src, dst, len, sp->s_cnv_src_nchan);
979484Sgarrett.damore@Sun.COM
989484Sgarrett.damore@Sun.COM sp->s_cnv_src = dst;
999484Sgarrett.damore@Sun.COM sp->s_cnv_dst = src;
1009484Sgarrett.damore@Sun.COM
1019484Sgarrett.damore@Sun.COM return (len);
1029484Sgarrett.damore@Sun.COM }
1039484Sgarrett.damore@Sun.COM
1049484Sgarrett.damore@Sun.COM static int
cnv_s24oe(audio_stream_t * sp,int len)1059484Sgarrett.damore@Sun.COM cnv_s24oe(audio_stream_t *sp, int len)
1069484Sgarrett.damore@Sun.COM {
1079484Sgarrett.damore@Sun.COM /*
1089484Sgarrett.damore@Sun.COM * Endian switch works in both directions. We do it in place.
1099484Sgarrett.damore@Sun.COM */
1109484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
1119484Sgarrett.damore@Sun.COM
1129484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--) {
1139484Sgarrett.damore@Sun.COM *src = ddi_swap32(*src);
1149484Sgarrett.damore@Sun.COM src++;
1159484Sgarrett.damore@Sun.COM }
1169484Sgarrett.damore@Sun.COM
1179484Sgarrett.damore@Sun.COM return (len);
1189484Sgarrett.damore@Sun.COM }
1199484Sgarrett.damore@Sun.COM
1209484Sgarrett.damore@Sun.COM
1219484Sgarrett.damore@Sun.COM static int
cnv_from_s8(audio_stream_t * sp,int len)1229484Sgarrett.damore@Sun.COM cnv_from_s8(audio_stream_t *sp, int len)
1239484Sgarrett.damore@Sun.COM {
1249484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
1259484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
1269484Sgarrett.damore@Sun.COM int8_t *src = s;
1279484Sgarrett.damore@Sun.COM int32_t *dst = d;
1289484Sgarrett.damore@Sun.COM
1299484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
1309484Sgarrett.damore@Sun.COM *dst++ = (*src++) << 16;
1319484Sgarrett.damore@Sun.COM
1329484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
1339484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
1349484Sgarrett.damore@Sun.COM return (len);
1359484Sgarrett.damore@Sun.COM }
1369484Sgarrett.damore@Sun.COM
1379484Sgarrett.damore@Sun.COM static int
cnv_from_u8(audio_stream_t * sp,int len)1389484Sgarrett.damore@Sun.COM cnv_from_u8(audio_stream_t *sp, int len)
1399484Sgarrett.damore@Sun.COM {
1409484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
1419484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
1429484Sgarrett.damore@Sun.COM uint8_t *src = s;
1439484Sgarrett.damore@Sun.COM int32_t *dst = d;
1449484Sgarrett.damore@Sun.COM
1459484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
1469484Sgarrett.damore@Sun.COM *dst++ = (int8_t)((*src++) ^ 0x80) << 16;
1479484Sgarrett.damore@Sun.COM
1489484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
1499484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
1509484Sgarrett.damore@Sun.COM return (len);
1519484Sgarrett.damore@Sun.COM }
1529484Sgarrett.damore@Sun.COM
1539484Sgarrett.damore@Sun.COM static int
cnv_from_ulaw(audio_stream_t * sp,int len)1549484Sgarrett.damore@Sun.COM cnv_from_ulaw(audio_stream_t *sp, int len)
1559484Sgarrett.damore@Sun.COM {
1569484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
1579484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
1589484Sgarrett.damore@Sun.COM uint8_t *src = s;
1599484Sgarrett.damore@Sun.COM int32_t *dst = d;
1609484Sgarrett.damore@Sun.COM
1619484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--) {
1629484Sgarrett.damore@Sun.COM *dst++ = _8ulaw2linear16[(*src++)] << 8;
1639484Sgarrett.damore@Sun.COM }
1649484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
1659484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
1669484Sgarrett.damore@Sun.COM return (len);
1679484Sgarrett.damore@Sun.COM }
1689484Sgarrett.damore@Sun.COM
1699484Sgarrett.damore@Sun.COM static int
cnv_from_alaw(audio_stream_t * sp,int len)1709484Sgarrett.damore@Sun.COM cnv_from_alaw(audio_stream_t *sp, int len)
1719484Sgarrett.damore@Sun.COM {
1729484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
1739484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
1749484Sgarrett.damore@Sun.COM uint8_t *src = s;
1759484Sgarrett.damore@Sun.COM int32_t *dst = d;
1769484Sgarrett.damore@Sun.COM
1779484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--) {
1789484Sgarrett.damore@Sun.COM *dst++ = _8alaw2linear16[(*src++)] << 8;
1799484Sgarrett.damore@Sun.COM }
1809484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
1819484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
1829484Sgarrett.damore@Sun.COM return (len);
1839484Sgarrett.damore@Sun.COM }
1849484Sgarrett.damore@Sun.COM
1859484Sgarrett.damore@Sun.COM static int
cnv_from_s16ne(audio_stream_t * sp,int len)1869484Sgarrett.damore@Sun.COM cnv_from_s16ne(audio_stream_t *sp, int len)
1879484Sgarrett.damore@Sun.COM {
1889484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
1899484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
1909484Sgarrett.damore@Sun.COM int16_t *src = s;
1919484Sgarrett.damore@Sun.COM int32_t *dst = d;
1929484Sgarrett.damore@Sun.COM
1939484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
1949484Sgarrett.damore@Sun.COM *dst++ = (*src++) << 8;
1959484Sgarrett.damore@Sun.COM
1969484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
1979484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
1989484Sgarrett.damore@Sun.COM return (len);
1999484Sgarrett.damore@Sun.COM }
2009484Sgarrett.damore@Sun.COM
2019484Sgarrett.damore@Sun.COM static int
cnv_from_s16oe(audio_stream_t * sp,int len)2029484Sgarrett.damore@Sun.COM cnv_from_s16oe(audio_stream_t *sp, int len)
2039484Sgarrett.damore@Sun.COM {
2049484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
2059484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
2069484Sgarrett.damore@Sun.COM int16_t *src = s;
2079484Sgarrett.damore@Sun.COM int32_t *dst = d;
2089484Sgarrett.damore@Sun.COM
2099484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
2109484Sgarrett.damore@Sun.COM *dst++ = (int16_t)(ddi_swap16(*src++)) << 8;
2119484Sgarrett.damore@Sun.COM
2129484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
2139484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
2149484Sgarrett.damore@Sun.COM return (len);
2159484Sgarrett.damore@Sun.COM }
2169484Sgarrett.damore@Sun.COM
2179484Sgarrett.damore@Sun.COM static int
cnv_from_u16ne(audio_stream_t * sp,int len)2189484Sgarrett.damore@Sun.COM cnv_from_u16ne(audio_stream_t *sp, int len)
2199484Sgarrett.damore@Sun.COM {
2209484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
2219484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
2229484Sgarrett.damore@Sun.COM uint16_t *src = s;
2239484Sgarrett.damore@Sun.COM int32_t *dst = d;
2249484Sgarrett.damore@Sun.COM
2259484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
2269484Sgarrett.damore@Sun.COM *dst++ = (int16_t)((*src++) ^ 0x8000) << 8;
2279484Sgarrett.damore@Sun.COM
2289484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
2299484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
2309484Sgarrett.damore@Sun.COM return (len);
2319484Sgarrett.damore@Sun.COM }
2329484Sgarrett.damore@Sun.COM
2339484Sgarrett.damore@Sun.COM static int
cnv_from_u16oe(audio_stream_t * sp,int len)2349484Sgarrett.damore@Sun.COM cnv_from_u16oe(audio_stream_t *sp, int len)
2359484Sgarrett.damore@Sun.COM {
2369484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
2379484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
2389484Sgarrett.damore@Sun.COM uint16_t *src = s;
2399484Sgarrett.damore@Sun.COM int32_t *dst = d;
2409484Sgarrett.damore@Sun.COM
2419484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--)
2429484Sgarrett.damore@Sun.COM *dst++ = (int16_t)(ddi_swap16((*src++) ^ 0x8000)) << 8;
2439484Sgarrett.damore@Sun.COM
2449484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
2459484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
2469484Sgarrett.damore@Sun.COM return (len);
2479484Sgarrett.damore@Sun.COM }
2489484Sgarrett.damore@Sun.COM
2499484Sgarrett.damore@Sun.COM static int
cnv_from_s24p(audio_stream_t * sp,int len)2509484Sgarrett.damore@Sun.COM cnv_from_s24p(audio_stream_t *sp, int len)
2519484Sgarrett.damore@Sun.COM {
2529484Sgarrett.damore@Sun.COM void *s = sp->s_cnv_src;
2539484Sgarrett.damore@Sun.COM void *d = sp->s_cnv_dst;
2549484Sgarrett.damore@Sun.COM uint8_t *src = s;
2559484Sgarrett.damore@Sun.COM int32_t *dst = d;
2569484Sgarrett.damore@Sun.COM int32_t tmp;
2579484Sgarrett.damore@Sun.COM
2589484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--) {
2599484Sgarrett.damore@Sun.COM /* NB: this is a little endian format */
2609484Sgarrett.damore@Sun.COM tmp = (*src++);
2619484Sgarrett.damore@Sun.COM tmp |= (*src++) << 8;
2629484Sgarrett.damore@Sun.COM tmp |= (*src++) << 16;
2639484Sgarrett.damore@Sun.COM *dst++ = tmp;
2649484Sgarrett.damore@Sun.COM }
2659484Sgarrett.damore@Sun.COM
2669484Sgarrett.damore@Sun.COM sp->s_cnv_src = d;
2679484Sgarrett.damore@Sun.COM sp->s_cnv_dst = s;
2689484Sgarrett.damore@Sun.COM return (len);
2699484Sgarrett.damore@Sun.COM }
2709484Sgarrett.damore@Sun.COM
2719484Sgarrett.damore@Sun.COM static int
cnv_from_s32ne(audio_stream_t * sp,int len)2729484Sgarrett.damore@Sun.COM cnv_from_s32ne(audio_stream_t *sp, int len)
2739484Sgarrett.damore@Sun.COM {
2749484Sgarrett.damore@Sun.COM /* 32-bit conversions can be done in place */
2759484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
2769484Sgarrett.damore@Sun.COM
2779484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--, src++)
2789484Sgarrett.damore@Sun.COM *src = *src >> 8;
2799484Sgarrett.damore@Sun.COM
2809484Sgarrett.damore@Sun.COM return (len);
2819484Sgarrett.damore@Sun.COM }
2829484Sgarrett.damore@Sun.COM
2839484Sgarrett.damore@Sun.COM static int
cnv_from_s32oe(audio_stream_t * sp,int len)2849484Sgarrett.damore@Sun.COM cnv_from_s32oe(audio_stream_t *sp, int len)
2859484Sgarrett.damore@Sun.COM {
2869484Sgarrett.damore@Sun.COM /* 32-bit conversions can be done in place */
2879484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
2889484Sgarrett.damore@Sun.COM
2899484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_src_nchan; i; i--, src++)
2909484Sgarrett.damore@Sun.COM *src = (int32_t)(ddi_swap32(*src)) >> 8;
2919484Sgarrett.damore@Sun.COM
2929484Sgarrett.damore@Sun.COM return (len);
2939484Sgarrett.damore@Sun.COM }
2949484Sgarrett.damore@Sun.COM
2959484Sgarrett.damore@Sun.COM /*
2969484Sgarrett.damore@Sun.COM * NB: All the destination format conversions use the same or fewer
2979484Sgarrett.damore@Sun.COM * bytes as the 24-bit unpacked (32-bits used per sample), so we can
2989484Sgarrett.damore@Sun.COM * convert all of them in place.
2999484Sgarrett.damore@Sun.COM */
3009484Sgarrett.damore@Sun.COM
3019484Sgarrett.damore@Sun.COM static int
cnv_to_u8(audio_stream_t * sp,int len)3029484Sgarrett.damore@Sun.COM cnv_to_u8(audio_stream_t *sp, int len)
3039484Sgarrett.damore@Sun.COM {
3049484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3059484Sgarrett.damore@Sun.COM uint8_t *dst = (void *)src;
3069484Sgarrett.damore@Sun.COM
3079484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
3089484Sgarrett.damore@Sun.COM *dst++ = (*src++ >> 16) ^ 0x80;
3099484Sgarrett.damore@Sun.COM
3109484Sgarrett.damore@Sun.COM return (len);
3119484Sgarrett.damore@Sun.COM }
3129484Sgarrett.damore@Sun.COM
3139484Sgarrett.damore@Sun.COM static int
cnv_to_s8(audio_stream_t * sp,int len)3149484Sgarrett.damore@Sun.COM cnv_to_s8(audio_stream_t *sp, int len)
3159484Sgarrett.damore@Sun.COM {
3169484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3179484Sgarrett.damore@Sun.COM int8_t *dst = (void *)src;
3189484Sgarrett.damore@Sun.COM
3199484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
3209484Sgarrett.damore@Sun.COM *dst++ = *src++ >> 16;
3219484Sgarrett.damore@Sun.COM
3229484Sgarrett.damore@Sun.COM return (len);
3239484Sgarrett.damore@Sun.COM }
3249484Sgarrett.damore@Sun.COM
3259484Sgarrett.damore@Sun.COM static int
cnv_to_ulaw(audio_stream_t * sp,int len)3269484Sgarrett.damore@Sun.COM cnv_to_ulaw(audio_stream_t *sp, int len)
3279484Sgarrett.damore@Sun.COM {
3289484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3299484Sgarrett.damore@Sun.COM uint8_t *dst = (void *)src;
3309484Sgarrett.damore@Sun.COM
3319484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
3329484Sgarrett.damore@Sun.COM int idx = *src++;
3339484Sgarrett.damore@Sun.COM idx >>= 10;
3349484Sgarrett.damore@Sun.COM idx += G711_ULAW_MIDPOINT;
3359484Sgarrett.damore@Sun.COM idx &= 0x3fff; /* safety precaution */
3369484Sgarrett.damore@Sun.COM *dst++ = _14linear2ulaw8[idx];
3379484Sgarrett.damore@Sun.COM }
3389484Sgarrett.damore@Sun.COM
3399484Sgarrett.damore@Sun.COM return (len);
3409484Sgarrett.damore@Sun.COM }
3419484Sgarrett.damore@Sun.COM
3429484Sgarrett.damore@Sun.COM static int
cnv_to_alaw(audio_stream_t * sp,int len)3439484Sgarrett.damore@Sun.COM cnv_to_alaw(audio_stream_t *sp, int len)
3449484Sgarrett.damore@Sun.COM {
3459484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3469484Sgarrett.damore@Sun.COM uint8_t *dst = (void *)src;
3479484Sgarrett.damore@Sun.COM
3489484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
3499484Sgarrett.damore@Sun.COM int idx = *src++;
3509484Sgarrett.damore@Sun.COM idx >>= 11;
3519484Sgarrett.damore@Sun.COM idx += G711_ALAW_MIDPOINT;
3529484Sgarrett.damore@Sun.COM idx &= 0x1fff; /* safety precaution */
3539484Sgarrett.damore@Sun.COM *dst++ = _13linear2alaw8[idx];
3549484Sgarrett.damore@Sun.COM }
3559484Sgarrett.damore@Sun.COM
3569484Sgarrett.damore@Sun.COM return (len);
3579484Sgarrett.damore@Sun.COM }
3589484Sgarrett.damore@Sun.COM
3599484Sgarrett.damore@Sun.COM static int
cnv_to_s16ne(audio_stream_t * sp,int len)3609484Sgarrett.damore@Sun.COM cnv_to_s16ne(audio_stream_t *sp, int len)
3619484Sgarrett.damore@Sun.COM {
3629484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3639484Sgarrett.damore@Sun.COM int16_t *dst = (void *)src;
3649484Sgarrett.damore@Sun.COM
3659484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
3669484Sgarrett.damore@Sun.COM *dst++ = *src++ >> 8;
3679484Sgarrett.damore@Sun.COM
3689484Sgarrett.damore@Sun.COM return (len);
3699484Sgarrett.damore@Sun.COM }
3709484Sgarrett.damore@Sun.COM
3719484Sgarrett.damore@Sun.COM static int
cnv_to_s16oe(audio_stream_t * sp,int len)3729484Sgarrett.damore@Sun.COM cnv_to_s16oe(audio_stream_t *sp, int len)
3739484Sgarrett.damore@Sun.COM {
3749484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3759484Sgarrett.damore@Sun.COM int16_t *dst = (void *)src;
3769484Sgarrett.damore@Sun.COM
3779484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
3789484Sgarrett.damore@Sun.COM *dst++ = ddi_swap16(*src++ >> 8);
3799484Sgarrett.damore@Sun.COM
3809484Sgarrett.damore@Sun.COM return (len);
3819484Sgarrett.damore@Sun.COM }
3829484Sgarrett.damore@Sun.COM
3839484Sgarrett.damore@Sun.COM static int
cnv_to_u16ne(audio_stream_t * sp,int len)3849484Sgarrett.damore@Sun.COM cnv_to_u16ne(audio_stream_t *sp, int len)
3859484Sgarrett.damore@Sun.COM {
3869484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3879484Sgarrett.damore@Sun.COM uint16_t *dst = (void *)src;
3889484Sgarrett.damore@Sun.COM
3899484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
3909484Sgarrett.damore@Sun.COM *dst++ = (*src++ >> 8) ^ 0x8000;
3919484Sgarrett.damore@Sun.COM
3929484Sgarrett.damore@Sun.COM return (len);
3939484Sgarrett.damore@Sun.COM }
3949484Sgarrett.damore@Sun.COM
3959484Sgarrett.damore@Sun.COM static int
cnv_to_u16oe(audio_stream_t * sp,int len)3969484Sgarrett.damore@Sun.COM cnv_to_u16oe(audio_stream_t *sp, int len)
3979484Sgarrett.damore@Sun.COM {
3989484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
3999484Sgarrett.damore@Sun.COM uint16_t *dst = (void *)src;
4009484Sgarrett.damore@Sun.COM
4019484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--)
4029484Sgarrett.damore@Sun.COM *dst++ = ddi_swap16(*src++ >> 8) ^ 0x8000;
4039484Sgarrett.damore@Sun.COM
4049484Sgarrett.damore@Sun.COM return (len);
4059484Sgarrett.damore@Sun.COM }
4069484Sgarrett.damore@Sun.COM
4079484Sgarrett.damore@Sun.COM static int
cnv_to_s24p(audio_stream_t * sp,int len)4089484Sgarrett.damore@Sun.COM cnv_to_s24p(audio_stream_t *sp, int len)
4099484Sgarrett.damore@Sun.COM {
4109484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
4119484Sgarrett.damore@Sun.COM uint8_t *dst = (void *)src;
4129484Sgarrett.damore@Sun.COM int32_t d;
4139484Sgarrett.damore@Sun.COM
4149484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--) {
4159484Sgarrett.damore@Sun.COM /* NB: this is a little endian format */
4169484Sgarrett.damore@Sun.COM d = *src++;
4179484Sgarrett.damore@Sun.COM *dst++ = d & 0xff;
4189484Sgarrett.damore@Sun.COM *dst++ = (d >> 8) & 0xff;
4199484Sgarrett.damore@Sun.COM *dst++ = (d >> 16) & 0xff;
4209484Sgarrett.damore@Sun.COM }
4219484Sgarrett.damore@Sun.COM
4229484Sgarrett.damore@Sun.COM return (len);
4239484Sgarrett.damore@Sun.COM }
4249484Sgarrett.damore@Sun.COM
4259484Sgarrett.damore@Sun.COM static int
cnv_to_s32ne(audio_stream_t * sp,int len)4269484Sgarrett.damore@Sun.COM cnv_to_s32ne(audio_stream_t *sp, int len)
4279484Sgarrett.damore@Sun.COM {
4289484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
4299484Sgarrett.damore@Sun.COM
4309484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--, src++)
4319484Sgarrett.damore@Sun.COM *src = *src << 8;
4329484Sgarrett.damore@Sun.COM
4339484Sgarrett.damore@Sun.COM return (len);
4349484Sgarrett.damore@Sun.COM }
4359484Sgarrett.damore@Sun.COM
4369484Sgarrett.damore@Sun.COM static int
cnv_to_s32oe(audio_stream_t * sp,int len)4379484Sgarrett.damore@Sun.COM cnv_to_s32oe(audio_stream_t *sp, int len)
4389484Sgarrett.damore@Sun.COM {
4399484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
4409484Sgarrett.damore@Sun.COM
4419484Sgarrett.damore@Sun.COM for (int i = len * sp->s_cnv_dst_nchan; i; i--, src++)
4429484Sgarrett.damore@Sun.COM *src = ddi_swap32(*src << 8);
4439484Sgarrett.damore@Sun.COM
4449484Sgarrett.damore@Sun.COM return (len);
4459484Sgarrett.damore@Sun.COM }
4469484Sgarrett.damore@Sun.COM
4479484Sgarrett.damore@Sun.COM static int
cnv_default(audio_stream_t * sp,int len)4489484Sgarrett.damore@Sun.COM cnv_default(audio_stream_t *sp, int len)
4499484Sgarrett.damore@Sun.COM {
4509484Sgarrett.damore@Sun.COM /*
4519484Sgarrett.damore@Sun.COM * Note that the formats were already preverified during
4529484Sgarrett.damore@Sun.COM * select_converter, to ensure that only supported formats are
4539484Sgarrett.damore@Sun.COM * used.
4549484Sgarrett.damore@Sun.COM */
4559484Sgarrett.damore@Sun.COM
4569484Sgarrett.damore@Sun.COM /*
4579484Sgarrett.damore@Sun.COM * Convert samples to 24 bit (32 bit lsb aligned) if
4589484Sgarrett.damore@Sun.COM * necessary.
4599484Sgarrett.damore@Sun.COM */
4609484Sgarrett.damore@Sun.COM
4619484Sgarrett.damore@Sun.COM switch (sp->s_cnv_src_format) {
4629484Sgarrett.damore@Sun.COM
4639484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U8:
4649484Sgarrett.damore@Sun.COM len = cnv_from_u8(sp, len);
4659484Sgarrett.damore@Sun.COM break;
4669484Sgarrett.damore@Sun.COM
4679484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S8:
4689484Sgarrett.damore@Sun.COM len = cnv_from_s8(sp, len);
4699484Sgarrett.damore@Sun.COM break;
4709484Sgarrett.damore@Sun.COM
4719484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_ULAW:
4729484Sgarrett.damore@Sun.COM len = cnv_from_ulaw(sp, len);
4739484Sgarrett.damore@Sun.COM break;
4749484Sgarrett.damore@Sun.COM
4759484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_ALAW:
4769484Sgarrett.damore@Sun.COM len = cnv_from_alaw(sp, len);
4779484Sgarrett.damore@Sun.COM break;
4789484Sgarrett.damore@Sun.COM
4799484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S16_NE:
4809484Sgarrett.damore@Sun.COM len = cnv_from_s16ne(sp, len);
4819484Sgarrett.damore@Sun.COM break;
4829484Sgarrett.damore@Sun.COM
4839484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S16_OE:
4849484Sgarrett.damore@Sun.COM len = cnv_from_s16oe(sp, len);
4859484Sgarrett.damore@Sun.COM break;
4869484Sgarrett.damore@Sun.COM
4879484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U16_NE:
4889484Sgarrett.damore@Sun.COM len = cnv_from_u16ne(sp, len);
4899484Sgarrett.damore@Sun.COM break;
4909484Sgarrett.damore@Sun.COM
4919484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U16_OE:
4929484Sgarrett.damore@Sun.COM len = cnv_from_u16oe(sp, len);
4939484Sgarrett.damore@Sun.COM break;
4949484Sgarrett.damore@Sun.COM
4959484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S32_NE:
4969484Sgarrett.damore@Sun.COM len = cnv_from_s32ne(sp, len);
4979484Sgarrett.damore@Sun.COM break;
4989484Sgarrett.damore@Sun.COM
4999484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S32_OE:
5009484Sgarrett.damore@Sun.COM len = cnv_from_s32oe(sp, len);
5019484Sgarrett.damore@Sun.COM break;
5029484Sgarrett.damore@Sun.COM
5039484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S24_OE:
5049484Sgarrett.damore@Sun.COM len = cnv_s24oe(sp, len);
5059484Sgarrett.damore@Sun.COM break;
5069484Sgarrett.damore@Sun.COM
5079484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S24_PACKED:
5089484Sgarrett.damore@Sun.COM len = cnv_from_s24p(sp, len);
5099484Sgarrett.damore@Sun.COM break;
5109484Sgarrett.damore@Sun.COM }
5119484Sgarrett.damore@Sun.COM
5129484Sgarrett.damore@Sun.COM /*
5139484Sgarrett.damore@Sun.COM * If we aren't decreasing the number of channels, then do the
5149484Sgarrett.damore@Sun.COM * SRC now. (We prefer to do SRC on the smaller number of channels.)
5159484Sgarrett.damore@Sun.COM */
5169484Sgarrett.damore@Sun.COM if (sp->s_cnv_src_rate != sp->s_cnv_dst_rate &&
5179484Sgarrett.damore@Sun.COM sp->s_cnv_src_nchan <= sp->s_cnv_dst_nchan) {
5189484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
5199484Sgarrett.damore@Sun.COM int32_t *dst = sp->s_cnv_dst;
5209484Sgarrett.damore@Sun.COM
5219484Sgarrett.damore@Sun.COM len = do_src(sp, src, dst, len, sp->s_cnv_src_nchan);
5229484Sgarrett.damore@Sun.COM
5239484Sgarrett.damore@Sun.COM sp->s_cnv_src = dst;
5249484Sgarrett.damore@Sun.COM sp->s_cnv_dst = src;
5259484Sgarrett.damore@Sun.COM }
5269484Sgarrett.damore@Sun.COM
5279484Sgarrett.damore@Sun.COM /*
5289484Sgarrett.damore@Sun.COM * Convert between mono and stereo
5299484Sgarrett.damore@Sun.COM */
5309484Sgarrett.damore@Sun.COM
5319484Sgarrett.damore@Sun.COM if (sp->s_cnv_src_nchan != sp->s_cnv_dst_nchan) {
5329484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
5339484Sgarrett.damore@Sun.COM int32_t *dst = sp->s_cnv_dst;
5349484Sgarrett.damore@Sun.COM int tc = sp->s_cnv_dst_nchan;
5359484Sgarrett.damore@Sun.COM int sc = sp->s_cnv_src_nchan;
5369484Sgarrett.damore@Sun.COM int nc;
5379484Sgarrett.damore@Sun.COM int i;
5389484Sgarrett.damore@Sun.COM
5399484Sgarrett.damore@Sun.COM sp->s_cnv_src = dst;
5409484Sgarrett.damore@Sun.COM sp->s_cnv_dst = src;
5419484Sgarrett.damore@Sun.COM
5429484Sgarrett.damore@Sun.COM if (sc == 1) {
5439484Sgarrett.damore@Sun.COM /*
5449484Sgarrett.damore@Sun.COM * Mono expansion. We expand into the stereo
5459484Sgarrett.damore@Sun.COM * channel, and leave other channels silent.
5469484Sgarrett.damore@Sun.COM */
5479484Sgarrett.damore@Sun.COM for (i = len; i; i--) {
5489484Sgarrett.damore@Sun.COM *dst++ = *src;
5499484Sgarrett.damore@Sun.COM *dst++ = *src++;
5509484Sgarrett.damore@Sun.COM for (int j = tc - 2; j > 0; j--) {
5519484Sgarrett.damore@Sun.COM *dst++ = 0;
5529484Sgarrett.damore@Sun.COM }
5539484Sgarrett.damore@Sun.COM
5549484Sgarrett.damore@Sun.COM }
5559484Sgarrett.damore@Sun.COM
5569484Sgarrett.damore@Sun.COM } else if (sc == 2 && tc == 1) {
5579484Sgarrett.damore@Sun.COM /*
5589484Sgarrett.damore@Sun.COM * Stereo -> mono. We do stereo separately to make
5599484Sgarrett.damore@Sun.COM * the division fast (div by const 2 is just shift).
5609484Sgarrett.damore@Sun.COM */
5619484Sgarrett.damore@Sun.COM for (i = len; i; i--) {
5629484Sgarrett.damore@Sun.COM /*
5639484Sgarrett.damore@Sun.COM * Take just the left channel sample,
5649484Sgarrett.damore@Sun.COM * discard the right channel.
5659484Sgarrett.damore@Sun.COM */
5669484Sgarrett.damore@Sun.COM *dst++ = *src++; /* left */
5679484Sgarrett.damore@Sun.COM src++; /* right */
5689484Sgarrett.damore@Sun.COM }
5699484Sgarrett.damore@Sun.COM } else {
5709484Sgarrett.damore@Sun.COM /*
5719484Sgarrett.damore@Sun.COM * Multi channel conversions. We just copy the
5729484Sgarrett.damore@Sun.COM * minimum number of channels.
5739484Sgarrett.damore@Sun.COM */
5749484Sgarrett.damore@Sun.COM
5759484Sgarrett.damore@Sun.COM /* Calculate number of frames */
5769484Sgarrett.damore@Sun.COM
5779484Sgarrett.damore@Sun.COM nc = min(sc, tc);
5789484Sgarrett.damore@Sun.COM
5799484Sgarrett.damore@Sun.COM /* Clear destination */
5809484Sgarrett.damore@Sun.COM bzero(dst, (len * tc * sizeof (int32_t)));
5819484Sgarrett.damore@Sun.COM
5829484Sgarrett.damore@Sun.COM for (i = len; i; i--) {
5839484Sgarrett.damore@Sun.COM int c;
5849484Sgarrett.damore@Sun.COM
5859484Sgarrett.damore@Sun.COM for (c = 0; c < nc; c++)
5869484Sgarrett.damore@Sun.COM dst[c] = src[c];
5879484Sgarrett.damore@Sun.COM
5889484Sgarrett.damore@Sun.COM src += sc;
5899484Sgarrett.damore@Sun.COM dst += tc;
5909484Sgarrett.damore@Sun.COM }
5919484Sgarrett.damore@Sun.COM }
5929484Sgarrett.damore@Sun.COM }
5939484Sgarrett.damore@Sun.COM
5949484Sgarrett.damore@Sun.COM /*
5959484Sgarrett.damore@Sun.COM * If we didn't do SRC pre-conversion, then do it now.
5969484Sgarrett.damore@Sun.COM */
5979484Sgarrett.damore@Sun.COM if (sp->s_cnv_src_rate != sp->s_cnv_dst_rate &&
5989484Sgarrett.damore@Sun.COM sp->s_cnv_src_nchan > sp->s_cnv_dst_nchan) {
5999484Sgarrett.damore@Sun.COM
6009484Sgarrett.damore@Sun.COM int32_t *src = sp->s_cnv_src;
6019484Sgarrett.damore@Sun.COM int32_t *dst = sp->s_cnv_dst;
6029484Sgarrett.damore@Sun.COM
6039484Sgarrett.damore@Sun.COM len = do_src(sp, src, dst, len, sp->s_cnv_dst_nchan);
6049484Sgarrett.damore@Sun.COM
6059484Sgarrett.damore@Sun.COM sp->s_cnv_src = dst;
6069484Sgarrett.damore@Sun.COM sp->s_cnv_dst = src;
6079484Sgarrett.damore@Sun.COM }
6089484Sgarrett.damore@Sun.COM
6099484Sgarrett.damore@Sun.COM /*
6109484Sgarrett.damore@Sun.COM * Finally convert samples from internal 24 bit format to target format
6119484Sgarrett.damore@Sun.COM */
6129484Sgarrett.damore@Sun.COM
6139484Sgarrett.damore@Sun.COM switch (sp->s_cnv_dst_format) {
6149484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U8:
6159484Sgarrett.damore@Sun.COM len = cnv_to_u8(sp, len);
6169484Sgarrett.damore@Sun.COM break;
6179484Sgarrett.damore@Sun.COM
6189484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S8:
6199484Sgarrett.damore@Sun.COM len = cnv_to_s8(sp, len);
6209484Sgarrett.damore@Sun.COM break;
6219484Sgarrett.damore@Sun.COM
6229484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S16_NE:
6239484Sgarrett.damore@Sun.COM len = cnv_to_s16ne(sp, len);
6249484Sgarrett.damore@Sun.COM break;
6259484Sgarrett.damore@Sun.COM
6269484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S16_OE:
6279484Sgarrett.damore@Sun.COM len = cnv_to_s16oe(sp, len);
6289484Sgarrett.damore@Sun.COM break;
6299484Sgarrett.damore@Sun.COM
6309484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U16_NE:
6319484Sgarrett.damore@Sun.COM len = cnv_to_u16ne(sp, len);
6329484Sgarrett.damore@Sun.COM break;
6339484Sgarrett.damore@Sun.COM
6349484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_U16_OE:
6359484Sgarrett.damore@Sun.COM len = cnv_to_u16oe(sp, len);
6369484Sgarrett.damore@Sun.COM break;
6379484Sgarrett.damore@Sun.COM
6389484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S24_OE:
6399484Sgarrett.damore@Sun.COM len = cnv_s24oe(sp, len);
6409484Sgarrett.damore@Sun.COM break;
6419484Sgarrett.damore@Sun.COM
6429484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S24_PACKED:
6439484Sgarrett.damore@Sun.COM len = cnv_to_s24p(sp, len);
6449484Sgarrett.damore@Sun.COM break;
6459484Sgarrett.damore@Sun.COM
6469484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S32_NE:
6479484Sgarrett.damore@Sun.COM len = cnv_to_s32ne(sp, len);
6489484Sgarrett.damore@Sun.COM break;
6499484Sgarrett.damore@Sun.COM
6509484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_S32_OE:
6519484Sgarrett.damore@Sun.COM len = cnv_to_s32oe(sp, len);
6529484Sgarrett.damore@Sun.COM break;
6539484Sgarrett.damore@Sun.COM
6549484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_ULAW:
6559484Sgarrett.damore@Sun.COM len = cnv_to_ulaw(sp, len);
6569484Sgarrett.damore@Sun.COM break;
6579484Sgarrett.damore@Sun.COM
6589484Sgarrett.damore@Sun.COM case AUDIO_FORMAT_ALAW:
6599484Sgarrett.damore@Sun.COM len = cnv_to_alaw(sp, len);
6609484Sgarrett.damore@Sun.COM break;
6619484Sgarrett.damore@Sun.COM }
6629484Sgarrett.damore@Sun.COM
6639484Sgarrett.damore@Sun.COM return (len);
6649484Sgarrett.damore@Sun.COM }
6659484Sgarrett.damore@Sun.COM
6669484Sgarrett.damore@Sun.COM static const struct audio_format_info {
6679484Sgarrett.damore@Sun.COM unsigned format;
6689484Sgarrett.damore@Sun.COM int sampsize;
6699484Sgarrett.damore@Sun.COM audio_cnv_func_t from;
6709484Sgarrett.damore@Sun.COM audio_cnv_func_t to;
6719484Sgarrett.damore@Sun.COM } audio_format_info[] = {
6729484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S8, 1, cnv_from_s8, cnv_to_s8 },
6739484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_U8, 1, cnv_from_u8, cnv_to_u8 },
6749484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_ULAW, 1, cnv_from_ulaw, cnv_to_ulaw },
6759484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_ALAW, 1, cnv_from_alaw, cnv_to_alaw },
6769484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S16_NE, 2, cnv_from_s16ne, cnv_to_s16ne },
6779484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S16_OE, 2, cnv_from_s16oe, cnv_to_s16oe },
6789484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_U16_NE, 2, cnv_from_u16ne, cnv_to_u16ne },
6799484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_U16_OE, 2, cnv_from_u16oe, cnv_to_u16oe },
6809484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S32_NE, 4, cnv_from_s32ne, cnv_to_s32ne },
6819484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S32_OE, 4, cnv_from_s32oe, cnv_to_s32oe },
6829484Sgarrett.damore@Sun.COM
6839484Sgarrett.damore@Sun.COM /* 24-bit formats are "special" */
6849484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S24_NE, 4, NULL, NULL },
6859484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S24_OE, 4, cnv_s24oe, cnv_s24oe },
6869484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_S24_PACKED, 3, cnv_from_s24p, cnv_to_s24p },
6879484Sgarrett.damore@Sun.COM
6889484Sgarrett.damore@Sun.COM /* sentinel */
6899484Sgarrett.damore@Sun.COM { AUDIO_FORMAT_NONE, 0, NULL, NULL }
6909484Sgarrett.damore@Sun.COM };
6919484Sgarrett.damore@Sun.COM
6929484Sgarrett.damore@Sun.COM int
auimpl_format_setup(audio_stream_t * sp,audio_parms_t * parms,uint_t mask)693*12165Sgdamore@opensolaris.org auimpl_format_setup(audio_stream_t *sp, audio_parms_t *parms, uint_t mask)
6949484Sgarrett.damore@Sun.COM {
695*12165Sgdamore@opensolaris.org audio_parms_t source;
696*12165Sgdamore@opensolaris.org audio_parms_t target;
697*12165Sgdamore@opensolaris.org audio_parms_t *uparms;
6989484Sgarrett.damore@Sun.COM audio_cnv_func_t converter = NULL;
6999484Sgarrett.damore@Sun.COM const struct audio_format_info *info;
7009484Sgarrett.damore@Sun.COM int expand = AUDIO_UNIT_EXPAND;
7019484Sgarrett.damore@Sun.COM unsigned cnv_sampsz = sizeof (uint32_t);
7029484Sgarrett.damore@Sun.COM unsigned cnv_max;
703*12165Sgdamore@opensolaris.org boolean_t needsrc = B_FALSE;
704*12165Sgdamore@opensolaris.org
705*12165Sgdamore@opensolaris.org uint_t framesz;
706*12165Sgdamore@opensolaris.org uint_t fragfr;
707*12165Sgdamore@opensolaris.org uint_t fragbytes;
708*12165Sgdamore@opensolaris.org uint_t nfrags;
709*12165Sgdamore@opensolaris.org
710*12165Sgdamore@opensolaris.org ASSERT(mutex_owned(&sp->s_lock));
711*12165Sgdamore@opensolaris.org
712*12165Sgdamore@opensolaris.org source = sp->s_cnv_src_parms;
713*12165Sgdamore@opensolaris.org target = sp->s_cnv_dst_parms;
7149484Sgarrett.damore@Sun.COM
7159484Sgarrett.damore@Sun.COM if (sp == &sp->s_client->c_ostream) {
716*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_FMT)
717*12165Sgdamore@opensolaris.org source.p_format = parms->p_format;
718*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_RATE)
719*12165Sgdamore@opensolaris.org source.p_rate = parms->p_rate;
720*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_CHAN)
721*12165Sgdamore@opensolaris.org source.p_nchan = parms->p_nchan;
722*12165Sgdamore@opensolaris.org uparms = &source;
7239484Sgarrett.damore@Sun.COM } else {
724*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_FMT)
725*12165Sgdamore@opensolaris.org target.p_format = parms->p_format;
726*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_RATE)
727*12165Sgdamore@opensolaris.org target.p_rate = parms->p_rate;
728*12165Sgdamore@opensolaris.org if (mask & FORMAT_MSK_CHAN)
729*12165Sgdamore@opensolaris.org target.p_nchan = parms->p_nchan;
730*12165Sgdamore@opensolaris.org uparms = ⌖
7319484Sgarrett.damore@Sun.COM }
7329484Sgarrett.damore@Sun.COM
7339484Sgarrett.damore@Sun.COM /*
7349484Sgarrett.damore@Sun.COM * At least one of the source or target are S24_NE.
7359484Sgarrett.damore@Sun.COM *
7369484Sgarrett.damore@Sun.COM * If we have a signed/native endian format, then pick an
7379484Sgarrett.damore@Sun.COM * optimized converter. While at it, ensure that a valid
7389484Sgarrett.damore@Sun.COM * format is selected.
739*12165Sgdamore@opensolaris.org *
740*12165Sgdamore@opensolaris.org * After this function executes, "info" will point to the
741*12165Sgdamore@opensolaris.org * format information for the user parameters.
7429484Sgarrett.damore@Sun.COM */
743*12165Sgdamore@opensolaris.org if (source.p_format != AUDIO_FORMAT_S24_NE) {
7449484Sgarrett.damore@Sun.COM for (info = &audio_format_info[0]; info->sampsize; info++) {
745*12165Sgdamore@opensolaris.org if (source.p_format == info->format) {
7469484Sgarrett.damore@Sun.COM converter = info->from;
7479484Sgarrett.damore@Sun.COM expand *= sizeof (int32_t);
7489484Sgarrett.damore@Sun.COM expand /= info->sampsize;
7499484Sgarrett.damore@Sun.COM /* save source frame size */
7509484Sgarrett.damore@Sun.COM cnv_sampsz = info->sampsize;
7519484Sgarrett.damore@Sun.COM break;
7529484Sgarrett.damore@Sun.COM }
7539484Sgarrett.damore@Sun.COM }
754*12165Sgdamore@opensolaris.org } else {
755*12165Sgdamore@opensolaris.org /*
756*12165Sgdamore@opensolaris.org * Target format. Note that this case is also taken
757*12165Sgdamore@opensolaris.org * if we're operating on S24_NE data. In that case
758*12165Sgdamore@opensolaris.org * the converter will be NULL and expand will not be
759*12165Sgdamore@opensolaris.org * altered.
760*12165Sgdamore@opensolaris.org */
7619484Sgarrett.damore@Sun.COM for (info = &audio_format_info[0]; info->sampsize; info++) {
762*12165Sgdamore@opensolaris.org if (target.p_format == info->format) {
7639484Sgarrett.damore@Sun.COM converter = info->to;
7649484Sgarrett.damore@Sun.COM expand *= info->sampsize;
7659484Sgarrett.damore@Sun.COM expand /= sizeof (int32_t);
7669484Sgarrett.damore@Sun.COM break;
7679484Sgarrett.damore@Sun.COM }
7689484Sgarrett.damore@Sun.COM }
769*12165Sgdamore@opensolaris.org }
770*12165Sgdamore@opensolaris.org if (info->format == AUDIO_FORMAT_NONE) {
771*12165Sgdamore@opensolaris.org audio_dev_warn(sp->s_client->c_dev, "invalid format selected");
772*12165Sgdamore@opensolaris.org return (EINVAL);
7739484Sgarrett.damore@Sun.COM }
7749484Sgarrett.damore@Sun.COM
775*12165Sgdamore@opensolaris.org
776*12165Sgdamore@opensolaris.org ASSERT(info->sampsize);
777*12165Sgdamore@opensolaris.org
778*12165Sgdamore@opensolaris.org if (source.p_nchan != target.p_nchan) {
7799484Sgarrett.damore@Sun.COM /*
7809484Sgarrett.damore@Sun.COM * if channels need conversion, then we must use the
7819484Sgarrett.damore@Sun.COM * default.
7829484Sgarrett.damore@Sun.COM */
7839484Sgarrett.damore@Sun.COM converter = cnv_default;
784*12165Sgdamore@opensolaris.org expand *= target.p_nchan;
785*12165Sgdamore@opensolaris.org expand /= source.p_nchan;
7869484Sgarrett.damore@Sun.COM }
7879484Sgarrett.damore@Sun.COM
788*12165Sgdamore@opensolaris.org if (source.p_rate != target.p_rate) {
789*12165Sgdamore@opensolaris.org needsrc = B_TRUE;
7909484Sgarrett.damore@Sun.COM converter = (converter == NULL) ? cnv_srconly : cnv_default;
7919484Sgarrett.damore@Sun.COM
792*12165Sgdamore@opensolaris.org expand *= target.p_rate;
793*12165Sgdamore@opensolaris.org expand /= source.p_rate;
7949484Sgarrett.damore@Sun.COM }
7959484Sgarrett.damore@Sun.COM
7969484Sgarrett.damore@Sun.COM /*
7979484Sgarrett.damore@Sun.COM * Figure out the size of the conversion buffer we need. We
7989484Sgarrett.damore@Sun.COM * assume room for two full source fragments, which ought to
7999484Sgarrett.damore@Sun.COM * be enough, even with rounding errors.
8009484Sgarrett.damore@Sun.COM */
801*12165Sgdamore@opensolaris.org cnv_max = 2 * (source.p_rate / audio_intrhz) *
802*12165Sgdamore@opensolaris.org cnv_sampsz * source.p_nchan;
8039484Sgarrett.damore@Sun.COM
8049484Sgarrett.damore@Sun.COM /*
8059484Sgarrett.damore@Sun.COM * If the conversion will cause us to expand fragments, then
8069484Sgarrett.damore@Sun.COM * we need to increase cnv_max. Scale by AUDIO_UNIT_EXPAND to
8079484Sgarrett.damore@Sun.COM * avoid rouding errors or losing bits when doing reducing
8089484Sgarrett.damore@Sun.COM * conversions.
8099484Sgarrett.damore@Sun.COM */
8109484Sgarrett.damore@Sun.COM if (expand > AUDIO_UNIT_EXPAND) {
8119484Sgarrett.damore@Sun.COM cnv_max *= expand;
8129484Sgarrett.damore@Sun.COM cnv_max /= AUDIO_UNIT_EXPAND;
8139484Sgarrett.damore@Sun.COM }
8149484Sgarrett.damore@Sun.COM
815*12165Sgdamore@opensolaris.org framesz = info->sampsize * uparms->p_nchan;
816*12165Sgdamore@opensolaris.org fragfr = (uparms->p_rate / audio_intrhz);
817*12165Sgdamore@opensolaris.org fragbytes = fragfr * framesz;
818*12165Sgdamore@opensolaris.org
819*12165Sgdamore@opensolaris.org /*
820*12165Sgdamore@opensolaris.org * We need to "tune" the buffer and fragment counts for some
821*12165Sgdamore@opensolaris.org * uses... OSS applications may like to configure a low
822*12165Sgdamore@opensolaris.org * latency, and they rely upon write() to block to prevent too
823*12165Sgdamore@opensolaris.org * much data from being queued up.
824*12165Sgdamore@opensolaris.org */
825*12165Sgdamore@opensolaris.org if (sp->s_hintsz) {
826*12165Sgdamore@opensolaris.org nfrags = sp->s_hintsz / fragbytes;
827*12165Sgdamore@opensolaris.org } else if (sp->s_hintfrags) {
828*12165Sgdamore@opensolaris.org nfrags = sp->s_hintfrags;
829*12165Sgdamore@opensolaris.org } else {
830*12165Sgdamore@opensolaris.org nfrags = sp->s_allocsz / fragbytes;
831*12165Sgdamore@opensolaris.org }
832*12165Sgdamore@opensolaris.org
833*12165Sgdamore@opensolaris.org /*
834*12165Sgdamore@opensolaris.org * Now make sure that the hint works -- we need at least 2 fragments,
835*12165Sgdamore@opensolaris.org * and we need to fit within the room allocated to us.
836*12165Sgdamore@opensolaris.org */
837*12165Sgdamore@opensolaris.org if (nfrags < 2) {
838*12165Sgdamore@opensolaris.org nfrags = 2;
839*12165Sgdamore@opensolaris.org }
840*12165Sgdamore@opensolaris.org while ((nfrags * fragbytes) > sp->s_allocsz) {
841*12165Sgdamore@opensolaris.org nfrags--;
842*12165Sgdamore@opensolaris.org }
843*12165Sgdamore@opensolaris.org /* if the resulting configuration is invalid, note it */
844*12165Sgdamore@opensolaris.org if (nfrags < 2) {
845*12165Sgdamore@opensolaris.org return (EINVAL);
846*12165Sgdamore@opensolaris.org }
847*12165Sgdamore@opensolaris.org
8489484Sgarrett.damore@Sun.COM /*
8499484Sgarrett.damore@Sun.COM * Now we need to allocate space.
850*12165Sgdamore@opensolaris.org *
851*12165Sgdamore@opensolaris.org * NB: Once the allocation succeeds, we must not fail. We are
852*12165Sgdamore@opensolaris.org * modifying the the stream settings and these changes must be
853*12165Sgdamore@opensolaris.org * made atomically.
8549484Sgarrett.damore@Sun.COM */
8559484Sgarrett.damore@Sun.COM if (sp->s_cnv_max < cnv_max) {
8569484Sgarrett.damore@Sun.COM uint32_t *buf0, *buf1;
8579484Sgarrett.damore@Sun.COM
8589484Sgarrett.damore@Sun.COM buf0 = kmem_alloc(cnv_max, KM_NOSLEEP);
8599484Sgarrett.damore@Sun.COM buf1 = kmem_alloc(cnv_max, KM_NOSLEEP);
8609484Sgarrett.damore@Sun.COM if ((buf0 == NULL) || (buf1 == NULL)) {
8619484Sgarrett.damore@Sun.COM audio_dev_warn(sp->s_client->c_dev,
8629484Sgarrett.damore@Sun.COM "failed to allocate audio conversion buffer "
8639484Sgarrett.damore@Sun.COM "(%u bytes)", cnv_max);
8649484Sgarrett.damore@Sun.COM if (buf0)
8659484Sgarrett.damore@Sun.COM kmem_free(buf0, cnv_max);
8669484Sgarrett.damore@Sun.COM if (buf1)
8679484Sgarrett.damore@Sun.COM kmem_free(buf1, cnv_max);
8689484Sgarrett.damore@Sun.COM return (ENOMEM);
8699484Sgarrett.damore@Sun.COM }
8709484Sgarrett.damore@Sun.COM
8719484Sgarrett.damore@Sun.COM if (sp->s_cnv_buf0)
8729484Sgarrett.damore@Sun.COM kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
8739484Sgarrett.damore@Sun.COM if (sp->s_cnv_buf1)
8749484Sgarrett.damore@Sun.COM kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
8759484Sgarrett.damore@Sun.COM
8769484Sgarrett.damore@Sun.COM sp->s_cnv_buf0 = buf0;
8779484Sgarrett.damore@Sun.COM sp->s_cnv_buf1 = buf1;
8789484Sgarrett.damore@Sun.COM sp->s_cnv_max = cnv_max;
8799484Sgarrett.damore@Sun.COM }
8809484Sgarrett.damore@Sun.COM
881*12165Sgdamore@opensolaris.org /* Set up the SRC state if we will be using SRC. */
882*12165Sgdamore@opensolaris.org if (needsrc) {
883*12165Sgdamore@opensolaris.org setup_src(sp, source.p_rate, target.p_rate,
884*12165Sgdamore@opensolaris.org source.p_nchan, target.p_nchan);
8859558Sgdamore@opensolaris.org }
8869558Sgdamore@opensolaris.org
8879558Sgdamore@opensolaris.org
888*12165Sgdamore@opensolaris.org sp->s_framesz = framesz;
889*12165Sgdamore@opensolaris.org sp->s_fragfr = fragfr;
890*12165Sgdamore@opensolaris.org sp->s_fragbytes = fragbytes;
891*12165Sgdamore@opensolaris.org sp->s_nfrags = nfrags;
892*12165Sgdamore@opensolaris.org sp->s_nframes = nfrags * fragfr;
893*12165Sgdamore@opensolaris.org sp->s_nbytes = sp->s_nframes * framesz;
8949484Sgarrett.damore@Sun.COM *sp->s_user_parms = *uparms;
8959484Sgarrett.damore@Sun.COM sp->s_converter = converter;
8969484Sgarrett.damore@Sun.COM
8979484Sgarrett.damore@Sun.COM /*
8989484Sgarrett.damore@Sun.COM * Ensure that we toss any stale data -- probably wrong format.
8999484Sgarrett.damore@Sun.COM * Note that as a consequence of this, all of the offsets and
9009484Sgarrett.damore@Sun.COM * counters get reset. Clients should not rely on these values
9019484Sgarrett.damore@Sun.COM * being preserved when changing formats.
9029484Sgarrett.damore@Sun.COM *
9039484Sgarrett.damore@Sun.COM * Its critical that we reset the indices, in particular,
9049484Sgarrett.damore@Sun.COM * because not only will the data be the wrong format, but the
9059484Sgarrett.damore@Sun.COM * indices themselves are quite possibly going to be invalid.
9069484Sgarrett.damore@Sun.COM */
9079484Sgarrett.damore@Sun.COM sp->s_cnv_cnt = 0;
9089484Sgarrett.damore@Sun.COM sp->s_tail = sp->s_head = 0;
9099484Sgarrett.damore@Sun.COM sp->s_tidx = sp->s_hidx = 0;
9109484Sgarrett.damore@Sun.COM
9119484Sgarrett.damore@Sun.COM return (0);
9129484Sgarrett.damore@Sun.COM }
9139484Sgarrett.damore@Sun.COM
9149484Sgarrett.damore@Sun.COM int
auimpl_format_alloc(audio_stream_t * sp)9159484Sgarrett.damore@Sun.COM auimpl_format_alloc(audio_stream_t *sp)
9169484Sgarrett.damore@Sun.COM {
9179484Sgarrett.damore@Sun.COM int i;
9189484Sgarrett.damore@Sun.COM
9199484Sgarrett.damore@Sun.COM ASSERT(mutex_owned(&sp->s_lock));
9209484Sgarrett.damore@Sun.COM for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
9219484Sgarrett.damore@Sun.COM sp->s_src_state[i] =
9229484Sgarrett.damore@Sun.COM kmem_zalloc(sizeof (grc3state_t), KM_NOSLEEP);
9239484Sgarrett.damore@Sun.COM if (sp->s_src_state[i] == NULL) {
9249484Sgarrett.damore@Sun.COM audio_dev_warn(sp->s_client->c_dev,
9259484Sgarrett.damore@Sun.COM "unable to allocate SRC state structures");
9269484Sgarrett.damore@Sun.COM return (ENOMEM);
9279484Sgarrett.damore@Sun.COM }
9289484Sgarrett.damore@Sun.COM }
9299484Sgarrett.damore@Sun.COM return (0);
9309484Sgarrett.damore@Sun.COM }
9319484Sgarrett.damore@Sun.COM
9329484Sgarrett.damore@Sun.COM void
auimpl_format_free(audio_stream_t * sp)9339484Sgarrett.damore@Sun.COM auimpl_format_free(audio_stream_t *sp)
9349484Sgarrett.damore@Sun.COM {
9359484Sgarrett.damore@Sun.COM int i;
9369484Sgarrett.damore@Sun.COM
9379484Sgarrett.damore@Sun.COM for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
9389484Sgarrett.damore@Sun.COM if (sp->s_src_state[i] != NULL) {
9399484Sgarrett.damore@Sun.COM kmem_free(sp->s_src_state[i], sizeof (grc3state_t));
9409484Sgarrett.damore@Sun.COM sp->s_src_state[i] = NULL;
9419484Sgarrett.damore@Sun.COM }
9429484Sgarrett.damore@Sun.COM }
9439484Sgarrett.damore@Sun.COM }
944