Index: sys/dev/sound/pci/atiixp.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/atiixp.c,v retrieving revision 1.6 diff -u -r1.6 atiixp.c --- sys/dev/sound/pci/atiixp.c 22 Mar 2006 21:29:47 -0000 1.6 +++ sys/dev/sound/pci/atiixp.c 29 Apr 2006 04:24:52 -0000 @@ -65,6 +65,18 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.6 2006/03/22 21:29:47 ariff Exp $"); +#define ATI_IXP_DMA_RETRY_MAX 100 +#define ATI_IXP_DMA_CHSEGS_DEFAULT 2 + +#define ATI_IXP_BUFSZ_MIN 4096 +#define ATI_IXP_BUFSZ_MAX 65536 +#define ATI_IXP_BUFSZ_DEFAULT 16384 + +#ifdef ATI_IXP_DEBUG_VERBOSE +#undef ATI_IXP_DEBUG +#define ATI_IXP_DEBUG 1 +#endif + struct atiixp_dma_op { volatile uint32_t addr; volatile uint16_t status; @@ -81,7 +93,10 @@ struct atiixp_dma_op *sgd_table; bus_addr_t sgd_addr; uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit; - uint32_t dma_segs; + uint32_t dma_segs, dma_blksz; +#ifdef ATI_IXP_DEBUG + uint32_t dma_ptr, dma_prevptr; +#endif uint32_t fmt; int caps_32bit, dir, active; }; @@ -159,9 +174,9 @@ static void atiixp_enable_interrupts(struct atiixp_info *); static void atiixp_disable_interrupts(struct atiixp_info *); static void atiixp_reset_aclink(struct atiixp_info *); -static void atiixp_flush_dma(struct atiixp_info *, struct atiixp_chinfo *); -static void atiixp_enable_dma(struct atiixp_info *, struct atiixp_chinfo *); -static void atiixp_disable_dma(struct atiixp_info *, struct atiixp_chinfo *); +static void atiixp_flush_dma(struct atiixp_chinfo *); +static void atiixp_enable_dma(struct atiixp_chinfo *); +static void atiixp_disable_dma(struct atiixp_chinfo *); static int atiixp_waitready_codec(struct atiixp_info *); static int atiixp_rdcd(kobj_t, void *, int); @@ -174,6 +189,7 @@ static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t); static void atiixp_buildsgdt(struct atiixp_chinfo *); static int atiixp_chan_trigger(kobj_t, void *, int); +static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *); static int atiixp_chan_getptr(kobj_t, void *); static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *); @@ -208,7 +224,10 @@ * Disable / ignore internal xrun/spdf interrupt flags * since it doesn't interest us (for now). */ -#if 0 +#if 1 + value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN | + ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN); +#else value |= ATI_REG_IER_IN_XRUN_EN; value |= ATI_REG_IER_OUT_XRUN_EN; @@ -243,7 +262,7 @@ /* have to wait at least 10 usec for it to initialise */ DELAY(20); - }; + } /* perform a soft reset */ value = atiixp_rd(sc, ATI_REG_CMD); @@ -284,7 +303,7 @@ /* check if its active now */ value = atiixp_rd(sc, ATI_REG_CMD); - }; + } if (timeout == 0) device_printf(sc->dev, "giving up aclink reset\n"); @@ -300,32 +319,32 @@ } static void -atiixp_flush_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +atiixp_flush_dma(struct atiixp_chinfo *ch) { - atiixp_wr(sc, ATI_REG_FIFO_FLUSH, ch->flush_bit); + atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit); } static void -atiixp_enable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +atiixp_enable_dma(struct atiixp_chinfo *ch) { uint32_t value; - value = atiixp_rd(sc, ATI_REG_CMD); + value = atiixp_rd(ch->parent, ATI_REG_CMD); if (!(value & ch->enable_bit)) { value |= ch->enable_bit; - atiixp_wr(sc, ATI_REG_CMD, value); + atiixp_wr(ch->parent, ATI_REG_CMD, value); } } static void -atiixp_disable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +atiixp_disable_dma(struct atiixp_chinfo *ch) { uint32_t value; - value = atiixp_rd(sc, ATI_REG_CMD); + value = atiixp_rd(ch->parent, ATI_REG_CMD); if (value & ch->enable_bit) { value &= ~ch->enable_bit; - atiixp_wr(sc, ATI_REG_CMD, value); + atiixp_wr(ch->parent, ATI_REG_CMD, value); } } @@ -342,7 +361,7 @@ ATI_REG_PHYS_OUT_ADDR_EN) == 0) return 0; DELAY(1); - } while (timeout--); + } while (--timeout); return -1; } @@ -372,7 +391,7 @@ if (data & ATI_REG_PHYS_IN_READ_FLAG) return data >> ATI_REG_PHYS_IN_DATA_SHIFT; DELAY(1); - } while (timeout--); + } while (--timeout); if (reg < 0x7c) device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg); @@ -440,6 +459,7 @@ ch->channel = c; ch->dir = dir; ch->dma_segs = sc->dma_segs; + ch->dma_blksz = sc->bufsz / sc->dma_segs; atiixp_unlock(sc); @@ -451,7 +471,7 @@ ch->sgd_table = &sc->sgd_table[num * ch->dma_segs]; ch->sgd_addr = sc->sgd_addr + (num * ch->dma_segs * sizeof(struct atiixp_dma_op)); - atiixp_disable_dma(sc, ch); + atiixp_disable_dma(ch); atiixp_unlock(sc); return ch; @@ -507,31 +527,40 @@ struct atiixp_chinfo *ch = data; struct atiixp_info *sc = ch->parent; - if (blksz > (sc->bufsz / ch->dma_segs)) - blksz = sc->bufsz / ch->dma_segs; + /* XXX Force static blocksize */ + sndbuf_resize(ch->buffer, ch->dma_segs, ch->dma_blksz); - sndbuf_resize(ch->buffer, ch->dma_segs, blksz); + if (ch->dma_blksz != sndbuf_getblksz(ch->buffer)) { + device_printf(sc->dev, + "%s: WARNING - dma_blksz=%u != blksz=%u !!!\n", + __func__, ch->dma_blksz, sndbuf_getblksz(ch->buffer)); + ch->dma_blksz = sndbuf_getblksz(ch->buffer); + } - return sndbuf_getblksz(ch->buffer); + return ch->dma_blksz; } static void atiixp_buildsgdt(struct atiixp_chinfo *ch) { - uint32_t addr, blksz; + uint32_t addr; int i; addr = sndbuf_getbufaddr(ch->buffer); - blksz = sndbuf_getblksz(ch->buffer); for (i = 0; i < ch->dma_segs; i++) { - ch->sgd_table[i].addr = htole32(addr + (i * blksz)); + ch->sgd_table[i].addr = htole32(addr + (i * ch->dma_blksz)); ch->sgd_table[i].status = htole16(0); - ch->sgd_table[i].size = htole16(blksz >> 2); + ch->sgd_table[i].size = htole16(ch->dma_blksz >> 2); ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr + (((i + 1) % ch->dma_segs) * sizeof(struct atiixp_dma_op))); } + +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = 0; + ch->dma_prevptr = 0; +#endif } static int @@ -545,17 +574,17 @@ switch (go) { case PCMTRIG_START: - atiixp_flush_dma(sc, ch); + atiixp_flush_dma(ch); atiixp_buildsgdt(ch); atiixp_wr(sc, ch->linkptr_bit, 0); - atiixp_enable_dma(sc, ch); + atiixp_enable_dma(ch); atiixp_wr(sc, ch->linkptr_bit, (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: - atiixp_disable_dma(sc, ch); - atiixp_flush_dma(sc, ch); + atiixp_disable_dma(ch); + atiixp_flush_dma(ch); break; default: atiixp_unlock(sc); @@ -578,41 +607,71 @@ return 0; } -static int -atiixp_chan_getptr(kobj_t obj, void *data) +static __inline uint32_t +atiixp_dmapos(struct atiixp_chinfo *ch) { - struct atiixp_chinfo *ch = data; struct atiixp_info *sc = ch->parent; - uint32_t addr, align, retry, sz; + uint32_t reg, addr, sz, retry; volatile uint32_t ptr; + reg = ch->dma_dt_cur_bit; addr = sndbuf_getbufaddr(ch->buffer); - align = (ch->fmt & AFMT_32BIT) ? 7 : 3; - retry = 100; - sz = sndbuf_getblksz(ch->buffer) * ch->dma_segs; + sz = ch->dma_segs * ch->dma_blksz; + retry = ATI_IXP_DMA_RETRY_MAX; - atiixp_lock(sc); do { - ptr = atiixp_rd(sc, ch->dma_dt_cur_bit); + ptr = atiixp_rd(sc, reg); if (ptr < addr) continue; ptr -= addr; - if (ptr < sz && !(ptr & align)) - break; + if (ptr < sz) { +#ifdef ATI_IXP_DEBUG + if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) { + uint32_t delta; + + delta = (sz + ptr - ch->dma_prevptr) % sz; +#ifndef ATI_IXP_DEBUG_VERBOSE + if (delta < ch->dma_blksz) +#endif + device_printf(sc->dev, + "PCMDIR_%s: incoherent DMA " + "dma_prevptr=%u ptr=%u " + "dma_ptr=%u dma_segs=%u " + "[delta=%u != dma_blksz=%u] " + "(%s)\n", + (ch->dir == PCMDIR_PLAY) ? + "PLAY" : "REC", + ch->dma_prevptr, ptr, + ch->dma_ptr, ch->dma_segs, + delta, ch->dma_blksz, + (delta < ch->dma_blksz) ? + "OVERLAPPED!" : "Ok"); + ch->dma_ptr = ptr & ~(ch->dma_blksz - 1); + } + ch->dma_prevptr = ptr; +#endif + return ptr; + } } while (--retry); - atiixp_unlock(sc); -#if 0 - if (retry != 100) { - device_printf(sc->dev, - "%saligned hwptr: dir=PCMDIR_%s ptr=%u fmt=0x%08x retry=%d\n", - (ptr & align) ? "un" : "", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr, - ch->fmt, 100 - retry); - } -#endif + device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr); + + return 0; +} + +static int +atiixp_chan_getptr(kobj_t obj, void *data) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + uint32_t ptr; + + atiixp_lock(sc); + ptr = atiixp_dmapos(ch); + atiixp_unlock(sc); - return (retry > 0) ? ptr : 0; + return ptr; } static struct pcmchan_caps * @@ -644,6 +703,7 @@ atiixp_intr(void *p) { struct atiixp_info *sc = p; + struct atiixp_chinfo *ch; uint32_t status, enable, detected_codecs; atiixp_lock(sc); @@ -655,13 +715,23 @@ } if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) { + ch = &sc->rch; +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) % + (ch->dma_segs * ch->dma_blksz); +#endif atiixp_unlock(sc); - chn_intr(sc->rch.channel); + chn_intr(ch->channel); atiixp_lock(sc); } if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) { + ch = &sc->pch; +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) % + (ch->dma_segs * ch->dma_blksz); +#endif atiixp_unlock(sc); - chn_intr(sc->pch.channel); + chn_intr(ch->channel); atiixp_lock(sc); } @@ -743,11 +813,11 @@ /* wait for the interrupts to happen */ timeout = 100; - while (--timeout) { + do { msleep(sc, sc->lock, PWAIT, "ixpslp", 1); if (sc->codec_not_ready_bits) break; - } + } while (--timeout); atiixp_disable_interrupts(sc); @@ -877,7 +947,7 @@ vendor = pci_get_vendor(dev); devid = pci_get_device(dev); - for (i = 0; i < sizeof(atiixp_hw)/sizeof(atiixp_hw[0]); i++) { + for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) { if (vendor == atiixp_hw[i].vendor && devid == atiixp_hw[i].devid) { device_set_desc(dev, atiixp_hw[i].desc); @@ -904,7 +974,7 @@ /* * Default DMA segments per playback / recording channel */ - sc->dma_segs = ATI_IXP_DMA_CHSEGS; + sc->dma_segs = ATI_IXP_DMA_CHSEGS_DEFAULT; pci_set_powerstate(dev, PCI_POWERSTATE_D0); pci_enable_busmaster(dev); @@ -922,7 +992,8 @@ sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); - sc->bufsz = pcm_getbuffersize(dev, 4096, ATI_IXP_DEFAULT_BUFSZ, 65536); + sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN, + ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX); sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, @@ -962,7 +1033,7 @@ /* * DMA tag for scatter-gather buffers and link pointers */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/sc->bufsz, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -1028,7 +1099,8 @@ return r; } sc->codec = NULL; - atiixp_disable_interrupts(sc); + if (sc->st != 0 && sc->sh != 0) + atiixp_disable_interrupts(sc); atiixp_release_resource(sc); free(sc, M_DEVBUF); } Index: sys/dev/sound/pci/atiixp.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/atiixp.h,v retrieving revision 1.2 diff -u -r1.2 atiixp.h --- sys/dev/sound/pci/atiixp.h 18 Feb 2006 10:23:37 -0000 1.2 +++ sys/dev/sound/pci/atiixp.h 27 Apr 2006 09:06:33 -0000 @@ -46,9 +46,8 @@ #define ATI_IXP_DMA_CHSEGS_MIN 2 #define ATI_IXP_DMA_CHSEGS_MAX 256 -#define ATI_IXP_DEFAULT_BUFSZ (1 << 13) /* 8192 */ - #define ATI_VENDOR_ID 0x1002 /* ATI Technologies */ + #define ATI_IXP_200_ID 0x4341 #define ATI_IXP_300_ID 0x4361 #define ATI_IXP_400_ID 0x4370 Index: sys/dev/sound/pcm/ac97.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/ac97.c,v retrieving revision 1.61 diff -u -r1.61 ac97.c --- sys/dev/sound/pcm/ac97.c 7 Jan 2006 05:20:46 -0000 1.61 +++ sys/dev/sound/pcm/ac97.c 27 Apr 2006 09:06:33 -0000 @@ -827,15 +827,17 @@ struct ac97_info * ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { + struct snddev_info *d = device_get_softc(dev); struct ac97_info *codec; + int eapd_inv; - codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); + codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); if (codec == NULL) return NULL; snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); - codec->methods = kobj_create(cls, M_AC97, M_WAITOK); + codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); if (codec->methods == NULL) { snd_mtxlock(codec->lock); snd_mtxfree(codec->lock); @@ -846,6 +848,13 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "ac97_eapd_inv", &eapd_inv) == 0) { + if (eapd_inv != 0) + codec->flags |= AC97_F_EAPD_INV; + } + if (d != NULL) + d->flags |= SD_F_AC97; return codec; } @@ -964,4 +973,65 @@ return &ac97mixer_class; } +#ifdef SND_DYNSYSCTL +static int +sysctl_hw_snd_ac97_external_amplifier(SYSCTL_HANDLER_ARGS) +{ + struct ac97_info *codec; + int ea, inv, err = 0; + u_int16_t val; + + codec = oidp->oid_arg1; + if (codec == NULL || codec->id == 0 || codec->lock == NULL) + return EINVAL; + snd_mtxlock(codec->lock); + val = ac97_rdcd(codec, AC97_REG_POWER); + inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; + ea = (val >> 15) ^ inv; + snd_mtxunlock(codec->lock); + err = sysctl_handle_int(oidp, &ea, sizeof(ea), req); + if (err == 0 && req->newptr != NULL) { + if (ea != 0 && ea != 1) + return EINVAL; + if (ea != ((val >> 15) ^ inv)) { + snd_mtxlock(codec->lock); + ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); + snd_mtxunlock(codec->lock); + } + } + return err; +} +#endif + +int +ac97_initsys(device_t dev) +{ +#ifdef SND_DYNSYSCTL + struct ac97_info *codec; + struct snddev_info *d; + u_int16_t orig, val; + d = device_get_softc(dev); + if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL) + return ENODEV; + if (!(d->flags & SD_F_AC97)) + return ENODEV; + codec = mix_getdevinfo(d->mixer_dev->si_drv1); + if (codec == NULL || codec->id == 0 || codec->lock == NULL) + return ENODEV; + + snd_mtxlock(codec->lock); + orig = ac97_rdcd(codec, AC97_REG_POWER); + ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); + val = ac97_rdcd(codec, AC97_REG_POWER); + ac97_wrcd(codec, AC97_REG_POWER, orig); + snd_mtxunlock(codec->lock); + if ((val & 0x8000) == (orig & 0x8000)) + return EINVAL; + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "ac97_external_amplifier", CTLTYPE_INT | CTLFLAG_RW, + codec, sizeof(codec), sysctl_hw_snd_ac97_external_amplifier, + "I", "AC97 External Amplifier"); +#endif + return 0; +} Index: sys/dev/sound/pcm/ac97.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/ac97.h,v retrieving revision 1.18 diff -u -r1.18 ac97.h --- sys/dev/sound/pcm/ac97.h 2 Oct 2005 15:37:40 -0000 1.18 +++ sys/dev/sound/pcm/ac97.h 27 Apr 2006 09:06:33 -0000 @@ -105,4 +105,4 @@ u_int16_t ac97_rdcd(struct ac97_info *codec, int reg); void ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val); - +int ac97_initsys(device_t dev); Index: sys/dev/sound/pcm/channel.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/channel.c,v retrieving revision 1.109 diff -u -r1.109 channel.c --- sys/dev/sound/pcm/channel.c 22 Mar 2006 00:34:17 -0000 1.109 +++ sys/dev/sound/pcm/channel.c 27 Apr 2006 09:06:33 -0000 @@ -63,7 +63,7 @@ return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); + 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "irq rate for buffering"); static int report_soft_formats = 1; SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, &report_soft_formats, 1, "report software-emulated formats"); @@ -193,13 +193,23 @@ if (c->direction == PCMDIR_PLAY) { amt = MIN(delta, sndbuf_getready(b)); + amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_dispose(b, NULL, amt); } else { amt = MIN(delta, sndbuf_getfree(b)); + amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_acquire(b, NULL, amt); } + if ((c->flags & CHN_F_TRIGGERED) && delta == 0) { + device_printf(c->dev, "WARNING: PCMDIR_%s DMA completion " + "too fast/slow ! hwptr=%u, old=%u " + "delta=%u amt=%u ready=%u free=%u\n", + (c->direction == PCMDIR_PLAY) ? "PLAY" : "REC", + hwptr, old, delta, amt, + sndbuf_getready(b), sndbuf_getfree(b)); + } return delta; } @@ -243,9 +253,11 @@ sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); amt = sndbuf_getfree(b); - KASSERT(amt <= sndbuf_getsize(bs), - ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, - amt, sndbuf_getsize(bs), c->flags)); + DEB(if (amt > sndbuf_getsize(bs) && + sndbuf_getbps(bs) >= sndbuf_getbps(b)) { + printf("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, + amt, sndbuf_getsize(bs), c->flags); + }); ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; /* @@ -746,6 +758,131 @@ return 0; } +static struct afmtstr_table default_afmtstr_table[] = { + { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW }, + { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, + { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, + { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, + { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, + { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, + { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, + { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, + { NULL, 0 }, +}; + +int +afmtstr_swap_sign(char *s) +{ + if (s == NULL || strlen(s) < 2) /* full length of "s8" */ + return 0; + if (*s == 's') + *s = 'u'; + else if (*s == 'u') + *s = 's'; + else + return 0; + return 1; +} + +int +afmtstr_swap_endian(char *s) +{ + if (s == NULL || strlen(s) < 5) /* full length of "s16le" */ + return 0; + if (s[3] == 'l') + s[3] = 'b'; + else if (s[3] == 'b') + s[3] = 'l'; + else + return 0; + return 1; +} + +u_int32_t +afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo) +{ + size_t fsz, sz; + + sz = (s == NULL) ? 0 : strlen(s); + + if (sz > 1) { + + if (tbl == NULL) + tbl = default_afmtstr_table; + + for (; tbl->fmtstr != NULL; tbl++) { + fsz = strlen(tbl->fmtstr); + if (sz < fsz) + continue; + if (strncmp(s, tbl->fmtstr, fsz) != 0) + continue; + if (fsz == sz) + return tbl->format | + ((stereo) ? AFMT_STEREO : 0); + if ((sz - fsz) < 2 || s[fsz] != ':') + break; + /* + * For now, just handle mono/stereo. + */ + if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' || + s[fsz + 1] == '1')) || + strcmp(s + fsz + 1, "mono") == 0) + return tbl->format; + if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' || + s[fsz + 1] == '2')) || + strcmp(s + fsz + 1, "stereo") == 0) + return tbl->format | AFMT_STEREO; + break; + } + } + + return 0; +} + +u_int32_t +afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst, + size_t len, int type, int stereo) +{ + u_int32_t fmt = 0; + char *fmtstr = NULL, *tag = ""; + + if (tbl == NULL) + tbl = default_afmtstr_table; + + for (; tbl->format != 0; tbl++) { + if (tbl->format == 0) + break; + if ((afmt & ~AFMT_STEREO) != tbl->format) + continue; + fmt = afmt; + fmtstr = tbl->fmtstr; + break; + } + + if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) { + strlcpy(dst, fmtstr, len); + switch (type) { + case AFMTSTR_SIMPLE: + tag = (fmt & AFMT_STEREO) ? ":s" : ":m"; + break; + case AFMTSTR_NUM: + tag = (fmt & AFMT_STEREO) ? ":2" : ":1"; + break; + case AFMTSTR_FULL: + tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono"; + break; + case AFMTSTR_NONE: + default: + break; + } + if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \ + (!stereo && (fmt & AFMT_STEREO)))) + strlcat(dst, tag, len); + } + + return fmt; +} + int chn_reset(struct pcm_channel *c, u_int32_t fmt) { @@ -1100,10 +1237,12 @@ CHN_LOCKASSERT(c); if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) { - KASSERT(sndbuf_getsize(bs) == 0 || - sndbuf_getsize(bs) >= sndbuf_getsize(b), - ("%s(%s): bufsoft size %d < bufhard size %d", __func__, - c->name, sndbuf_getsize(bs), sndbuf_getsize(b))); + DEB(if (!(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b) || + sndbuf_getbps(bs) < sndbuf_getbps(b))) { + printf("%s(%s): bufsoft size %d < bufhard size %d", __func__, + c->name, sndbuf_getsize(bs), sndbuf_getsize(b)) + }); return EINVAL; } c->flags |= CHN_F_SETBLOCKSIZE; @@ -1186,7 +1325,7 @@ maxsize = sndbuf_getsize(b); if (reqblksz * blkcnt > maxsize) maxsize = reqblksz * blkcnt; - if (maxsize > sndbuf_getsize(bs)) + if (sndbuf_getbps(bs) >= sndbuf_getbps(b) && maxsize > sndbuf_getsize(bs)) printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n", c->name, sndbuf_getsize(bs), maxsize); if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { @@ -1197,11 +1336,13 @@ chn_resetbuf(c); out1: - KASSERT(sndbuf_getsize(bs) == 0 || - sndbuf_getsize(bs) >= sndbuf_getsize(b), - ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", + DEB(if (!(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b) || + sndbuf_getbps(bs) < sndbuf_getbps(b))) { + printf("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz, - blksz, maxsize, blkcnt)); + blksz, maxsize, blkcnt); + }); out: c->flags &= ~CHN_F_SETBLOCKSIZE; #if 0 @@ -1259,11 +1400,14 @@ hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ return hwptr; #endif +#if 0 int hwptr; CHN_LOCKASSERT(c); hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; return (hwptr - (hwptr % sndbuf_getbps(c->bufhard))); +#endif + return (c->flags & CHN_F_TRIGGERED) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; } struct pcmchan_caps * @@ -1301,6 +1445,7 @@ struct pcm_feederdesc desc; u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; int err; + char fmtstr[AFMTSTR_MAXSZ]; CHN_LOCKASSERT(c); while (chn_removefeeder(c) == 0); @@ -1322,7 +1467,7 @@ } else { if (c->flags & CHN_F_HAS_VCHAN) { desc.type = FEEDER_MIXER; - desc.in = 0; + desc.in = c->format; } else { DEB(printf("can't decide which feeder type to use!\n")); return EOPNOTSUPP; @@ -1361,7 +1506,42 @@ desc.out = 0; desc.flags = 0; DEB(printf("find feeder type %d, ", type)); - fc = feeder_getclass(&desc); + if (type == FEEDER_VOLUME || type == FEEDER_RATE) { + if (c->feeder->desc->out & AFMT_32BIT) + strlcpy(fmtstr,"s32le", sizeof(fmtstr)); + else if (c->feeder->desc->out & AFMT_24BIT) + strlcpy(fmtstr, "s24le", sizeof(fmtstr)); + else { + /* + * 8bit doesn't provide enough headroom + * for proper processing without + * creating too much noises. Force to + * 16bit instead. + */ + strlcpy(fmtstr, "s16le", sizeof(fmtstr)); + } + if (!(c->feeder->desc->out & AFMT_8BIT) && + c->feeder->desc->out & AFMT_BIGENDIAN) + afmtstr_swap_endian(fmtstr); + if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && + !(c->feeder->desc->out & AFMT_SIGNED)) + afmtstr_swap_sign(fmtstr); + desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); + if (desc.in == 0) + desc.in = AFMT_S16_LE; + /* feeder_volume need stereo processing */ + if (type == FEEDER_VOLUME || + c->feeder->desc->out & AFMT_STEREO) + desc.in |= AFMT_STEREO; + desc.out = desc.in; + fc = feeder_getclass(&desc); + if (fc != NULL && fc->desc != NULL) + desc.flags = fc->desc->flags; + } else { + fc = feeder_getclass(&desc); + if (fc != NULL && fc->desc != NULL) + desc = *fc->desc; + } DEB(printf("got %p\n", fc)); if (fc == NULL) { DEB(printf("can't find required feeder type %d\n", type)); @@ -1370,7 +1550,7 @@ } DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); - tmp[0] = fc->desc->in; + tmp[0] = desc.in; tmp[1] = 0; if (chn_fmtchain(c, tmp) == 0) { DEB(printf("failed\n")); @@ -1379,7 +1559,7 @@ } DEB(printf("ok\n")); - err = chn_addfeeder(c, fc, fc->desc); + err = chn_addfeeder(c, fc, &desc); if (err) { DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); @@ -1462,15 +1642,21 @@ */ } if (flags & CHN_N_BLOCKSIZE) { + u_int32_t fmt; int blksz; /* * scan the children, find the lowest blocksize and use that * for the hard blocksize */ + fmt = sndbuf_getfmt(c->bufsoft); blksz = sndbuf_getmaxsize(c->bufhard) / 2; SLIST_FOREACH(pce, &c->children, link) { child = pce->channel; CHN_LOCK(child); + if (fmt != sndbuf_getfmt(child->bufhard)) { + CHN_UNLOCK(child); + continue; + } if (sndbuf_getblksz(child->bufhard) < blksz) blksz = sndbuf_getblksz(child->bufhard); CHN_UNLOCK(child); Index: sys/dev/sound/pcm/channel.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/channel.h,v retrieving revision 1.32 diff -u -r1.32 channel.h --- sys/dev/sound/pcm/channel.h 10 Sep 2005 18:10:31 -0000 1.32 +++ sys/dev/sound/pcm/channel.h 27 Apr 2006 09:06:33 -0000 @@ -114,6 +114,26 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); +#define AFMTSTR_NONE 0 /* "s16le" */ +#define AFMTSTR_SIMPLE 1 /* "s16le:s" */ +#define AFMTSTR_NUM 2 /* "s16le:2" */ +#define AFMTSTR_FULL 3 /* "s16le:stereo" */ + +#define AFMTSTR_MAXSZ 13 /* include null terminator */ + +#define AFMTSTR_MONO_RETURN 0 +#define AFMTSTR_STEREO_RETURN 1 + +struct afmtstr_table { + char *fmtstr; + u_int32_t format; +}; + +int afmtstr_swap_sign(char *); +int afmtstr_swap_endian(char *); +u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int); +u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int); + #define PCMDIR_VIRTUAL 2 #define PCMDIR_PLAY 1 #define PCMDIR_REC -1 Index: sys/dev/sound/pcm/dsp.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/dsp.c,v retrieving revision 1.95 diff -u -r1.95 dsp.c --- sys/dev/sound/pcm/dsp.c 30 Mar 2006 06:17:03 -0000 1.95 +++ sys/dev/sound/pcm/dsp.c 27 Apr 2006 09:06:33 -0000 @@ -201,10 +201,12 @@ fmt = 0; break; - case SND_DEV_DSPREC: + case SND_DEV_DSPHW: + /* + * HW *specific* access without channel numbering confusion + * caused by "first come first served" by dsp_clone(). + */ fmt = AFMT_U8; - if (flags & FWRITE) - return EINVAL; chnum = PCMCHAN(i_dev); break; Index: sys/dev/sound/pcm/fake.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/fake.c,v retrieving revision 1.16 diff -u -r1.16 fake.c --- sys/dev/sound/pcm/fake.c 10 Sep 2005 17:47:38 -0000 1.16 +++ sys/dev/sound/pcm/fake.c 27 Apr 2006 09:06:33 -0000 @@ -142,10 +142,11 @@ c->parentsnddev = d; /* * Fake channel is such a blessing in disguise. Using this, - * we can keep track prefered virtual channel speed without + * we can keep track prefered virtual channel speed / format without * querying kernel hint repetitively (see vchan_create / vchan.c). */ c->speed = 0; + c->format = 0; snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev)); return c; Index: sys/dev/sound/pcm/feeder.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/feeder.c,v retrieving revision 1.37 diff -u -r1.37 feeder.c --- sys/dev/sound/pcm/feeder.c 23 Feb 2006 19:23:55 -0000 1.37 +++ sys/dev/sound/pcm/feeder.c 27 Apr 2006 09:06:33 -0000 @@ -259,122 +259,152 @@ return 1; } -static struct pcm_feeder * -feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) -{ - struct feedertab_entry *fte; - struct pcm_feeder *try, *ret; - - DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); - if (fmtvalid(source->desc->out, to)) { - DEB(printf("got it\n")); - return source; - } - - if (maxdepth < 0) - return NULL; - - SLIST_FOREACH(fte, &feedertab, link) { - if (fte->desc == NULL) - continue; - if (fte->desc->type != FEEDER_FMT) - continue; - if (fte->desc->in == source->desc->out) { - try = feeder_create(fte->feederclass, fte->desc); - if (try) { - try->source = source; - ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; - if (ret != NULL) - return ret; - feeder_destroy(try); - } - } - } - /* printf("giving up %s...\n", source->class->name); */ +/* + * See feeder_fmtchain() for the mumbo-jumbo ridiculous explaination + * of what the heck is this FMT_Q_* + */ +#define FMT_Q_UP 1 +#define FMT_Q_DOWN 2 +#define FMT_Q_EQ 3 +#define FMT_Q_MULTI 4 - return NULL; -} +/* + * 14bit format scoring + * -------------------- + * + * 13 12 11 10 9 8 2 1 0 offset + * +---+---+---+---+---+---+-------------+---+---+ + * | X | X | X | X | X | X | X X X X X X | X | X | + * +---+---+---+---+---+---+-------------+---+---+ + * | | | | | | | | | + * | | | | | | | | +--> signed? + * | | | | | | | | + * | | | | | | | +------> bigendian? + * | | | | | | | + * | | | | | | +---------------> total channels + * | | | | | | + * | | | | | +------------------------> AFMT_A_LAW + * | | | | | + * | | | | +----------------------------> AFMT_MU_LAW + * | | | | + * | | | +--------------------------------> AFMT_8BIT + * | | | + * | | +------------------------------------> AFMT_16BIT + * | | + * | +----------------------------------------> AFMT_24BIT + * | + * +--------------------------------------------> AFMT_32BIT + */ +#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) +#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) +#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) +#define score_val(s1) ((s1) & 0x3f00) +#define score_cse(s1) ((s1) & 0x7f) -int +u_int32_t chn_fmtscore(u_int32_t fmt) { - if (fmt & AFMT_32BIT) - return 60; - if (fmt & AFMT_24BIT) - return 50; - if (fmt & AFMT_16BIT) - return 40; - if (fmt & (AFMT_U8|AFMT_S8)) - return 30; - if (fmt & AFMT_MU_LAW) - return 20; + u_int32_t ret; + + ret = 0; + if (fmt & AFMT_SIGNED) + ret |= 1 << 0; + if (fmt & AFMT_BIGENDIAN) + ret |= 1 << 1; + if (fmt & AFMT_STEREO) + ret |= (2 & 0x3f) << 2; + else + ret |= (1 & 0x3f) << 2; if (fmt & AFMT_A_LAW) - return 10; - return 0; + ret |= 1 << 8; + else if (fmt & AFMT_MU_LAW) + ret |= 1 << 9; + else if (fmt & AFMT_8BIT) + ret |= 1 << 10; + else if (fmt & AFMT_16BIT) + ret |= 1 << 11; + else if (fmt & AFMT_24BIT) + ret |= 1 << 12; + else if (fmt & AFMT_32BIT) + ret |= 1 << 13; + + return ret; } -u_int32_t -chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) +static u_int32_t +chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) { - u_int32_t best; - int i, score, score2, oldscore; + u_int32_t best, score, score2, oldscore; + int i; + + if (fmt == 0 || fmts == NULL || fmts[0] == 0) + return 0; + + if (fmtvalid(fmt, fmts)) + return fmt; best = 0; score = chn_fmtscore(fmt); oldscore = 0; for (i = 0; fmts[i] != 0; i++) { score2 = chn_fmtscore(fmts[i]); - if (oldscore == 0 || (score2 == score) || - (score2 > oldscore && score2 < score) || - (score2 < oldscore && score2 > score) || - (oldscore < score && score2 > oldscore)) { - best = fmts[i]; - oldscore = score2; + if (cheq && !score_cheq(score, score2)) + continue; + if (oldscore == 0 || + (score_val(score2) == score_val(score)) || + (score_val(score2) == score_val(oldscore)) || + (score_val(score2) > score_val(oldscore) && + score_val(score2) < score_val(score)) || + (score_val(score2) < score_val(oldscore) && + score_val(score2) > score_val(score)) || + (score_val(oldscore) < score_val(score) && + score_val(score2) > score_val(oldscore))) { + if (score_val(oldscore) != score_val(score2) || + score_cse(score) == score_cse(score2) || + ((score_cse(oldscore) != score_cse(score) && + !score_endianeq(score, oldscore) && + (score_endianeq(score, score2) || + (!score_signeq(score, oldscore) && + score_signeq(score, score2)))))) { + best = fmts[i]; + oldscore = score2; + } } } return best; } u_int32_t -chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) { - u_int32_t best; - int i, score, score2, oldscore; + return chn_fmtbestfunc(fmt, fmts, 0); +} - best = 0; - score = chn_fmtscore(fmt); - oldscore = 0; - for (i = 0; fmts[i] != 0; i++) { - if ((fmt & AFMT_STEREO) == (fmts[i] & AFMT_STEREO)) { - score2 = chn_fmtscore(fmts[i]); - if (oldscore == 0 || (score2 == score) || - (score2 > oldscore && score2 < score) || - (score2 < oldscore && score2 > score) || - (oldscore < score && score2 > oldscore)) { - best = fmts[i]; - oldscore = score2; - } - } - } - return best; +u_int32_t +chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +{ + return chn_fmtbestfunc(fmt, fmts, 1); } u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) { u_int32_t best1, best2; - int score, score1, score2; + u_int32_t score, score1, score2; + + if (fmtvalid(fmt, fmts)) + return fmt; best1 = chn_fmtbeststereo(fmt, fmts); best2 = chn_fmtbestbit(fmt, fmts); - if (best1 != 0 && best2 != 0) { + if (best1 != 0 && best2 != 0 && best1 != best2) { if (fmt & AFMT_STEREO) return best1; else { - score = chn_fmtscore(fmt); - score1 = chn_fmtscore(best1); - score2 = chn_fmtscore(best2); + score = score_val(chn_fmtscore(fmt)); + score1 = score_val(chn_fmtscore(best1)); + score2 = score_val(chn_fmtscore(best2)); if (score1 == score2 || score1 == score) return best1; else if (score2 == score) @@ -389,6 +419,184 @@ return best2; } +static struct pcm_feeder * +feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) +{ + struct feedertab_entry *fte, *ftebest; + struct pcm_feeder *try, *ret; + uint32_t fl, qout, qsrc, qdst; + int qtype; + + if (to == NULL || to[0] == 0) + return NULL; + + DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); + if (fmtvalid(source->desc->out, to)) { + DEB(printf("got it\n")); + return source; + } + + if (maxdepth < 0) + return NULL; + + /* + * WARNING: THIS IS _NOT_ FOR THE FAINT HEART + * Disclaimer: I don't expect anybody could understand this + * without deep logical and mathematical analysis + * involving various unnamed probability theorem. + * + * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm + * is **extremely** difficult to digest especially when applied to + * large sets / numbers of random chains (feeders), each with + * unique characteristic providing different sets of in/out format. + * + * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: + * 1) Format chains + * 1.1 "8bit to any, not to 8bit" + * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] + * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] + * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] + * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] + * 1.2 "Any to 8bit, not from 8bit" + * 1.2.1 sign can remain consistent, e.g: s16le -> s8 + * 1.2.2 sign can be changed, e.g: s16le -> u8 + * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 + * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 + * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" + * 1.3.1 endian MUST remain consistent + * 1.3.2 sign CAN be changed + * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding + * 16bit to 24bit . + * 2) Channel chains (mono <-> stereo) + * 2.1 Both endian and sign MUST remain consistent + * 3) Endian chains (big endian <-> little endian) + * 3.1 Channels and sign MUST remain consistent + * 4) Sign chains (signed <-> unsigned) + * 4.1 Channels and endian MUST remain consistent + * + * .. and the mother of all chaining rules: + * + * Rules 0: Source and destination MUST not contain multiple selections. + * (qtype != FMT_Q_MULTI) + * + * First of all, our caller ( chn_fmtchain() ) will reduce the possible + * multiple from/to formats to a single best format using chn_fmtbest(). + * Then, using chn_fmtscore(), we determine the chaining characteristic. + * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining + * type while still adhering above chaining rules. + * + * The need for this complicated chaining procedures is inevitable, + * since currently we have more than 200 different types of FEEDER_FMT + * doing various unique format conversion. Without this (the old way), + * it is possible to generate broken chain since it doesn't do any + * sanity checking to ensure that the output format is "properly aligned" + * with the direction of conversion (quality up/down/equal). + * + * Conversion: s24le to s32le + * Possible chain: 1) s24le -> s32le (correct, optimized) + * 2) s24le -> s16le -> s32le + * (since we have feeder_24to16 and feeder_16to32) + * +-- obviously broken! + * + * Using scoring mechanisme, this will ensure that the chaining + * process do the right thing, or at least, give the best chain + * possible without causing quality (the 'Q') degradation. + */ + + qdst = chn_fmtscore(to[0]); + qsrc = chn_fmtscore(source->desc->out); + +#define score_q(s1) score_val(s1) +#define score_8bit(s1) ((s1) & 0x700) +#define score_non8bit(s1) (!score_8bit(s1)) +#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ + (score_8bit(s2) && score_non8bit(s1))) + +#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) +#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) +#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) +#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ + 0x2 : 0x0)) +#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) +#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ + ((score_cheq(s1, s2) && \ + score_endianeq(s1, s2)) ? \ + 0x1 : 0x0) | \ + ((score_cheq(s1, s2) && \ + score_signeq(s1, s2)) ? \ + 0x2 : 0x0)) + + /* Determine chaining direction and set matching flag */ + fl = 0x3fff; + if (to[1] != 0) { + qtype = FMT_Q_MULTI; + printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); + } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { + qtype = FMT_Q_DOWN; + fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); + } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { + qtype = FMT_Q_UP; + fl = FMT_Q_UP_FLAGS(qsrc, qdst); + } else { + qtype = FMT_Q_EQ; + fl = FMT_Q_EQ_FLAGS(qsrc, qdst); + } + + ftebest = NULL; + + SLIST_FOREACH(fte, &feedertab, link) { + if (fte->desc == NULL) + continue; + if (fte->desc->type != FEEDER_FMT) + continue; + qout = chn_fmtscore(fte->desc->out); +#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) +#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) +#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ + score_q(s3) >= score_q(s1) && \ + score_q(s3) <= score_q(s2)) +#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ + score_q(s3) <= score_q(s1) && \ + score_q(s3) >= score_q(s2)) +#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ + score_q(s1) == score_q(s2)) + if (fte->desc->in == source->desc->out && + (FMT_Q_MULTI_VALIDATE(qtype) || + (FMT_Q_FL_MATCH(fl, qout, qdst) && + (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || + FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || + FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { + try = feeder_create(fte->feederclass, fte->desc); + if (try) { + try->source = source; + ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; + if (ret != NULL) + return ret; + feeder_destroy(try); + } + } else if (fte->desc->in == source->desc->out) { + /* XXX quality must be considered! */ + if (ftebest == NULL) + ftebest = fte; + } + } + + if (ftebest != NULL) { + try = feeder_create(ftebest->feederclass, ftebest->desc); + if (try) { + try->source = source; + ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; + if (ret != NULL) + return ret; + feeder_destroy(try); + } + } + + /* printf("giving up %s...\n", source->class->name); */ + + return NULL; +} + u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to) { @@ -401,13 +609,15 @@ KASSERT(to != NULL, ("to == NULL")); KASSERT(to[0] != 0, ("to[0] == 0")); + if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) + return 0; + stop = c->feeder; + best = 0; if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { from = chn_getcaps(c)->fmtlist; - if (fmtvalid(to[0], from)) - from = to; - else { + if (from[1] != 0) { best = chn_fmtbest(to[0], from); if (best != 0) { tmpfrom[0] = best; @@ -420,49 +630,60 @@ tmpfrom[1] = 0; from = tmpfrom; if (to[1] != 0) { - if (fmtvalid(tmpfrom[0], to)) { - tmpto[0] = tmpfrom[0]; + best = chn_fmtbest(from[0], to); + if (best != 0) { + tmpto[0] = best; tmpto[1] = 0; to = tmpto; - } else { - best = chn_fmtbest(tmpfrom[0], to); - if (best != 0) { - tmpto[0] = best; - tmpto[1] = 0; - to = tmpto; - } } } } - i = 0; - best = 0; - bestmax = 100; - while (from[i] != 0) { - c->feeder->desc->out = from[i]; - try = NULL; +#define FEEDER_FMTCHAIN_MAXDEPTH 8 + + try = NULL; + + if (to[0] != 0 && from[0] != 0 && + to[1] == 0 && from[1] == 0) { max = 0; - while (try == NULL && max < 8) { + best = from[0]; + c->feeder->desc->out = best; + do { try = feeder_fmtchain(to, c->feeder, stop, max); - if (try == NULL) - max++; - } - if (try != NULL && max < bestmax) { - bestmax = max; - best = from[i]; - } - while (try != NULL && try != stop) { - del = try; - try = try->source; - feeder_destroy(del); + DEB(if (try != NULL) { + printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", + __func__, from[0], to[0], max) + }); + } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); + } else { + printf("%s: Using the old-way format chaining!\n", __func__); + i = 0; + best = 0; + bestmax = 100; + while (from[i] != 0) { + c->feeder->desc->out = from[i]; + try = NULL; + max = 0; + do { + try = feeder_fmtchain(to, c->feeder, stop, max); + } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); + if (try != NULL && max < bestmax) { + bestmax = max; + best = from[i]; + } + while (try != NULL && try != stop) { + del = try; + try = try->source; + feeder_destroy(del); + } + i++; } - i++; - } - if (best == 0) - return 0; + if (best == 0) + return 0; - c->feeder->desc->out = best; - try = feeder_fmtchain(to, c->feeder, stop, bestmax); + c->feeder->desc->out = best; + try = feeder_fmtchain(to, c->feeder, stop, bestmax); + } if (try == NULL) return 0; Index: sys/dev/sound/pcm/feeder.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/feeder.h,v retrieving revision 1.13 diff -u -r1.13 feeder.h --- sys/dev/sound/pcm/feeder.h 22 Jan 2006 15:06:49 -0000 1.13 +++ sys/dev/sound/pcm/feeder.h 27 Apr 2006 09:06:33 -0000 @@ -53,7 +53,7 @@ void feeder_register(void *p); struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); -int chn_fmtscore(u_int32_t fmt); +u_int32_t chn_fmtscore(u_int32_t fmt); u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts); u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts); u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts); @@ -72,7 +72,7 @@ .desc = feeder ## _desc, \ .data = pdata, \ }; \ -SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class); +SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class); #define FEEDER_ROOT 1 #define FEEDER_FMT 2 Index: sys/dev/sound/pcm/feeder_fmt.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/feeder_fmt.c,v retrieving revision 1.20 diff -u -r1.20 feeder_fmt.c --- sys/dev/sound/pcm/feeder_fmt.c 25 Jan 2006 21:13:46 -0000 1.20 +++ sys/dev/sound/pcm/feeder_fmt.c 27 Apr 2006 09:06:33 -0000 @@ -43,12 +43,11 @@ MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder"); -#define FEEDBUFSZ 8192 -#define FEEDBUF24SZ 8190 +#define FEEDBUFSZ 65536 +#define FEEDBUF24SZ 65532 #define FMT_TRACE(x...) /* printf(x) */ #define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */ -#define FMT_ALIGNBYTE(x) /* x */ /* * Sign inverted ulaw/alaw -> 8 table @@ -194,33 +193,42 @@ }; static int -feed_table_u8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_table_8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int j, k = FEEDER_FEED(f->source, c, b, count, source); + int j, sign, k = FEEDER_FEED(f->source, c, b, count, source); uint8_t *tbl = (uint8_t *)f->data; j = k; + sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80; while (j > 0) { j--; - b[j] = tbl[b[j]] ^ 0x80; + b[j] = tbl[b[j]] ^ sign; } return k; } static int -feed_table_s16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_table_16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + int i, j, sign, k = FEEDER_FEED(f->source, c, b, count >> 1, source); uint8_t *tbl = (uint8_t *)f->data; i = k; k <<= 1; j = k; - while (i > 0) { - b[--j] = tbl[b[--i]]; - b[--j] = 0; + sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80; + if (f->desc->out & AFMT_BIGENDIAN) { + while (i > 0) { + b[--j] = 0; + b[--j] = tbl[b[--i]] ^ sign; + } + } else { + while (i > 0) { + b[--j] = tbl[b[--i]] ^ sign; + b[--j] = 0; + } } return k; } @@ -229,91 +237,105 @@ feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int j, k = FEEDER_FEED(f->source, c, b, count, source); + int j, sign, k = FEEDER_FEED(f->source, c, b, count, source); uint8_t *tbl = (uint8_t *)f->data; j = k; + sign = (f->desc->in & AFMT_SIGNED) ? 0x80 : 0x00; while (j > 0) { j--; - b[j] = tbl[b[j]]; + b[j] = tbl[b[j] ^ sign]; } return k; } -static struct pcm_feederdesc feeder_ulawtou8_desc[] = { +static struct pcm_feederdesc feeder_ulawto8_desc[] = { {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0}, {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_ulawtou8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_u8), +static kobj_method_t feeder_ulawto8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_8), {0, 0} }; -FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_s8_tbl); +FEEDER_DECLARE(feeder_ulawto8, 0, ulaw_to_s8_tbl); -static struct pcm_feederdesc feeder_alawtou8_desc[] = { +static struct pcm_feederdesc feeder_alawto8_desc[] = { {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0}, {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_alawtou8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_u8), +static kobj_method_t feeder_alawto8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_8), {0, 0} }; -FEEDER_DECLARE(feeder_alawtou8, 0, alaw_to_s8_tbl); +FEEDER_DECLARE(feeder_alawto8, 0, alaw_to_s8_tbl); -static struct pcm_feederdesc feeder_ulawtos16le_desc[] = { +static struct pcm_feederdesc feeder_ulawto16_desc[] = { {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_ulawtos16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_s16le), +static kobj_method_t feeder_ulawto16_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_16), {0, 0} }; -FEEDER_DECLARE(feeder_ulawtos16le, 0, ulaw_to_s8_tbl); +FEEDER_DECLARE(feeder_ulawto16, 0, ulaw_to_s8_tbl); -static struct pcm_feederdesc feeder_alawtos16le_desc[] = { +static struct pcm_feederdesc feeder_alawto16_desc[] = { {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_alawtos16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_s16le), +static kobj_method_t feeder_alawto16_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_16), {0, 0} }; -FEEDER_DECLARE(feeder_alawtos16le, 0, alaw_to_s8_tbl); +FEEDER_DECLARE(feeder_alawto16, 0, alaw_to_s8_tbl); -static struct pcm_feederdesc feeder_u8toulaw_desc[] = { +static struct pcm_feederdesc feeder_8toulaw_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0}, {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_MU_LAW, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_u8toulaw_methods[] = { +static kobj_method_t feeder_8toulaw_methods[] = { KOBJMETHOD(feeder_feed, feed_table_xlaw), {0, 0} }; -FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw_tbl); +FEEDER_DECLARE(feeder_8toulaw, 0, u8_to_ulaw_tbl); -static struct pcm_feederdesc feeder_u8toalaw_desc[] = { +static struct pcm_feederdesc feeder_8toalaw_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0}, {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_A_LAW, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_u8toalaw_methods[] = { +static kobj_method_t feeder_8toalaw_methods[] = { KOBJMETHOD(feeder_feed, feed_table_xlaw), {0, 0} }; -FEEDER_DECLARE(feeder_u8toalaw, 0, u8_to_alaw_tbl); +FEEDER_DECLARE(feeder_8toalaw, 0, u8_to_alaw_tbl); /* - * Conversion rules:- - * 1. BE -> LE - * 2. if fmt == u8 , u8 -> s8 (economical) - * 3. Xle -> 16le - * 4. if fmt != u8 && fmt == u16le , u16le -> s16le - * 4. s16le mono -> s16le stereo - * * All conversion done in byte level to preserve endianess. */ @@ -335,42 +357,68 @@ return 0; } +#define swap_sign(fdr) \ + (((((fdr)->desc->in & AFMT_SIGNED) && \ + !((fdr)->desc->out & AFMT_SIGNED)) || \ + (!((fdr)->desc->in & AFMT_SIGNED) && \ + ((fdr)->desc->out & AFMT_SIGNED))) ? 0x80 : 0x00) /* * Bit conversion */ static int -feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_8to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + int sign; i = k; k <<= 1; j = k; - while (i > 0) { - b[--j] = b[--i]; - b[--j] = 0; + sign = swap_sign(f); + if (f->desc->out & AFMT_BIGENDIAN) { + while (i > 0) { + b[--j] = 0; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 0) { + b[--j] = b[--i] ^ sign; + b[--j] = 0; + } } return k; } -static struct pcm_feederdesc feeder_8to16le_desc[] = { +static struct pcm_feederdesc feeder_8to16_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U8, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U8, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U8, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_8to16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_8to16le), +static kobj_method_t feeder_8to16_methods[] = { + KOBJMETHOD(feeder_feed, feed_8to16), {0, 0} }; -FEEDER_DECLARE(feeder_8to16le, 0, NULL); +FEEDER_DECLARE(feeder_8to16, 0, NULL); static int -feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_16to8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k; + int i, j, k, sign; uint8_t *src = (uint8_t *)f->data; k = count << 1; @@ -381,35 +429,55 @@ return 0; } FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~1); + k &= ~1; i = k; j = k >> 1; - while (i > 0) { - b[--j] = src[--i]; - i--; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + i--; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = src[--i] ^ sign; + i--; + } } return k >> 1; } -static struct pcm_feederdesc feeder_16leto8_desc[] = { +static struct pcm_feederdesc feeder_16to8_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0}, {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0}, {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_16leto8_methods[] = { +static kobj_method_t feeder_16to8_methods[] = { KOBJMETHOD(feeder_init, feed_common_init), KOBJMETHOD(feeder_free, feed_common_free), - KOBJMETHOD(feeder_feed, feed_16leto8), + KOBJMETHOD(feeder_feed, feed_16to8), {0, 0} }; -FEEDER_DECLARE(feeder_16leto8, 0, NULL); +FEEDER_DECLARE(feeder_16to8, 0, NULL); static int -feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_16to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k; + int i, j, k, sign; k = (count / 3) << 1; k = FEEDER_FEED(f->source, c, b, k, source); @@ -419,35 +487,56 @@ return 0; } FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~1); + k &= ~1; i = k; j = (k >> 1) * 3; k = j; - while (i > 0) { - b[--j] = b[--i]; - b[--j] = b[--i]; - b[--j] = 0; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = 0; + } } return k; } -static struct pcm_feederdesc feeder_16leto24le_desc[] = { +static struct pcm_feederdesc feeder_16to24_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0}, {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0}, {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_16leto24le_methods[] = { - KOBJMETHOD(feeder_feed, feed_16leto24le), +static kobj_method_t feeder_16to24_methods[] = { + KOBJMETHOD(feeder_feed, feed_16to24), {0, 0} }; -FEEDER_DECLARE(feeder_16leto24le, 0, NULL); +FEEDER_DECLARE(feeder_16to24, 0, NULL); static int -feed_24leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_24to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k; + int i, j, k, sign; uint8_t *src = (uint8_t *)f->data; k = (count * 3) >> 1; @@ -458,72 +547,115 @@ return 0; } FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__); - FMT_ALIGNBYTE(k -= k % 3); + k -= k % 3; i = (k / 3) << 1; j = i; - while (i > 0) { - b[--i] = src[--k]; - b[--i] = src[--k]; - k--; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + k--; + b[--i] = src[--k]; + b[--i] = src[--k] ^ sign; + } + } else { + while (i > 1) { + b[--i] = src[--k] ^ sign; + b[--i] = src[--k]; + k--; + } } return j; } -static struct pcm_feederdesc feeder_24leto16le_desc[] = { +static struct pcm_feederdesc feeder_24to16_desc[] = { {FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_24leto16le_methods[] = { +static kobj_method_t feeder_24to16_methods[] = { KOBJMETHOD(feeder_init, feed_common_init), KOBJMETHOD(feeder_free, feed_common_free), - KOBJMETHOD(feeder_feed, feed_24leto16le), + KOBJMETHOD(feeder_feed, feed_24to16), {0, 0} }; -FEEDER_DECLARE(feeder_24leto16le, 1, NULL); +FEEDER_DECLARE(feeder_24to16, 1, NULL); static int -feed_16leto32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_16to32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + int i, j, sign, k = FEEDER_FEED(f->source, c, b, count >> 1, source); if (k < 2) { FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", __func__, k); return 0; } FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~1); + k &= ~1; i = k; j = k << 1; k = j; - while (i > 0) { - b[--j] = b[--i]; - b[--j] = b[--i]; - b[--j] = 0; - b[--j] = 0; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + b[--j] = 0; + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = 0; + b[--j] = 0; + } } return k; } -static struct pcm_feederdesc feeder_16leto32le_desc[] = { +static struct pcm_feederdesc feeder_16to32_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0}, {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0}, {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_16leto32le_methods[] = { - KOBJMETHOD(feeder_feed, feed_16leto32le), +static kobj_method_t feeder_16to32_methods[] = { + KOBJMETHOD(feeder_feed, feed_16to32), {0, 0} }; -FEEDER_DECLARE(feeder_16leto32le, 0, NULL); +FEEDER_DECLARE(feeder_16to32, 0, NULL); static int -feed_32leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_32to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { - int i, j, k; + int i, j, k, sign; uint8_t *src = (uint8_t *)f->data; k = count << 1; @@ -534,31 +666,177 @@ return 0; } FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~3); + k &= ~3; i = k; k >>= 1; j = k; - while (i > 0) { - b[--j] = src[--i]; - b[--j] = src[--i]; - i -= 2; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + i -= 2; + b[--j] = src[--i]; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 3) { + b[--j] = src[--i] ^ sign; + b[--j] = src[--i]; + i -= 2; + } } return k; } -static struct pcm_feederdesc feeder_32leto16le_desc[] = { +static struct pcm_feederdesc feeder_32to16_desc[] = { {FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_32leto16le_methods[] = { +static kobj_method_t feeder_32to16_methods[] = { KOBJMETHOD(feeder_init, feed_common_init), KOBJMETHOD(feeder_free, feed_common_free), - KOBJMETHOD(feeder_feed, feed_32leto16le), + KOBJMETHOD(feeder_feed, feed_32to16), {0, 0} }; -FEEDER_DECLARE(feeder_32leto16le, 1, NULL); +FEEDER_DECLARE(feeder_32to16, 1, NULL); + +static int +feed_24to32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k, sign; + + k = ((count & ~3) >> 2) * 3; + k = FEEDER_FEED(f->source, c, b, k, source); + if (k < 3) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__); + k -= k % 3; + i = k; + k = (k / 3) << 2; + j = k; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 2) { + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 2) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = 0; + } + } + return k; +} +static struct pcm_feederdesc feeder_24to32_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_24to32_methods[] = { + KOBJMETHOD(feeder_feed, feed_24to32), + {0, 0} +}; +FEEDER_DECLARE(feeder_24to32, 1, NULL); + +static int +feed_32to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k, sign; + uint8_t *src = (uint8_t *)f->data; + + k = (count / 3) << 2; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__); + k &= ~3; + i = k; + k = (k >> 2) * 3; + j = k; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + --i; + b[--j] = src[--i]; + b[--j] = src[--i]; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 3) { + b[--j] = src[--i] ^ sign; + b[--j] = src[--i]; + b[--j] = src[--i]; + --i; + } + } + return k; +} +static struct pcm_feederdesc feeder_32to24_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_32to24_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_32to24), + {0, 0} +}; +FEEDER_DECLARE(feeder_32to24, 1, NULL); /* * Bit conversion end */ @@ -583,6 +861,8 @@ static struct pcm_feederdesc feeder_monotostereo8_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_U8|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S8, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_MU_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_A_LAW|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; static kobj_method_t feeder_monotostereo8_methods[] = { @@ -604,10 +884,10 @@ return 0; } FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~1); + k &= ~1; i = k; j = k << 1; - while (i > 0) { + while (i > 1) { l = b[--i]; m = b[--i]; b[--j] = l; @@ -643,10 +923,10 @@ return 0; } FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__); - FMT_ALIGNBYTE(k -= k % 3); + k -= k % 3; i = k; j = k << 1; - while (i > 0) { + while (i > 2) { l = b[--i]; m = b[--i]; n = b[--i]; @@ -685,10 +965,10 @@ return 0; } FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~3); + k &= ~3; i = k; j = k << 1; - while (i > 0) { + while (i > 3) { l = b[--i]; m = b[--i]; n = b[--i]; @@ -738,7 +1018,7 @@ return 0; } FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~1); + k &= ~1; i = k >> 1; j = i; while (i > 0) { @@ -750,6 +1030,8 @@ static struct pcm_feederdesc feeder_stereotomono8_desc[] = { {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U8, 0}, {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_MU_LAW, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_A_LAW, 0}, {0, 0, 0, 0}, }; static kobj_method_t feeder_stereotomono8_methods[] = { @@ -775,10 +1057,10 @@ return 0; } FMT_TEST(k & 3, "%s: Bytes not 16bit (stereo) aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~3); + k &= ~3; i = k >> 1; j = i; - while (i > 0) { + while (i > 1) { k -= 2; b[--i] = src[--k]; b[--i] = src[--k]; @@ -815,10 +1097,10 @@ return 0; } FMT_TEST(k % 6, "%s: Bytes not 24bit (stereo) aligned.\n", __func__); - FMT_ALIGNBYTE(k -= k % 6); + k -= k % 6; i = k >> 1; j = i; - while (i > 0) { + while (i > 2) { k -= 3; b[--i] = src[--k]; b[--i] = src[--k]; @@ -856,10 +1138,10 @@ return 0; } FMT_TEST(k & 7, "%s: Bytes not 32bit (stereo) aligned.\n", __func__); - FMT_ALIGNBYTE(k &= ~7); + k &= ~7; i = k >> 1; j = i; - while (i > 0) { + while (i > 3) { k -= 4; b[--i] = src[--k]; b[--i] = src[--k]; @@ -914,7 +1196,7 @@ FEEDER_DECLARE(feeder_sign8, 0, NULL); static int -feed_sign16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_sign16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { int i, j = FEEDER_FEED(f->source, c, b, count, source); @@ -925,29 +1207,40 @@ return 0; } FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(j &= ~1); + j &= ~1; i = j; - while (i > 0) { - b[--i] ^= 0x80; - i--; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + i--; + b[--i] ^= 0x80; + } + } else { + while (i > 1) { + b[--i] ^= 0x80; + i--; + } } return j; } -static struct pcm_feederdesc feeder_sign16le_desc[] = { +static struct pcm_feederdesc feeder_sign16_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_sign16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign16le), +static kobj_method_t feeder_sign16_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign16), {0, 0} }; -FEEDER_DECLARE(feeder_sign16le, 0, NULL); +FEEDER_DECLARE(feeder_sign16, 0, NULL); static int -feed_sign24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_sign24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { int i, j = FEEDER_FEED(f->source, c, b, count, source); @@ -958,29 +1251,40 @@ return 0; } FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__); - FMT_ALIGNBYTE(j -= j % 3); + j -= j % 3; i = j; - while (i > 0) { - b[--i] ^= 0x80; - i -= 2; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 2) { + i -= 2; + b[--i] ^= 0x80; + } + } else { + while (i > 2) { + b[--i] ^= 0x80; + i -= 2; + } } return j; } -static struct pcm_feederdesc feeder_sign24le_desc[] = { +static struct pcm_feederdesc feeder_sign24_desc[] = { {FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0}, {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0}, {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_sign24le_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign24le), +static kobj_method_t feeder_sign24_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign24), {0, 0} }; -FEEDER_DECLARE(feeder_sign24le, 0, NULL); +FEEDER_DECLARE(feeder_sign24, 0, NULL); static int -feed_sign32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, +feed_sign32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, uint32_t count, void *source) { int i, j = FEEDER_FEED(f->source, c, b, count, source); @@ -991,26 +1295,37 @@ return 0; } FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__); - FMT_ALIGNBYTE(j &= ~3); + j &= ~3; i = j; - while (i > 0) { - b[--i] ^= 0x80; - i -= 3; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + i -= 3; + b[--i] ^= 0x80; + } + } else { + while (i > 3) { + b[--i] ^= 0x80; + i -= 3; + } } return j; } -static struct pcm_feederdesc feeder_sign32le_desc[] = { +static struct pcm_feederdesc feeder_sign32_desc[] = { {FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0}, {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0}, {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_sign32le_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign32le), +static kobj_method_t feeder_sign32_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign32), {0, 0} }; -FEEDER_DECLARE(feeder_sign32le, 0, NULL); +FEEDER_DECLARE(feeder_sign32, 0, NULL); /* * Sign conversion end. */ @@ -1031,9 +1346,9 @@ return 0; } FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__); - FMT_ALIGNBYTE(j &= ~1); + j &= ~1; i = j; - while (i > 0) { + while (i > 1) { v = b[--i]; b[i] = b[i - 1]; b[--i] = v; @@ -1070,9 +1385,9 @@ return 0; } FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__); - FMT_ALIGNBYTE(j -= j % 3); + j -= j % 3; i = j; - while (i > 0) { + while (i > 2) { v = b[--i]; b[i] = b[i - 2]; b[i -= 2] = v; @@ -1109,9 +1424,9 @@ return 0; } FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__); - FMT_ALIGNBYTE(j &= ~3); + j &= ~3; i = j; - while (i > 0) { + while (i > 3) { l = b[--i]; m = b[--i]; b[i] = b[i - 1]; Index: sys/dev/sound/pcm/feeder_rate.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/feeder_rate.c,v retrieving revision 1.17 diff -u -r1.17 feeder_rate.c --- sys/dev/sound/pcm/feeder_rate.c 25 Jan 2006 21:13:46 -0000 1.17 +++ sys/dev/sound/pcm/feeder_rate.c 27 Apr 2006 09:06:33 -0000 @@ -25,6 +25,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * 2006-02-21: + * ========== + * + * Major cleanup and overhaul to remove much redundant codes. + * Highlights: + * 1) Support for signed / unsigned 16, 24 and 32 bit, + * big / little endian, + * 2) Unlimited channels. + * * 2005-06-11: * ========== * @@ -68,30 +77,32 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.17 2006/01/25 21:13:46 joel Exp $"); -#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ -#define RATE_TEST(x, y) /* if (!(x)) printf y */ -#define RATE_TRACE(x...) /* printf(x) */ +#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ +#define RATE_TEST(x, y) /* if (!(x)) printf y */ +#define RATE_TRACE(x...) /* printf(x) */ MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); -#define FEEDBUFSZ 8192 -#define ROUNDHZ 25 +#define FEEDBUFSZ 65536 #define RATEMIN 4000 -/* 8000 * 138 or 11025 * 100 . This is insane, indeed! */ -#define RATEMAX 1102500 -#define MINGAIN 92 -#define MAXGAIN 96 - -#define FEEDRATE_CONVERT_64 0 -#define FEEDRATE_CONVERT_SCALE64 1 -#define FEEDRATE_CONVERT_SCALE32 2 -#define FEEDRATE_CONVERT_PLAIN 3 -#define FEEDRATE_CONVERT_FIXED 4 -#define FEEDRATE_CONVERT_OPTIMAL 5 -#define FEEDRATE_CONVERT_WORST 6 +/* Be reasonable. Playtime is over. */ +#define RATEMAX 256000 -#define FEEDRATE_64_MAXROLL 32 -#define FEEDRATE_32_MAXROLL 16 +/* + * Don't overflow 32bit integer, since everything is done + * within 32bit arithmetic. + */ +#define RATE_FACTOR_MIN 1 +#define RATE_FACTOR_MAX PCM_S24_MAX +#define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \ + (val) > RATE_FACTOR_MAX)) + +#define RATE_BUFSZ_MIN 8 +#define RATE_BUFSZ_MAX 131072 + +struct feed_rate_info; + +typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, uint8_t *, uint32_t); struct feed_rate_info { uint32_t src, dst; /* rounded source / destination rates */ @@ -99,37 +110,27 @@ uint32_t gx, gy; /* interpolation / decimation ratio */ uint32_t alpha; /* interpolation distance */ uint32_t pos, bpos; /* current sample / buffer positions */ - uint32_t bufsz; /* total buffer size */ + uint32_t bufsz; /* total buffer size limit */ + uint32_t bufsz_init; /* allocated buffer size */ + uint32_t channels; /* total channels */ + uint32_t bps; /* bytes-per-sample */ uint32_t stray; /* stray bytes */ - int32_t scale, roll; /* scale / roll factor */ - int16_t *buffer; - uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t); + uint8_t *buffer; + feed_rate_converter convert; }; -static uint32_t -feed_convert_64(struct feed_rate_info *, int16_t *, uint32_t); -static uint32_t -feed_convert_scale64(struct feed_rate_info *, int16_t *, uint32_t); -static uint32_t -feed_convert_scale32(struct feed_rate_info *, int16_t *, uint32_t); -static uint32_t -feed_convert_plain(struct feed_rate_info *, int16_t *, uint32_t); - int feeder_rate_ratemin = RATEMIN; int feeder_rate_ratemax = RATEMAX; -/* - * See 'Feeder Scaling Type' below.. - */ -static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL; -static int feeder_rate_buffersize = FEEDBUFSZ & ~1; +static int feeder_rate_buffersize = FEEDBUFSZ; +static int feeder_rate_round = 1; /* * sysctls.. I love sysctls.. */ TUNABLE_INT("hw.snd.feeder_rate_ratemin", &feeder_rate_ratemin); TUNABLE_INT("hw.snd.feeder_rate_ratemax", &feeder_rate_ratemin); -TUNABLE_INT("hw.snd.feeder_rate_scaling", &feeder_rate_scaling); TUNABLE_INT("hw.snd.feeder_rate_buffersize", &feeder_rate_buffersize); +TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round); static int sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS) @@ -138,14 +139,15 @@ val = feeder_rate_ratemin; err = sysctl_handle_int(oidp, &val, sizeof(val), req); - if (val < 1 || val >= feeder_rate_ratemax) - err = EINVAL; - else + if (RATE_FACTOR_SAFE(val) && val < feeder_rate_ratemax) feeder_rate_ratemin = val; + else + err = EINVAL; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemin, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", ""); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", + "minimum allowable rate"); static int sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS) @@ -154,81 +156,127 @@ val = feeder_rate_ratemax; err = sysctl_handle_int(oidp, &val, sizeof(val), req); - if (val <= feeder_rate_ratemin || val > 0x7fffff) - err = EINVAL; - else + if (RATE_FACTOR_SAFE(val) && val > feeder_rate_ratemin) feeder_rate_ratemax = val; + else + err = EINVAL; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemax, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", ""); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", + "maximum allowable rate"); static int -sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS) +sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS) { int err, val; - val = feeder_rate_scaling; + val = feeder_rate_buffersize; err = sysctl_handle_int(oidp, &val, sizeof(val), req); /* - * Feeder Scaling Type - * =================== - * - * 1. Plain 64bit (high precision) - * 2. 64bit scaling (high precision, CPU friendly, but can - * cause gain up/down). - * 3. 32bit scaling (somehow can cause hz roundup, gain - * up/down). - * 4. Plain copy (default if src == dst. Except if src == dst, - * this is the worst / silly conversion method!). - * - * Sysctl options:- - * - * 0 - Plain 64bit - no fallback. - * 1 - 64bit scaling - no fallback. - * 2 - 32bit scaling - no fallback. - * 3 - Plain copy - no fallback. - * 4 - Fixed rate. Means that, choose optimal conversion method - * without causing hz roundup. - * 32bit scaling (as long as hz roundup does not occur), - * 64bit scaling, Plain 64bit. - * 5 - Optimal / CPU friendly (DEFAULT). - * 32bit scaling, 64bit scaling, Plain 64bit - * 6 - Optimal to worst, no 64bit arithmetic involved. - * 32bit scaling, Plain copy. + * Don't waste too much kernel space */ - if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST) + if (val < RATE_BUFSZ_MIN || val > RATE_BUFSZ_MAX) err = EINVAL; else - feeder_rate_scaling = val; + feeder_rate_buffersize = val; return err; } -SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", ""); +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", + "default circular buffer size"); static int -sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS) +sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) { int err, val; - val = feeder_rate_buffersize; + val = feeder_rate_round; err = sysctl_handle_int(oidp, &val, sizeof(val), req); - /* - * Don't waste too much kernel space - */ - if (val < 2 || val > 65536) - err = EINVAL; + if (val == 0 || val == 1) + feeder_rate_round = val; else - feeder_rate_buffersize = val & ~1; + err = EINVAL; return err; } -SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", ""); +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", + "allow 25Hz rate-rounding to avoid large integer multiplication and division"); + +#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static uint32_t \ +feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ + uint8_t *dst, uint32_t max) \ +{ \ + uint32_t ret, smpsz, bps, ch, pos, bpos, gx, gy, alpha, distance; \ + int32_t x, y; \ + int i; \ + uint8_t *src, *sx, *sy; \ + \ + ret = 0; \ + alpha = info->alpha; \ + gx = info->gx; \ + gy = info->gy; \ + pos = info->pos; \ + bpos = info->bpos; \ + src = info->buffer + pos; \ + ch = info->channels; \ + bps = info->bps; \ + smpsz = bps * ch; \ + for (;;) { \ + if (alpha < gx) { \ + alpha += gy; \ + pos += smpsz; \ + if (pos == bpos) \ + break; \ + src += smpsz; \ + } else { \ + alpha -= gx; \ + distance = (alpha << PCM_FXSHIFT) / gy; \ + sx = src - smpsz; \ + sy = src; \ + i = ch; \ + do { \ + x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ + y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ + x = (((RATE_INTCAST)x * distance) + \ + ((RATE_INTCAST)y * ((1 << PCM_FXSHIFT) - \ + distance))) >> PCM_FXSHIFT; \ + PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ + dst += bps; \ + sx += bps; \ + sy += bps; \ + ret += bps; \ + } while (--i); \ + if (ret == max) \ + break; \ + } \ + } \ + info->alpha = alpha; \ + info->pos = pos; \ + return ret; \ +} + +FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) +FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) +FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) +FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) +FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) +FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) +FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) +/* unsigned */ +FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) +FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) +FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) +FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) +FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) +FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) +FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) static void -feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy) +feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) { - uint32_t w, src = x, dst = y; + uint32_t w, x = src, y = dst; while (y != 0) { w = x % y; @@ -240,242 +288,123 @@ } static void -feed_scale_roll(uint32_t dst, int32_t *scale, int32_t *roll, int32_t max) -{ - int64_t k, tscale; - int32_t j, troll; - - *scale = *roll = -1; - for (j = MAXGAIN; j >= MINGAIN; j -= 3) { - for (troll = 0; troll < max; troll++) { - tscale = (1 << troll) / dst; - k = (tscale * dst * 100) >> troll; - if (k > j && k <= 100) { - *scale = tscale; - *roll = troll; - return; - } - } - } -} - -static int -feed_get_best_coef(uint32_t *src, uint32_t *dst, uint32_t *gx, uint32_t *gy, - int32_t *scale, int32_t *roll) -{ - uint32_t tsrc, tdst, sscale, dscale; - int32_t tscale, troll; - int i, j, hzmin, hzmax; - - *scale = *roll = -1; - for (i = 0; i < 2; i++) { - hzmin = (ROUNDHZ * i) + 1; - hzmax = hzmin + ROUNDHZ; - for (j = hzmin; j < hzmax; j++) { - tsrc = *src - (*src % j); - tdst = *dst; - if (tsrc < 1 || tdst < 1) - goto coef_failed; - feed_speed_ratio(tsrc, tdst, &sscale, &dscale); - feed_scale_roll(dscale, &tscale, &troll, - FEEDRATE_32_MAXROLL); - if (tscale != -1 && troll != -1) { - *src = tsrc; - *gx = sscale; - *gy = dscale; - *scale = tscale; - *roll = troll; - return j; - } - } - for (j = hzmin; j < hzmax; j++) { - tsrc = *src - (*src % j); - tdst = *dst - (*dst % j); - if (tsrc < 1 || tdst < 1) - goto coef_failed; - feed_speed_ratio(tsrc, tdst, &sscale, &dscale); - feed_scale_roll(dscale, &tscale, &troll, - FEEDRATE_32_MAXROLL); - if (tscale != -1 && troll != -1) { - *src = tsrc; - *dst = tdst; - *gx = sscale; - *gy = dscale; - *scale = tscale; - *roll = troll; - return j; - } - } - for (j = hzmin; j < hzmax; j++) { - tsrc = *src; - tdst = *dst - (*dst % j); - if (tsrc < 1 || tdst < 1) - goto coef_failed; - feed_speed_ratio(tsrc, tdst, &sscale, &dscale); - feed_scale_roll(dscale, &tscale, &troll, - FEEDRATE_32_MAXROLL); - if (tscale != -1 && troll != -1) { - *src = tsrc; - *dst = tdst; - *gx = sscale; - *gy = dscale; - *scale = tscale; - *roll = troll; - return j; - } - } - } -coef_failed: - feed_speed_ratio(*src, *dst, gx, gy); - feed_scale_roll(*gy, scale, roll, FEEDRATE_32_MAXROLL); - return 0; -} - -static void feed_rate_reset(struct feed_rate_info *info) { - info->scale = -1; - info->roll = -1; - info->src = info->rsrc; - info->dst = info->rdst; - info->gx = 0; - info->gy = 0; + info->src = info->rsrc - (info->rsrc % (feeder_rate_round ? 25 : 1)); + info->dst = info->rdst - (info->rdst % (feeder_rate_round ? 25 : 1)); + info->gx = 1; + info->gy = 1; + info->alpha = 0; + info->channels = 2; + info->bps = 2; + info->convert = NULL; + info->bufsz = info->bufsz_init; + info->pos = 4; + info->bpos = 8; + info->stray = 0; } static int feed_rate_setup(struct pcm_feeder *f) { struct feed_rate_info *info = f->data; - int r = 0; + static const struct { + uint32_t format; /* pcm / audio format */ + uint32_t channels; /* total channels */ + uint32_t bps; /* bytes-per-sample, regardless of + total channels */ + feed_rate_converter convert; + } convtbl[] = { + { AFMT_S8, 1, PCM_8_BPS, feed_convert_s8ne }, + { AFMT_S16_LE, 1, PCM_16_BPS, feed_convert_s16le }, + { AFMT_S24_LE, 1, PCM_24_BPS, feed_convert_s24le }, + { AFMT_S32_LE, 1, PCM_32_BPS, feed_convert_s32le }, + { AFMT_S16_BE, 1, PCM_16_BPS, feed_convert_s16be }, + { AFMT_S24_BE, 1, PCM_24_BPS, feed_convert_s24be }, + { AFMT_S32_BE, 1, PCM_32_BPS, feed_convert_s32be }, + { AFMT_S8 | AFMT_STEREO, 2, PCM_8_BPS, feed_convert_s8ne }, + { AFMT_S16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_convert_s16le }, + { AFMT_S24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_convert_s24le }, + { AFMT_S32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_convert_s32le }, + { AFMT_S16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_convert_s16be }, + { AFMT_S24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_convert_s24be }, + { AFMT_S32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_convert_s32be }, + /* unsigned */ + { AFMT_U8, 1, PCM_8_BPS, feed_convert_u8ne }, + { AFMT_U16_LE, 1, PCM_16_BPS, feed_convert_u16le }, + { AFMT_U24_LE, 1, PCM_24_BPS, feed_convert_u24le }, + { AFMT_U32_LE, 1, PCM_32_BPS, feed_convert_u32le }, + { AFMT_U16_BE, 1, PCM_16_BPS, feed_convert_u16be }, + { AFMT_U24_BE, 1, PCM_24_BPS, feed_convert_u24be }, + { AFMT_U32_BE, 1, PCM_32_BPS, feed_convert_u32be }, + { AFMT_U8 | AFMT_STEREO, 2, PCM_8_BPS, feed_convert_u8ne }, + { AFMT_U16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_convert_u16le }, + { AFMT_U24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_convert_u24le }, + { AFMT_U32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_convert_u32le }, + { AFMT_U16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_convert_u16be }, + { AFMT_U24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_convert_u24be }, + { AFMT_U32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_convert_u32be }, + { 0, 0, 0, NULL }, + }; + uint32_t i; - info->pos = 2; - info->bpos = 4; - info->alpha = 0; - info->stray = 0; feed_rate_reset(info); - if (info->src == info->dst) { - /* - * No conversion ever needed. Just do plain copy. - */ - info->convert = feed_convert_plain; - info->gx = 1; - info->gy = 1; - } else { - switch (feeder_rate_scaling) { - case FEEDRATE_CONVERT_64: - feed_speed_ratio(info->src, info->dst, - &info->gx, &info->gy); - info->convert = feed_convert_64; - break; - case FEEDRATE_CONVERT_SCALE64: - feed_speed_ratio(info->src, info->dst, - &info->gx, &info->gy); - feed_scale_roll(info->gy, &info->scale, - &info->roll, FEEDRATE_64_MAXROLL); - if (info->scale == -1 || info->roll == -1) - return -1; - info->convert = feed_convert_scale64; - break; - case FEEDRATE_CONVERT_SCALE32: - r = feed_get_best_coef(&info->src, &info->dst, - &info->gx, &info->gy, &info->scale, - &info->roll); - if (r == 0) - return -1; - info->convert = feed_convert_scale32; - break; - case FEEDRATE_CONVERT_PLAIN: - feed_speed_ratio(info->src, info->dst, + + if (info->src != info->dst) + feed_speed_ratio(info->src, info->dst, &info->gx, &info->gy); - info->convert = feed_convert_plain; - break; - case FEEDRATE_CONVERT_FIXED: - r = feed_get_best_coef(&info->src, &info->dst, - &info->gx, &info->gy, &info->scale, - &info->roll); - if (r != 0 && info->src == info->rsrc && - info->dst == info->rdst) - info->convert = feed_convert_scale32; - else { - /* Fallback */ - feed_rate_reset(info); - feed_speed_ratio(info->src, info->dst, - &info->gx, &info->gy); - feed_scale_roll(info->gy, &info->scale, - &info->roll, FEEDRATE_64_MAXROLL); - if (info->scale != -1 && info->roll != -1) - info->convert = feed_convert_scale64; - else - info->convert = feed_convert_64; - } - break; - case FEEDRATE_CONVERT_OPTIMAL: - r = feed_get_best_coef(&info->src, &info->dst, - &info->gx, &info->gy, &info->scale, - &info->roll); - if (r != 0) - info->convert = feed_convert_scale32; - else { - /* Fallback */ - feed_rate_reset(info); - feed_speed_ratio(info->src, info->dst, - &info->gx, &info->gy); - feed_scale_roll(info->gy, &info->scale, - &info->roll, FEEDRATE_64_MAXROLL); - if (info->scale != -1 && info->roll != -1) - info->convert = feed_convert_scale64; - else - info->convert = feed_convert_64; - } - break; - case FEEDRATE_CONVERT_WORST: - r = feed_get_best_coef(&info->src, &info->dst, - &info->gx, &info->gy, &info->scale, - &info->roll); - if (r != 0) - info->convert = feed_convert_scale32; - else { - /* Fallback */ - feed_rate_reset(info); - feed_speed_ratio(info->src, info->dst, - &info->gx, &info->gy); - info->convert = feed_convert_plain; - } - break; - default: - return -1; - break; - } - /* No way! */ - if (info->gx == 0 || info->gy == 0) + + if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) + return -1; + + for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) { + if (convtbl[i].format == 0) return -1; - /* - * No need to interpolate/decimate, just do plain copy. - * This probably caused by Hz roundup. - */ - if (info->gx == info->gy) - info->convert = feed_convert_plain; + if (f->desc->out == convtbl[i].format) { + info->channels = convtbl[i].channels; + info->bps = convtbl[i].bps; + info->convert = convtbl[i].convert; + break; + } } + + /* + * No need to interpolate/decimate, just do plain copy. + */ + if (info->gx == info->gy) + info->convert = NULL; + + info->pos = info->bps * info->channels; + info->bpos = info->pos << 1; + info->bufsz -= info->bufsz % info->pos; + + RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , " + "format=0x%08x, channels=%u, bufsz=%u\n", + __func__, info->src, info->rsrc, info->dst, info->rdst, + info->gx, info->gy, + f->desc->out, info->channels, + info->bufsz - (info->bpos >> 1)); + return 0; } static int -feed_rate_set(struct pcm_feeder *f, int what, int value) +feed_rate_set(struct pcm_feeder *f, int what, int32_t value) { struct feed_rate_info *info = f->data; if (value < feeder_rate_ratemin || value > feeder_rate_ratemax) return -1; - + switch (what) { - case FEEDRATE_SRC: - info->rsrc = value; - break; - case FEEDRATE_DST: - info->rdst = value; - break; - default: - return -1; + case FEEDRATE_SRC: + info->rsrc = value; + break; + case FEEDRATE_DST: + info->rdst = value; + break; + default: + return -1; } return feed_rate_setup(f); } @@ -485,16 +414,13 @@ { struct feed_rate_info *info = f->data; - /* - * Return *real* src/dst rate. - */ switch (what) { - case FEEDRATE_SRC: - return info->rsrc; - case FEEDRATE_DST: - return info->rdst; - default: - return -1; + case FEEDRATE_SRC: + return info->rsrc; + case FEEDRATE_DST: + return info->rdst; + default: + return -1; } return -1; } @@ -504,14 +430,17 @@ { struct feed_rate_info *info; + if (f->desc->out != f->desc->in) + return EINVAL; + info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); if (info == NULL) return ENOMEM; /* - * bufsz = sample from last cycle + conversion space + * bufsz_init = sample from last cycle + conversion space */ - info->bufsz = 2 + feeder_rate_buffersize; - info->buffer = malloc(sizeof(*info->buffer) * info->bufsz, + info->bufsz_init = 8 + feeder_rate_buffersize; + info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init, M_RATEFEEDER, M_NOWAIT | M_ZERO); if (info->buffer == NULL) { free(info, M_RATEFEEDER); @@ -537,233 +466,88 @@ return 0; } -static uint32_t -feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max) -{ - int64_t x, alpha, distance; - uint32_t ret; - int32_t pos, bpos, gx, gy; - int16_t *src; - /* - * Plain, straight forward 64bit arith. No bit-magic applied here. - */ - ret = 0; - alpha = info->alpha; - gx = info->gx; - gy = info->gy; - pos = info->pos; - bpos = info->bpos; - src = info->buffer; - for (;;) { - if (alpha < gx) { - alpha += gy; - pos += 2; - if (pos == bpos) - break; - } else { - alpha -= gx; - distance = gy - alpha; - x = (alpha * src[pos - 2]) + (distance * src[pos]); - dst[ret++] = x / gy; - x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); - dst[ret++] = x / gy; - if (ret == max) - break; - } - } - info->alpha = alpha; - info->pos = pos; - return ret; -} - -static uint32_t -feed_convert_scale64(struct feed_rate_info *info, int16_t *dst, uint32_t max) -{ - int64_t x, alpha, distance; - uint32_t ret; - int32_t pos, bpos, gx, gy, roll; - int16_t *src; - /* - * 64bit scaling. - */ - ret = 0; - roll = info->roll; - alpha = info->alpha * info->scale; - gx = info->gx * info->scale; - gy = info->gy * info->scale; - pos = info->pos; - bpos = info->bpos; - src = info->buffer; - for (;;) { - if (alpha < gx) { - alpha += gy; - pos += 2; - if (pos == bpos) - break; - } else { - alpha -= gx; - distance = gy - alpha; - x = (alpha * src[pos - 2]) + (distance * src[pos]); - dst[ret++] = x >> roll; - x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); - dst[ret++] = x >> roll; - if (ret == max) - break; - } - } - info->alpha = alpha / info->scale; - info->pos = pos; - return ret; -} - -static uint32_t -feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max) -{ - uint32_t ret; - int32_t x, pos, bpos, gx, gy, alpha, roll, distance; - int16_t *src; - /* - * 32bit scaling. - */ - ret = 0; - roll = info->roll; - alpha = info->alpha * info->scale; - gx = info->gx * info->scale; - gy = info->gy * info->scale; - pos = info->pos; - bpos = info->bpos; - src = info->buffer; - for (;;) { - if (alpha < gx) { - alpha += gy; - pos += 2; - if (pos == bpos) - break; - } else { - alpha -= gx; - distance = gy - alpha; - x = (alpha * src[pos - 2]) + (distance * src[pos]); - dst[ret++] = x >> roll; - x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); - dst[ret++] = x >> roll; - if (ret == max) - break; - } - } - info->alpha = alpha / info->scale; - info->pos = pos; - return ret; -} - -static uint32_t -feed_convert_plain(struct feed_rate_info *info, int16_t *dst, uint32_t max) -{ - uint32_t ret; - int32_t pos, bpos, gx, gy, alpha; - int16_t *src; - /* - * Plain copy. - */ - ret = 0; - gx = info->gx; - gy = info->gy; - alpha = info->alpha; - pos = info->pos; - bpos = info->bpos; - src = info->buffer; - for (;;) { - if (alpha < gx) { - alpha += gy; - pos += 2; - if (pos == bpos) - break; - } else { - alpha -= gx; - dst[ret++] = src[pos]; - dst[ret++] = src[pos + 1]; - if (ret == max) - break; - } - } - info->pos = pos; - info->alpha = alpha; - return ret; -} - -static int32_t +static int feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) + uint32_t count, void *source) { struct feed_rate_info *info = f->data; - uint32_t i; + uint32_t i, smpsz; int32_t fetch, slot; - int16_t *dst = (int16_t *)b; + + if (info->convert == NULL) + return FEEDER_FEED(f->source, c, b, count, source); + /* * This loop has been optimized to generalize both up / down * sampling without causing missing samples or excessive buffer - * feeding. + * feeding. The tricky part is to calculate *precise* (slot) value + * needed for the entire conversion space since we are bound to + * return and fill up the buffer according to the requested 'count'. + * Too much feeding will cause the extra buffer stay within temporary + * circular buffer forever and always manifest itself as a truncated + * sound during end of playback / recording. Too few, and we end up + * with possible underruns and waste of cpu cycles. + * + * 'Stray' management exist to combat with possible unaligned + * buffering by the caller. */ - RATE_TEST(count >= 4 && (count & 3) == 0, - ("%s: Count size not byte integral (%d)\n", __func__, count)); - if (count < 4) + smpsz = info->bps * info->channels; + RATE_TEST(count >= smpsz && (count % smpsz) == 0, + ("%s: Count size not sample integral (%d)\n", __func__, count)); + if (count < smpsz) return 0; - count >>= 1; - count &= ~1; - slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1; - RATE_TEST((slot & 1) == 0, ("%s: Slot count not sample integral (%d)\n", - __func__, slot)); + count -= count % smpsz; /* - * Optimize buffer feeding aggressively to ensure calculated slot - * can be fitted nicely into available buffer free space, hence - * avoiding multiple feeding. + * This slot count formula will stay here for the next million years + * to come. This is the key of our circular buffering precision. */ + slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / info->gy) * smpsz; + RATE_TEST((slot % smpsz) == 0, ("%s: Slot count not sample integral (%d)\n", + __func__, slot)); RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", __func__,info->stray)); - if (info->pos != 2 && info->bpos - info->pos == 2 && + if (info->pos != smpsz && info->bpos - info->pos == smpsz && info->bpos + slot > info->bufsz) { /* * Copy last unit sample and its previous to * beginning of buffer. */ - info->buffer[0] = info->buffer[info->pos - 2]; - info->buffer[1] = info->buffer[info->pos - 1]; - info->buffer[2] = info->buffer[info->pos]; - info->buffer[3] = info->buffer[info->pos + 1]; - info->pos = 2; - info->bpos = 4; + bcopy(info->buffer + info->pos - smpsz, info->buffer, + sizeof(*info->buffer) * (smpsz << 1)); + info->pos = smpsz; + info->bpos = smpsz << 1; } RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", __func__, slot)); i = 0; for (;;) { for (;;) { - fetch = (info->bufsz - info->bpos) << 1; + fetch = info->bufsz - info->bpos; fetch -= info->stray; RATE_ASSERT(fetch >= 0, ("%s: [1] Buffer overrun: %d > %d\n", __func__, info->bpos, info->bufsz)); - if ((slot << 1) < fetch) - fetch = slot << 1; + if (slot < fetch) + fetch = slot; if (fetch > 0) { - RATE_ASSERT(((info->bpos << 1) - info->stray) >= 0 && - ((info->bpos << 1) - info->stray) < (info->bufsz << 1), + RATE_ASSERT((int32_t)(info->bpos - info->stray) >= 0 && + (info->bpos - info->stray) < info->bufsz, ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", __func__, - info->bufsz << 1, (info->bpos << 1) - info->stray)); + info->bufsz, info->bpos - info->stray)); fetch = FEEDER_FEED(f->source, c, - (uint8_t *)(info->buffer) + (info->bpos << 1) - info->stray, + info->buffer + info->bpos - info->stray, fetch, source); info->stray = 0; if (fetch == 0) break; - RATE_TEST((fetch & 3) == 0, - ("%s: Fetch size not byte integral (%d)\n", + RATE_TEST((fetch % smpsz) == 0, + ("%s: Fetch size not sample integral (%d)\n", __func__, fetch)); - info->stray += fetch & 3; + info->stray += fetch % smpsz; RATE_TEST(info->stray == 0, ("%s: Stray bytes detected (%d)\n", __func__, info->stray)); - fetch >>= 1; - fetch &= ~1; + fetch -= fetch % smpsz; info->bpos += fetch; slot -= fetch; RATE_ASSERT(slot >= 0, @@ -777,7 +561,7 @@ break; } if (info->pos == info->bpos) { - RATE_TEST(info->pos == 2, + RATE_TEST(info->pos == smpsz, ("%s: EOF while in progress\n", __func__)); break; } @@ -786,10 +570,10 @@ info->pos, info->bpos)); RATE_ASSERT(info->pos < info->bpos, ("%s: Zero buffer!\n", __func__)); - RATE_ASSERT(((info->bpos - info->pos) & 1) == 0, + RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0, ("%s: Buffer not sample integral (%d)\n", __func__, info->bpos - info->pos)); - i += info->convert(info, dst + i, count - i); + i += info->convert(info, b + i, count - i); RATE_ASSERT(info->pos <= info->bpos, ("%s: [3] Buffer overrun: %d > %d\n", __func__, info->pos, info->bpos)); @@ -800,25 +584,60 @@ * interpolate using it. */ RATE_TEST(info->stray == 0, ("%s: [2] Stray bytes: %u\n", __func__, info->stray)); - info->buffer[0] = info->buffer[info->pos - 2]; - info->buffer[1] = info->buffer[info->pos - 1]; - info->bpos = 2; - info->pos = 2; + bcopy(info->buffer + info->pos - smpsz, info->buffer, + sizeof(*info->buffer) * smpsz); + info->bpos = smpsz; + info->pos = smpsz; } if (i == count) break; } -#if 0 - RATE_TEST(count == i, ("Expect: %u , Got: %u\n", count << 1, i << 1)); -#endif + + RATE_TEST((slot == 0 && count == i) || + (slot > 0 && count > i && + info->pos == info->bpos && info->pos == smpsz), + ("%s: Inconsistent slot/count! " + "Count Expect: %u , Got: %u, Slot Left: %d\n", + __func__, count, i, slot)); + RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, info->stray)); - return i << 1; + + return i; } static struct pcm_feederdesc feeder_rate_desc[] = { + {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, + {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0}, + {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0}, + {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0}, + {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0}, + {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0}, + {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0}, + {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, + /* unsigned */ + {FEEDER_RATE, AFMT_U8, AFMT_U8, 0}, + {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0}, + {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0}, + {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0}, + {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0}, + {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0}, + {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0}, + {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, + {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, {0, 0, 0, 0}, }; + static kobj_method_t feeder_rate_methods[] = { KOBJMETHOD(feeder_init, feed_rate_init), KOBJMETHOD(feeder_free, feed_rate_free), @@ -827,4 +646,5 @@ KOBJMETHOD(feeder_feed, feed_rate), {0, 0} }; + FEEDER_DECLARE(feeder_rate, 2, NULL); Index: sys/dev/sound/pcm/feeder_volume.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/feeder_volume.c,v retrieving revision 1.2 diff -u -r1.2 feeder_volume.c --- sys/dev/sound/pcm/feeder_volume.c 14 Nov 2005 18:37:59 -0000 1.2 +++ sys/dev/sound/pcm/feeder_volume.c 27 Apr 2006 09:06:33 -0000 @@ -31,48 +31,190 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_volume.c,v 1.2 2005/11/14 18:37:59 ariff Exp $"); +MALLOC_DEFINE(M_VOLUMEFEEDER, "volumefeed", "pcm volume feeder"); + +#define FVOL_TRACE(x...) /* device_printf(c->dev, x) */ +#define FVOL_TEST(x, y...) /* if (x) FVOL_TRACE(y) */ + +#define FVOL_RESOLUTION 6 /* 6bit volume resolution */ +#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / 100) +#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f) +#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8) +#define FVOL_MAX (1 << FVOL_RESOLUTION) +#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION) + +struct feed_volume_info; + +typedef uint32_t (*feed_volume_filter)(struct feed_volume_info *, + uint8_t *, int *, uint32_t); + +struct feed_volume_info { + uint32_t bps, channels; + feed_volume_filter filter; +}; + +#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static uint32_t \ +feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(struct feed_volume_info *info, \ + uint8_t *b, int *vol, uint32_t count) \ +{ \ + uint32_t bps; \ + int32_t j; \ + int i; \ + \ + bps = info->bps; \ + i = count; \ + b += i; \ + while (i > 0) { \ + b -= bps; \ + i -= bps; \ + j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \ + j = FVOL_CALC((VOL_INTCAST)j, vol[(i / bps) & 1]); \ + PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \ + } \ + return count; \ +} + +FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne) +FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le) +FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le) +FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le) +FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be) +FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be) +FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be) +/* unsigned */ +FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne) +FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le) +FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le) +FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le) +FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be) +FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be) +FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be) + +static int +feed_volume_setup(struct pcm_feeder *f) +{ + struct feed_volume_info *info = f->data; + static const struct { + uint32_t format; /* pcm / audio format */ + uint32_t channels; /* total channels */ + uint32_t bps; /* bytes-per-sample, regardless of + total channels */ + feed_volume_filter filter; + } voltbl[] = { + { AFMT_S8 | AFMT_STEREO, 2, PCM_8_BPS, feed_volume_filter_s8ne }, + { AFMT_S16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_volume_filter_s16le }, + { AFMT_S24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_volume_filter_s24le }, + { AFMT_S32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_volume_filter_s32le }, + { AFMT_S16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_volume_filter_s16be }, + { AFMT_S24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_volume_filter_s24be }, + { AFMT_S32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_volume_filter_s32be }, + /* unsigned */ + { AFMT_U8 | AFMT_STEREO, 2, PCM_8_BPS, feed_volume_filter_u8ne }, + { AFMT_U16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_volume_filter_u16le }, + { AFMT_U24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_volume_filter_u24le }, + { AFMT_U32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_volume_filter_u32le }, + { AFMT_U16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_volume_filter_u16be }, + { AFMT_U24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_volume_filter_u24be }, + { AFMT_U32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_volume_filter_u32be }, + { 0, 0, 0, NULL }, + }; + uint32_t i; + + for (i = 0; i < sizeof(voltbl) / sizeof(*voltbl); i++) { + if (voltbl[i].format == 0) + return -1; + if (f->desc->out == voltbl[i].format) { + info->channels = voltbl[i].channels; + info->bps = voltbl[i].bps; + info->filter = voltbl[i].filter; + break; + } + } + + return 0; +} + +static int +feed_volume_init(struct pcm_feeder *f) +{ + struct feed_volume_info *info; + + if (f->desc->in != f->desc->out) + return EINVAL; + + /* Mandatory */ + if (!(f->desc->out & AFMT_STEREO)) + return EINVAL; + + info = malloc(sizeof(*info), M_VOLUMEFEEDER, M_NOWAIT | M_ZERO); + if (info == NULL) + return ENOMEM; + f->data = info; + return feed_volume_setup(f); +} + static int -feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) +feed_volume_free(struct pcm_feeder *f) { - int i, j, k, vol[2]; - int16_t *buf; + struct feed_volume_info *info = f->data; - k = FEEDER_FEED(f->source, c, b, count & ~1, source); - if (k < 2) { -#if 0 - device_printf(c->dev, "%s: Not enough data (Got: %d bytes)\n", + if (info) + free(info, M_VOLUMEFEEDER); + f->data = NULL; + return 0; +} + +static int +feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_volume_info *info = f->data; + uint32_t k, smpsz; + int vol[2]; + + vol[0] = FVOL_LEFT(c->volume); + vol[1] = FVOL_RIGHT(c->volume); + + if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) + return FEEDER_FEED(f->source, c, b, count, source); + + smpsz = info->bps * info->channels; + count -= count % smpsz; + k = FEEDER_FEED(f->source, c, b, count, source); + if (k < smpsz) { + FVOL_TRACE("%s: Not enough data (Got: %u bytes)\n", __func__, k); -#endif return 0; } -#if 0 - if (k & 1) - device_printf(c->dev, "%s: Bytes not 16bit aligned.\n", __func__); -#endif - k &= ~1; - i = k >> 1; - buf = (int16_t *)b; - vol[0] = c->volume & 0x7f; - vol[1] = (c->volume >> 8) & 0x7f; - while (i > 0) { - i--; - j = (vol[i & 1] * buf[i]) / 100; - if (j > 32767) - j = 32767; - if (j < -32768) - j = -32768; - buf[i] = j; - } - return k; + FVOL_TEST(k % smpsz, "%s: Bytes not %dbit (stereo) aligned.\n", + __func__, info->bps << 3); + k -= k % smpsz; + return info->filter(info, b, vol, k); } -static struct pcm_feederdesc feeder_volume_s16_desc[] = { - {FEEDER_VOLUME, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, +static struct pcm_feederdesc feeder_volume_desc[] = { + {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, + /* unsigned */ + {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, + {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, {0, 0, 0, 0}, }; -static kobj_method_t feeder_volume_s16_methods[] = { - KOBJMETHOD(feeder_feed, feed_volume_s16), +static kobj_method_t feeder_volume_methods[] = { + KOBJMETHOD(feeder_init, feed_volume_init), + KOBJMETHOD(feeder_free, feed_volume_free), + KOBJMETHOD(feeder_feed, feed_volume), {0, 0} }; -FEEDER_DECLARE(feeder_volume_s16, 2, NULL); +FEEDER_DECLARE(feeder_volume, 2, NULL); Index: sys/dev/sound/pcm/mixer.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/mixer.c,v retrieving revision 1.49 diff -u -r1.49 mixer.c --- sys/dev/sound/pcm/mixer.c 21 Mar 2006 06:35:48 -0000 1.49 +++ sys/dev/sound/pcm/mixer.c 27 Apr 2006 09:06:33 -0000 @@ -339,7 +339,7 @@ m = oidp->oid_arg1; snd_mtxlock(m->lock); - strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); + strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); snd_mtxunlock(m->lock); error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); snd_mtxlock(m->lock); Index: sys/dev/sound/pcm/sndstat.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/sndstat.c,v retrieving revision 1.22 diff -u -r1.22 sndstat.c --- sys/dev/sound/pcm/sndstat.c 2 Oct 2005 15:43:57 -0000 1.22 +++ sys/dev/sound/pcm/sndstat.c 27 Apr 2006 09:06:33 -0000 @@ -300,7 +300,8 @@ struct sndstat_entry *ent; int i, j; - sbuf_printf(s, "FreeBSD Audio Driver (newpcm)\n"); + sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit)\n", + (unsigned int)sizeof(intpcm_t) << 3); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); Index: sys/dev/sound/pcm/sound.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/sound.c,v retrieving revision 1.103 diff -u -r1.103 sound.c --- sys/dev/sound/pcm/sound.c 31 Mar 2006 10:36:36 -0000 1.103 +++ sys/dev/sound/pcm/sound.c 27 Apr 2006 09:06:33 -0000 @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -229,7 +230,7 @@ ORPHAN_CDEVT(sce->dsp_devt) && ORPHAN_CDEVT(sce->dspW_devt) && ORPHAN_CDEVT(sce->audio_devt) && - ORPHAN_CDEVT(sce->dspr_devt)) + ORPHAN_CDEVT(sce->dspHW_devt)) goto remok; /* * Either we're busy, or our cdev @@ -546,6 +547,7 @@ unsigned rdevcount; int device = device_get_unit(d->dev); size_t namelen; + char dtype; /* * Note it's confusing nomenclature. @@ -637,11 +639,20 @@ } #endif + if (ch->flags & CHN_F_VIRTUAL) + dtype = 'v'; + else if (ch->direction == PCMDIR_PLAY) + dtype = 'p'; + else if (ch->direction == PCMDIR_REC) + dtype = 'r'; + else + dtype = 'u'; /* we're screwed */ + namelen = strlen(ch->name); - if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ + if ((CHN_NAMELEN - namelen) > 11) { /* ":dspXX.TYYY" */ snprintf(ch->name + namelen, - CHN_NAMELEN - namelen, ":dsp%d.%d", - device, sce->chan_num); + CHN_NAMELEN - namelen, ":dsp%d.%c%d", + device, dtype, ch->num); } snd_mtxunlock(d->lock); @@ -663,11 +674,11 @@ UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", device, sce->chan_num); - if (ch->direction == PCMDIR_REC) - sce->dspr_devt = make_dev(&dsp_cdevsw, - PCMMKMINOR(device, SND_DEV_DSPREC, - sce->chan_num), UID_ROOT, GID_WHEEL, - 0666, "dspr%d.%d", device, sce->chan_num); + /* Except this. */ + sce->dspHW_devt = make_dev(&dsp_cdevsw, + PCMMKMINOR(device, SND_DEV_DSPHW, sce->chan_num), + UID_ROOT, GID_WHEEL, 0666, "dsp%d.%c%d", + device, dtype, ch->num); return 0; } @@ -760,7 +771,7 @@ struct snddev_info *d = device_get_softc(dev); snd_mtxlock(d->lock); - strncpy(d->status, str, SND_STATUSLEN); + strlcpy(d->status, str, SND_STATUSLEN); snd_mtxunlock(d->lock); if (snd_maxautovchans > 0) pcm_setvchans(d, 1); @@ -874,6 +885,9 @@ d->flags |= SD_F_AUTOVCHAN; vchan_initsys(dev); } + if ((d->flags & SD_F_AC97) && d->mixer_dev != NULL && + d->mixer_dev->si_drv1 != NULL) + ac97_initsys(dev); sndstat_register(dev, d->status, sndstat_prepare_pcm); return 0; @@ -933,9 +947,9 @@ destroy_dev(sce->audio_devt); sce->audio_devt = NULL; } - if (sce->dspr_devt) { - destroy_dev(sce->dspr_devt); - sce->dspr_devt = NULL; + if (sce->dspHW_devt) { + destroy_dev(sce->dspHW_devt); + sce->dspHW_devt = NULL; } d->devcount--; ch = sce->channel; Index: sys/dev/sound/pcm/sound.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/sound.h,v retrieving revision 1.68 diff -u -r1.68 sound.h --- sys/dev/sound/pcm/sound.h 21 Mar 2006 06:35:48 -0000 1.68 +++ sys/dev/sound/pcm/sound.h 27 Apr 2006 09:06:33 -0000 @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,7 @@ #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 #define SD_F_SOFTVOL 0x00000004 +#define SD_F_AC97 0x00000008 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) @@ -147,6 +149,281 @@ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) +/* + * Macros for reading/writing PCM sample / int values from bytes array. + * Since every process is done using signed integer (and to make our life + * less miserable), unsigned sample will be converted to its signed + * counterpart and restored during writing back. To avoid overflow, + * we truncate 32bit (and only 32bit) samples down to 24bit (see below + * for the reason), unless PCM_USE_64BIT_ARITH is defined. + */ + +/* + * Automatically turn on 64bit arithmetic on suitable archs + * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. + */ +#if LONG_BIT >= 64 +#undef PCM_USE_64BIT_ARITH +#define PCM_USE_64BIT_ARITH 1 +#else +#if 0 +#undef PCM_USE_64BIT_ARITH +#define PCM_USE_64BIT_ARITH 1 +#endif +#endif + +#ifdef PCM_USE_64BIT_ARITH +typedef int64_t intpcm_t; +#else +typedef int32_t intpcm_t; +#endif + +/* 32bit fixed point shift */ +#define PCM_FXSHIFT 8 + +#define PCM_S8_MAX 0x7f +#define PCM_S8_MIN -0x80 +#define PCM_S16_MAX 0x7fff +#define PCM_S16_MIN -0x8000 +#define PCM_S24_MAX 0x7fffff +#define PCM_S24_MIN -0x800000 +#ifdef PCM_USE_64BIT_ARITH +#if LONG_BIT >= 64 +#define PCM_S32_MAX 0x7fffffffL +#define PCM_S32_MIN -0x80000000L +#else +#define PCM_S32_MAX 0x7fffffffLL +#define PCM_S32_MIN -0x80000000LL +#endif +#else +#define PCM_S32_MAX 0x7fffffff +#define PCM_S32_MIN (-0x7fffffff - 1) +#endif + +/* Bytes-per-sample definition */ +#define PCM_8_BPS 1 +#define PCM_16_BPS 2 +#define PCM_24_BPS 3 +#define PCM_32_BPS 4 + +#if BYTE_ORDER == LITTLE_ENDIAN +#define PCM_READ_S16_LE(b8) *((int16_t *)(b8)) +#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8)) +#define PCM_READ_S16_BE(b8) \ + ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8)) +#define _PCM_READ_S32_BE(b8) \ + ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ + ((int8_t)((b8)[0])) << 24)) + +#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val) +#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val) +#define PCM_WRITE_S16_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = val >> 8; \ + } while(0) +#define _PCM_WRITE_S32_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = val >> 24; \ + } while(0) + +#define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) +#define PCM_READ_U16_BE(b8) \ + ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8)) +#define _PCM_READ_U32_BE(b8) \ + ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ + ((int8_t)((b8)[0] ^ 0x80)) << 24)) + +#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 +#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 +#define PCM_WRITE_U16_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = (val >> 8) ^ 0x80; \ + } while(0) +#define _PCM_WRITE_U32_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = (val >> 24) ^ 0x80; \ + } while(0) +#else /* !LITTLE_ENDIAN */ +#define PCM_READ_S16_LE(b8) \ + ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8)) +#define _PCM_READ_S32_LE(b8) \ + ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ + ((int8_t)((b8)[3])) << 24)) +#define PCM_READ_S16_BE(b8) *((int16_t *)(b8)) +#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8)) + +#define PCM_WRITE_S16_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + } while(0) +#define _PCM_WRITE_S32_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = val >> 24; \ + } while(0) +#define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val) +#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val) + +#define PCM_READ_U16_LE(b8) \ + ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8)) +#define _PCM_READ_U32_LE(b8) \ + ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ + ((int8_t)((b8)[3] ^ 0x80)) << 24)) +#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) + +#define PCM_WRITE_U16_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = (val >> 8) ^ 0x80; \ + } while(0) +#define _PCM_WRITE_U32_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = (val >> 24) ^ 0x80; \ + } while(0) +#define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 +#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 +#endif + +#define PCM_READ_S24_LE(b8) \ + ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16)) +#define PCM_READ_S24_BE(b8) \ + ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16)) + +#define PCM_WRITE_S24_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + } while(0) +#define PCM_WRITE_S24_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = val >> 16; \ + } while(0) + +#define PCM_READ_U24_LE(b8) \ + ((int32_t)((b8)[0] | (b8)[1] << 8 | \ + ((int8_t)((b8)[2] ^ 0x80)) << 16)) +#define PCM_READ_U24_BE(b8) \ + ((int32_t)((b8)[2] | (b8)[1] << 8 | \ + ((int8_t)((b8)[0] ^ 0x80)) << 16)) + +#define PCM_WRITE_U24_LE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = (val >> 16) ^ 0x80; \ + } while(0) +#define PCM_WRITE_U24_BE(bb8, vval) do { \ + int32_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = (val >> 16) ^ 0x80; \ + } while(0) + +#ifdef PCM_USE_64BIT_ARITH +#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) +#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) +#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) +#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) + +#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) +#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) +#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) +#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) +#else /* !PCM_USE_64BIT_ARITH */ +/* + * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: + * Dynamic range for: + * 1) Human =~ 140db + * 2) 16bit = 96db (close enough) + * 3) 24bit = 144db (perfect) + * 4) 32bit = 196db (way too much) + * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db + * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit + * is pretty much sufficient for our signed integer processing. + */ +#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) +#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) + +#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) +#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) +#endif + +/* + * 8bit sample is pretty much useless since it doesn't provide + * sufficient dynamic range throughout our filtering process. + * For the sake of completeness, declare it anyway. + */ +#define PCM_READ_S8(b8) *((int8_t *)(b8)) +#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8) +#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) +#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8) + +#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val) +#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) +#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80 +#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) + +#define PCM_CLAMP_S8(val) \ + (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ + (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) +#define PCM_CLAMP_S16(val) \ + (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ + (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) +#define PCM_CLAMP_S24(val) \ + (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) + +#ifdef PCM_USE_64BIT_ARITH +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) +#else +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ + ((val) << PCM_FXSHIFT))) +#endif + +#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) +#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) +#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) +#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) + /* make figuring out what a format is easier. got AFMT_STEREO already */ #define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) #define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE) @@ -184,7 +461,7 @@ #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ #define SND_DEV_PSS SND_DEV_SNDPROC /* ? */ #define SND_DEV_NORESET 10 -#define SND_DEV_DSPREC 11 /* recording channels */ +#define SND_DEV_DSPHW 11 /* specific channel request */ #define DSP_DEFAULT_SPEED 8000 @@ -285,7 +562,7 @@ struct cdev *dsp_devt; struct cdev *dspW_devt; struct cdev *audio_devt; - struct cdev *dspr_devt; + struct cdev *dspHW_devt; }; struct snddev_info { Index: sys/dev/sound/pcm/vchan.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pcm/vchan.c,v retrieving revision 1.24 diff -u -r1.24 vchan.c --- sys/dev/sound/pcm/vchan.c 31 Mar 2006 10:27:47 -0000 1.24 +++ sys/dev/sound/pcm/vchan.c 27 Apr 2006 09:06:33 -0000 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2001 Cameron Grant + * Copyright (c) 2001 Cameron Grant + * Copyright (c) 2006 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,6 +23,9 @@ * 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. + * + * Almost entirely rewritten to add multi-format/channels mixing support. + * */ #include @@ -30,74 +34,228 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.24 2006/03/31 10:27:47 ariff Exp $"); +MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); + /* - * Default speed + * Default speed / format */ #define VCHAN_DEFAULT_SPEED 48000 +#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) +#define VCHAN_DEFAULT_STRFMT "s16le" extern int feeder_rate_ratemin; extern int feeder_rate_ratemax; +struct feed_vchan_info; + +typedef uint32_t (*feed_vchan_mixer)(struct feed_vchan_info *, + uint8_t *, uint8_t *, uint32_t); + +struct feed_vchan_info { + uint32_t bps, channels, zero_sample; + feed_vchan_mixer mix; +}; + struct vchinfo { - u_int32_t spd, fmt, blksz, bps, run; + uint32_t spd, fmt, fmts[2], blksz, bps, run; struct pcm_channel *channel, *parent; struct pcmchan_caps caps; }; -static u_int32_t vchan_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, - 0 +/* support everything (mono / stereo), except a-law / mu-law */ +static struct afmtstr_table vchan_supported_fmts[] = { + { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, + { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, + { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, + { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, + { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, + { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, + { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, + { NULL, 0 }, +}; + +/* alias table, shorter. */ +static const struct { + char *alias, *fmtstr; +} vchan_fmtstralias[] = { + { "8", "u8" }, { "16", "s16le" }, + { "24", "s24le" }, { "32", "s32le" }, + { NULL, NULL }, }; +#define vchan_valid_format(fmt) \ + afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \ + AFMTSTR_STEREO_RETURN) +#define vchan_valid_strformat(strfmt) \ + afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); + +/* + * Need specialized WRITE macros since 32bit might involved saturation + * if calculation is done within 32bit arithmetic. + */ +#define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) +#define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) +#define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) +#define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) +#define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) +#define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) +#define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) +#define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) +#define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) +#define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) +#define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) +#define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) +#define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) +#define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) + +#define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static uint32_t \ +feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(struct feed_vchan_info *info, \ + uint8_t *to, uint8_t *tmp, uint32_t count) \ +{ \ + uint32_t bps; \ + int32_t x, y; \ + VCHAN_INTCAST z; \ + int i; \ + \ + bps = info->bps; \ + i = count; \ + tmp += i; \ + to += i; \ + while (i > 0) { \ + tmp -= bps; \ + to -= bps; \ + i -= bps; \ + x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ + y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ + z = (VCHAN_INTCAST)x + y; \ + x = PCM_CLAMP_##SIGN##FMTBIT(z); \ + VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ + } \ + return count; \ +} + +FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) +FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) +FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) +FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) +FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) +FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) +FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) +/* unsigned */ +FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) +FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) +FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) +FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) +FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) +FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) +FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) + static int -vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) +feed_vchan_setup(struct pcm_feeder *f) { - /* - * to is the output buffer, tmp is the input buffer - * count is the number of 16bit samples to mix - */ - int i; - int x; - - for(i = 0; i < count; i++) { - x = to[i]; - x += tmp[i]; - if (x < -32768) { - /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ - x = -32768; - } - if (x > 32767) { - /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ - x = 32767; + struct feed_vchan_info *info = f->data; + static const struct { + uint32_t format; /* pcm / audio format */ + uint32_t channels; /* total channels */ + uint32_t bps; /* bytes-per-sample, regardless of + total channels */ + feed_vchan_mixer mix; + } vchan_mix_tbl[] = { + { AFMT_S8, 1, PCM_8_BPS, feed_vchan_mix_s8ne }, + { AFMT_S16_LE, 1, PCM_16_BPS, feed_vchan_mix_s16le }, + { AFMT_S24_LE, 1, PCM_24_BPS, feed_vchan_mix_s24le }, + { AFMT_S32_LE, 1, PCM_32_BPS, feed_vchan_mix_s32le }, + { AFMT_S16_BE, 1, PCM_16_BPS, feed_vchan_mix_s16be }, + { AFMT_S24_BE, 1, PCM_24_BPS, feed_vchan_mix_s24be }, + { AFMT_S32_BE, 1, PCM_32_BPS, feed_vchan_mix_s32be }, + { AFMT_S8 | AFMT_STEREO, 2, PCM_8_BPS, feed_vchan_mix_s8ne }, + { AFMT_S16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_vchan_mix_s16le }, + { AFMT_S24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_vchan_mix_s24le }, + { AFMT_S32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_vchan_mix_s32le }, + { AFMT_S16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_vchan_mix_s16be }, + { AFMT_S24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_vchan_mix_s24be }, + { AFMT_S32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_vchan_mix_s32be }, + /* unsigned */ + { AFMT_U8, 1, PCM_8_BPS, feed_vchan_mix_u8ne }, + { AFMT_U16_LE, 1, PCM_16_BPS, feed_vchan_mix_u16le }, + { AFMT_U24_LE, 1, PCM_24_BPS, feed_vchan_mix_u24le }, + { AFMT_U32_LE, 1, PCM_32_BPS, feed_vchan_mix_u32le }, + { AFMT_U16_BE, 1, PCM_16_BPS, feed_vchan_mix_u16be }, + { AFMT_U24_BE, 1, PCM_24_BPS, feed_vchan_mix_u24be }, + { AFMT_U32_BE, 1, PCM_32_BPS, feed_vchan_mix_u32be }, + { AFMT_U8 | AFMT_STEREO, 2, PCM_8_BPS, feed_vchan_mix_u8ne }, + { AFMT_U16_LE | AFMT_STEREO, 2, PCM_16_BPS, feed_vchan_mix_u16le }, + { AFMT_U24_LE | AFMT_STEREO, 2, PCM_24_BPS, feed_vchan_mix_u24le }, + { AFMT_U32_LE | AFMT_STEREO, 2, PCM_32_BPS, feed_vchan_mix_u32le }, + { AFMT_U16_BE | AFMT_STEREO, 2, PCM_16_BPS, feed_vchan_mix_u16be }, + { AFMT_U24_BE | AFMT_STEREO, 2, PCM_24_BPS, feed_vchan_mix_u24be }, + { AFMT_U32_BE | AFMT_STEREO, 2, PCM_32_BPS, feed_vchan_mix_u32be }, + { 0, 0, 0, NULL }, + }; + uint32_t i; + + for (i = 0; i < sizeof(vchan_mix_tbl) / sizeof(*vchan_mix_tbl); i++) { + if (vchan_mix_tbl[i].format == 0) + return -1; + if (f->desc->out == vchan_mix_tbl[i].format) { + info->channels = vchan_mix_tbl[i].channels; + info->bps = vchan_mix_tbl[i].bps; + info->mix = vchan_mix_tbl[i].mix; + break; } - to[i] = x & 0x0000ffff; } + + info->zero_sample = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80; + return 0; } static int -feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_vchan_init(struct pcm_feeder *f) { - /* we're going to abuse things a bit */ + struct feed_vchan_info *info; + + if (f->desc->out != f->desc->in) + return EINVAL; + + info = malloc(sizeof(*info), M_VCHANFEEDER, M_NOWAIT | M_ZERO); + if (info == NULL) + return ENOMEM; + f->data = info; + return feed_vchan_setup(f); +} + +static int +feed_vchan_free(struct pcm_feeder *f) +{ + struct feed_vchan_info *info = f->data; + + if (info) + free(info, M_VCHANFEEDER); + f->data = NULL; + return 0; +} + +static int +feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_vchan_info *info = f->data; struct snd_dbuf *src = source; struct pcmchan_children *cce; struct pcm_channel *ch; - uint32_t sz; - int16_t *tmp, *dst; - unsigned int cnt, rcnt = 0; - - #if 0 - if (sndbuf_getsize(src) < count) - panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x", - c->name, sndbuf_getsize(src), count, c->flags); - #endif + uint32_t cnt, rcnt = 0, sz; + uint8_t *tmp; + sz = sndbuf_getsize(src); if (sz < count) count = sz; - count &= ~1; - if (count < 2) + + sz = info->bps * info->channels; + count -= count % sz; + if (count < sz) return 0; - bzero(b, count); /* * we are going to use our source as a temporary buffer since it's @@ -105,35 +263,65 @@ * list of children and calling vchan_mix_* to mix count bytes from each * into our destination buffer, b */ - dst = (int16_t *)b; - tmp = (int16_t *)sndbuf_getbuf(src); - bzero(tmp, count); + tmp = sndbuf_getbuf(src); + memset(b, info->zero_sample, count); SLIST_FOREACH(cce, &c->children, link) { ch = cce->channel; CHN_LOCK(ch); if (ch->flags & CHN_F_TRIGGERED) { if (ch->flags & CHN_F_MAPPED) sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); - cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); - vchan_mix_s16(dst, tmp, cnt >> 1); + cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, ch->bufsoft); + cnt -= cnt % sz; + cnt = info->mix(info, b, tmp, cnt); if (cnt > rcnt) rcnt = cnt; } CHN_UNLOCK(ch); } - return rcnt & ~1; + return rcnt; } -static struct pcm_feederdesc feeder_vchan_s16_desc[] = { +static struct pcm_feederdesc feeder_vchan_desc[] = { + {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, + {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, + {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, + {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, + {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, + {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, + {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, + {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {0}, + {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, + /* unsigned */ + {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, + {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, + {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, + {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, + {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, + {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, + {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, + {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, + {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; -static kobj_method_t feeder_vchan_s16_methods[] = { - KOBJMETHOD(feeder_feed, feed_vchan_s16), - { 0, 0 } +static kobj_method_t feeder_vchan_methods[] = { + KOBJMETHOD(feeder_init, feed_vchan_init), + KOBJMETHOD(feeder_free, feed_vchan_free), + KOBJMETHOD(feeder_feed, feed_vchan), + {0, 0} }; -FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); +FEEDER_DECLARE(feeder_vchan, 2, NULL); /************************************************************/ @@ -165,7 +353,7 @@ } static int -vchan_setformat(kobj_t obj, void *data, u_int32_t format) +vchan_setformat(kobj_t obj, void *data, uint32_t format) { struct vchinfo *ch = data; struct pcm_channel *parent = ch->parent; @@ -188,7 +376,7 @@ } static int -vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +vchan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct vchinfo *ch = data; struct pcm_channel *parent = ch->parent; @@ -204,7 +392,7 @@ } static int -vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct vchinfo *ch = data; struct pcm_channel *channel = ch->channel; @@ -225,6 +413,15 @@ CHN_UNLOCK(parent); blocksize *= prate; blocksize /= crate; + blocksize += ch->bps; + prate = 0; + while (blocksize >> prate) + prate++; + blocksize = 1 << (prate - 1); + blocksize -= blocksize % ch->bps; + /* XXX screwed !@#$ */ + if (blocksize < ch->bps) + blocksize = 4096 - (4096 % ch->bps); return blocksize; } @@ -251,11 +448,21 @@ vchan_getcaps(kobj_t obj, void *data) { struct vchinfo *ch = data; + uint32_t fmt; ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); ch->caps.maxspeed = ch->caps.minspeed; - ch->caps.fmtlist = vchan_fmt; ch->caps.caps = 0; + ch->fmts[1] = 0; + fmt = sndbuf_getfmt(ch->parent->bufsoft); + if (fmt != vchan_valid_format(fmt)) { + device_printf(ch->parent->dev, + "%s: WARNING: invalid vchan format! (0x%08x)\n", + __func__, fmt); + fmt = VCHAN_DEFAULT_AFMT; + } + ch->fmts[0] = fmt; + ch->caps.fmtlist = ch->fmts; return &ch->caps; } @@ -367,6 +574,97 @@ pcm_inprog(d, -1); return err; } + +static int +sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct snddev_channel *sce; + struct pcm_channel *c, *ch = NULL, *fake; + uint32_t newfmt, spd; + char fmtstr[AFMTSTR_MAXSZ]; + int err = 0, i; + + d = oidp->oid_arg1; + if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) + return EINVAL; + if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { + pcm_inprog(d, -1); + return EINPROGRESS; + } + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY) { + if (c->flags & CHN_F_VIRTUAL) { + /* Sanity check */ + if (ch != NULL && ch != c->parentchannel) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EINVAL; + } + if (req->newptr != NULL && + (c->flags & CHN_F_BUSY)) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EBUSY; + } + } else if (c->flags & CHN_F_HAS_VCHAN) { + /* No way!! */ + if (ch != NULL) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EINVAL; + } + ch = c; + if (ch->format != afmt2afmtstr(vchan_supported_fmts, + ch->format, fmtstr, sizeof(fmtstr), + AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { + strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr)); + } + } + } + CHN_UNLOCK(c); + } + if (ch == NULL) { + pcm_inprog(d, -1); + return EINVAL; + } + err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); + if (err == 0 && req->newptr != NULL) { + for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { + if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { + strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr)); + break; + } + } + newfmt = vchan_valid_strformat(fmtstr); + if (newfmt == 0) { + pcm_inprog(d, -1); + return EINVAL; + } + CHN_LOCK(ch); + if (newfmt != ch->format) { + /* Get channel speed, before chn_reset() screw it. */ + spd = ch->speed; + err = chn_reset(ch, newfmt); + if (err == 0) + err = chn_setspeed(ch, spd); + CHN_UNLOCK(ch); + if (err == 0) { + fake = pcm_getfakechan(d); + if (fake != NULL) { + CHN_LOCK(fake); + fake->format = newfmt; + CHN_UNLOCK(fake); + } + } + } else + CHN_UNLOCK(ch); + } + pcm_inprog(d, -1); + return err; +} #endif /* virtual channel interface */ @@ -378,7 +676,8 @@ struct pcmchan_children *pce; struct pcm_channel *child, *fake; struct pcmchan_caps *parent_caps; - int err, first, speed = 0; + uint32_t vchanfmt = 0; + int err, first, speed = 0, r; if (!(parent->flags & CHN_F_BUSY)) return EBUSY; @@ -424,23 +723,50 @@ if (parent_caps == NULL) err = EINVAL; - if (!err) - err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); + fake = pcm_getfakechan(d); + + if (!err && fake != NULL) { + /* + * Avoid querying kernel hint, use saved value + * from fake channel. + */ + CHN_UNLOCK(parent); + CHN_LOCK(fake); + speed = fake->speed; + vchanfmt = fake->format; + CHN_UNLOCK(fake); + CHN_LOCK(parent); + } if (!err) { - fake = pcm_getfakechan(d); - if (fake != NULL) { - /* - * Avoid querying kernel hint, use saved value - * from fake channel. - */ + if (vchanfmt == 0) { + const char *vfmt; + CHN_UNLOCK(parent); - CHN_LOCK(fake); - speed = fake->speed; - CHN_UNLOCK(fake); + r = resource_string_value(device_get_name(parent->dev), + device_get_unit(parent->dev), + "vchanformat", &vfmt); CHN_LOCK(parent); + if (r != 0) + vfmt = NULL; + if (vfmt != NULL) { + vchanfmt = vchan_valid_strformat(vfmt); + for (r = 0; vchanfmt == 0 && + vchan_fmtstralias[r].alias != NULL; + r++) { + if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { + vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); + break; + } + } + } + if (vchanfmt == 0) + vchanfmt = VCHAN_DEFAULT_AFMT; } + err = chn_reset(parent, vchanfmt); + } + if (!err) { /* * This is very sad. Few soundcards advertised as being * able to do (insanely) higher/lower speed, but in @@ -448,7 +774,6 @@ * to set sane value via kernel hints or sysctl. */ if (speed < 1) { - int r; CHN_UNLOCK(parent); r = resource_int_value(device_get_name(parent->dev), device_get_unit(parent->dev), @@ -509,6 +834,7 @@ CHN_UNLOCK(parent); CHN_LOCK(fake); fake->speed = speed; + fake->format = vchanfmt; CHN_UNLOCK(fake); CHN_LOCK(parent); } @@ -571,9 +897,9 @@ destroy_dev(sce->audio_devt); sce->audio_devt = NULL; } - if (sce->dspr_devt) { - destroy_dev(sce->dspr_devt); - sce->dspr_devt = NULL; + if (sce->dspHW_devt) { + destroy_dev(sce->dspHW_devt); + sce->dspHW_devt = NULL; } d->devcount--; break; @@ -609,10 +935,13 @@ d = device_get_softc(dev); SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), - sysctl_hw_snd_vchans, "I", ""); + sysctl_hw_snd_vchans, "I", "total virtual channels"); SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), - sysctl_hw_snd_vchanrate, "I", ""); + sysctl_hw_snd_vchanrate, "I", "default virtual channel speed for mixing"); + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), + sysctl_hw_snd_vchanformat, "A", "default virtual channel format for mixing"); #endif return 0;