/*- * Copyright (c) 2008 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "pcm.h" #ifndef AFMT_INTPCM #if BYTE_ORDER == LITTLE_ENDIAN #define AFMT_INTPCM AFMT_S32_LE #else #define AFMT_INTPCM AFMT_S32_BE #endif #endif #ifndef SND_CHN_MAX #define SND_CHN_MAX 18 #endif #define FEEDINTPCM_RESERVOIR (SND_CHN_MAX * PCM_32_BPS) #define FEEDINTPCM_CHANNELS 0 #ifdef DYNAMIC_G711 #include "g711.c" #else #include "g711.h" G711_DECLARE(g711_conv_tables); #endif static int feed_intpcm_set(struct pcm_feeder *f, int what, int value) { switch (what) { case FEEDINTPCM_CHANNELS: if (value < SND_CHN_MIN || value > SND_CHN_MAX) return (EINVAL); f->data = (void *)(uintptr_t)value; break; default: return (EINVAL); break; } return (0); } static int feed_intpcm_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { uint32_t afmt_in, afmt_out, j, ibps, obps, ialign, oalign; intpcm_t v; uint8_t reservoir[FEEDINTPCM_RESERVOIR], *src, *dst, *end; afmt_in = f->desc->in & ~AFMT_STEREO; afmt_out = f->desc->out & ~AFMT_STEREO; ibps = AFMT_BPS(afmt_in); obps = AFMT_BPS(afmt_out); ialign = ibps * (uintptr_t)f->data; oalign = obps * (uintptr_t)f->data; dst = b; count -= count % oalign; do { if (count < oalign) break; j = (count / oalign) * ialign; if (j > FEEDINTPCM_RESERVOIR) { if (j > count) j = count - (count % ialign); src = dst + count - j; } else src = reservoir; j = FEEDER_FEED(f->source, c, src, j, source) / ialign; if (j == 0) break; j *= oalign; end = dst + j; do { switch (afmt_in) { case AFMT_S8: v = _PCM_READ_S8_NE(src) << 24; break; case AFMT_S16_LE: v = _PCM_READ_S16_LE(src) << 16; break; case AFMT_S16_BE: v = _PCM_READ_S16_BE(src) << 16; break; case AFMT_S24_LE: v = _PCM_READ_S24_LE(src) << 8; break; case AFMT_S24_BE: v = _PCM_READ_S24_BE(src) << 8; break; case AFMT_S32_LE: v = _PCM_READ_S32_LE(src); break; case AFMT_S32_BE: v = _PCM_READ_S32_BE(src); break; case AFMT_U8: v = _PCM_READ_U8_NE(src) << 24; break; case AFMT_U16_LE: v = _PCM_READ_U16_LE(src) << 16; break; case AFMT_U16_BE: v = _PCM_READ_U16_BE(src) << 16; break; case AFMT_U24_LE: v = _PCM_READ_U24_LE(src) << 8; break; case AFMT_U24_BE: v = _PCM_READ_U24_BE(src) << 8; break; case AFMT_U32_LE: v = _PCM_READ_U32_LE(src); break; case AFMT_U32_BE: v = _PCM_READ_U32_BE(src); break; case AFMT_A_LAW: v = alaw_to_intpcm(*src); break; case AFMT_MU_LAW: v = ulaw_to_intpcm(*src); break; default: return (0); break; } switch (afmt_out) { case AFMT_S8: _PCM_WRITE_S8_NE(dst, v >> 24); break; case AFMT_S16_LE: _PCM_WRITE_S16_LE(dst, v >> 16); break; case AFMT_S16_BE: _PCM_WRITE_S16_BE(dst, v >> 16); break; case AFMT_S24_LE: _PCM_WRITE_S24_LE(dst, v >> 8); break; case AFMT_S24_BE: _PCM_WRITE_S24_BE(dst, v >> 8); break; case AFMT_S32_LE: _PCM_WRITE_S32_LE(dst, v); break; case AFMT_S32_BE: _PCM_WRITE_S32_BE(dst, v); break; case AFMT_U8: _PCM_WRITE_U8_NE(dst, v >> 24); break; case AFMT_U16_LE: _PCM_WRITE_U16_LE(dst, v >> 16); break; case AFMT_U16_BE: _PCM_WRITE_U16_BE(dst, v >> 16); break; case AFMT_U24_LE: _PCM_WRITE_U24_LE(dst, v >> 8); break; case AFMT_U24_BE: _PCM_WRITE_U24_BE(dst, v >> 8); break; case AFMT_U32_LE: _PCM_WRITE_U32_LE(dst, v); break; case AFMT_U32_BE: _PCM_WRITE_U32_BE(dst, v); break; case AFMT_A_LAW: *dst = intpcm_to_alaw(v); break; case AFMT_MU_LAW: *dst = intpcm_to_ulaw(v); break; default: return (0); break; } dst += obps; src += ibps; } while (dst != end); count -= j; } while (count != 0); return (dst - b); } static struct pcm_feederdesc feeder_intpcm_desc[] = { { 0, 0, 0, 0 } }; static kobj_method_t feeder_intpcm_methods[] = { KOBJMETHOD(feeder_feed, feed_intpcm_feed), KOBJMETHOD(feeder_set, feed_intpcm_set), { 0, 0 } }; FEEDER_DECLARE(feeder_intpcm, 0, 0);