summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
committerariff <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
commit7b36f6d96b710844a6d2abd90a94bc169f72a5d1 (patch)
treebe8f05cc6b2e41975dd084107c4db8978e7c9734 /sys/dev/sound/pcm
parent70fe7b890e66532cef253d3bad928093268a24fd (diff)
downloadFreeBSD-src-7b36f6d96b710844a6d2abd90a94bc169f72a5d1.zip
FreeBSD-src-7b36f6d96b710844a6d2abd90a94bc169f72a5d1.tar.gz
Welcome to Once-a-year Sound Mega-Commit. Enjoy numerous updates and fixes
in every sense. General ------- - Multichannel safe, endian safe, format safe * Large part of critical pcm filters such as vchan.c, feeder_rate.c, feeder_volume.c, feeder_fmt.c and feeder.c has been rewritten so that using them does not cause the pcm data to be converted to 16bit little endian. * Macrosses for accessing pcm data safely are defined within sound.h in the form of PCM_READ_* / PCM_WRITE_* * Currently, most of them are probably limited for mono/stereo handling, but the future addition of true multichannel will be much easier. - Low latency operation * Well, this require lot more works to do not just within sound driver, but we're heading towards right direction. Buffer/block sizing within channel.c is rewritten to calculate precise allocation for various combination of sample/data/rate size. As a result, applying correct SNDCTL_DSP_POLICY value will achive expected latency behaviour simmilar to what commercial 4front driver do. * Signal handling fix. ctrl+c of "cat /dev/zero > /dev/dsp" does not result long delay. * Eliminate sound truncation if the sound data is too small. DIY: 1) Download / extract http://people.freebsd.org/~ariff/lowlatency/shortfiles.tar.gz 2) Do a comparison between "cat state*.au > /dev/dsp" and "for x in state*.au ; do cat $x > /dev/dsp ; done" - there should be no "perceivable" differences. Double close for PR kern/31445. CAVEAT: Low latency come with (unbearable) price especially for poorly written applications. Applications that trying to act smarter by requesting (wrong) blocksize/blockcount will suffer the most. Fixup samples/patches can be found at: http://people.freebsd.org/~ariff/ports/ - Switch minimum/maximum sampling rate limit to "1" and "2016000" (48k * 42) due to closer compatibility with 4front driver. Discussed with: marcus@ (long time ago?) - All driver specific sysctls in the form of "hw.snd.pcm%d.*" have been moved to their own dev sysctl nodes, notably: hw.snd.pcm%d.vchans -> dev.pcm.%d.vchans Bump __FreeBSD_version. Driver specific --------------- - Ditto for sysctls. - snd_atiixp, snd_es137x, snd_via8233, snd_hda * Numerous cleanups and fixes. * _EXPERIMENTAL_ polling mode support using simple callout_* mechanisme. This was intended for pure debugging and latency measurement, but proven good enough in few unexpected and rare cases (such as problematic shared IRQ with GIANT devices - USB). Polling can be enabled/disabled through dev.pcm.0.polling. Disabled by default. - snd_ich * Fix possible overflow during speed calibration. Delay final initialization (pcm_setstatus) after calibration finished. PR: kern/100169 Tested by: Kevin Overman <oberman@es.net> * Inverted EAPD for few Nec VersaPro. PR: kern/104715 Submitted by: KAWATA Masahiko <kawata@mta.biglobe.ne.jp> Thanks to various people, notably Joel Dahl, Yuriy Tsibizov, Kevin Oberman, those at #freebsd-azalia @ freenode and others for testing. Joel Dahl will do the manpage update.
Diffstat (limited to 'sys/dev/sound/pcm')
-rw-r--r--sys/dev/sound/pcm/ac97.c104
-rw-r--r--sys/dev/sound/pcm/ac97.h2
-rw-r--r--sys/dev/sound/pcm/buffer.c86
-rw-r--r--sys/dev/sound/pcm/buffer.h3
-rw-r--r--sys/dev/sound/pcm/channel.c1032
-rw-r--r--sys/dev/sound/pcm/channel.h40
-rw-r--r--sys/dev/sound/pcm/dsp.c33
-rw-r--r--sys/dev/sound/pcm/fake.c3
-rw-r--r--sys/dev/sound/pcm/feeder.c584
-rw-r--r--sys/dev/sound/pcm/feeder.h29
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c826
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c866
-rw-r--r--sys/dev/sound/pcm/feeder_volume.c211
-rw-r--r--sys/dev/sound/pcm/mixer.c8
-rw-r--r--sys/dev/sound/pcm/sndstat.c21
-rw-r--r--sys/dev/sound/pcm/sound.c88
-rw-r--r--sys/dev/sound/pcm/sound.h287
-rw-r--r--sys/dev/sound/pcm/vchan.c589
18 files changed, 3319 insertions, 1493 deletions
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
index 9b3380b..46ceef0 100644
--- a/sys/dev/sound/pcm/ac97.c
+++ b/sys/dev/sound/pcm/ac97.c
@@ -28,6 +28,8 @@
#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pcm/ac97_patch.h>
+#include <dev/pci/pcivar.h>
+
#include "mixer_if.h"
SND_DECLARE_FILE("$FreeBSD$");
@@ -791,7 +793,7 @@ struct ac97_info *
ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
{
struct ac97_info *codec;
- int eapd_inv;
+ int eapdinv;
codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO);
if (codec == NULL)
@@ -811,8 +813,8 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
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)
+ "eapdinv", &eapdinv) == 0) {
+ if (eapdinv != 0)
codec->flags |= AC97_F_EAPD_INV;
}
return codec;
@@ -842,11 +844,66 @@ ac97_getflags(struct ac97_info *codec)
/* -------------------------------------------------------------------- */
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hw_snd_ac97_eapd(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
+
+static void
+ac97_init_sysctl(struct ac97_info *codec)
+{
+#ifdef SND_DYNSYSCTL
+ u_int16_t orig, val;
+
+ if (codec == NULL || codec->dev == NULL)
+ return;
+ 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;
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)),
+ OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
+ codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
+ "I", "AC97 External Amplifier");
+#endif
+}
+
static int
ac97mix_init(struct snd_mixer *m)
{
struct ac97_info *codec = mix_getdevinfo(m);
struct snddev_info *d;
+ u_int32_t subvendor;
u_int32_t i, mask;
if (codec == NULL)
@@ -857,20 +914,30 @@ ac97mix_init(struct snd_mixer *m)
switch (codec->id) {
case 0x41445374: /* AD1981B */
-#if 0
- mask = 0;
- if (codec->mix[SOUND_MIXER_OGAIN].enable)
- mask |= SOUND_MASK_OGAIN;
- if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
- mask |= SOUND_MASK_PHONEOUT;
- /* Tie ogain/phone to master volume */
- if (codec->mix[SOUND_MIXER_VOLUME].enable)
- mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
- else {
- mix_setparentchild(m, SOUND_MIXER_VOLUME, mask);
- mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
+ subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16;
+ subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) &
+ 0x0000ffff;
+ /* IBM Thinkcentre */
+ if (subvendor == 0x02d91014) {
+ /* Enable headphone jack sensing */
+ ac97_wrcd(codec, 0x72, ac97_rdcd(codec, 0x72) |
+ 0x0800);
+ mask = 0;
+ if (codec->mix[SOUND_MIXER_OGAIN].enable)
+ mask |= SOUND_MASK_OGAIN;
+ if (codec->mix[SOUND_MIXER_PHONEOUT].enable)
+ mask |= SOUND_MASK_PHONEOUT;
+ /* Tie ogain/phone to master volume */
+ if (codec->mix[SOUND_MIXER_VOLUME].enable)
+ mix_setparentchild(m, SOUND_MIXER_VOLUME,
+ mask);
+ else {
+ mix_setparentchild(m, SOUND_MIXER_VOLUME,
+ mask);
+ mix_setrealdev(m, SOUND_MIXER_VOLUME,
+ SOUND_MIXER_NONE);
+ }
}
-#endif
break;
case 0x434d4941: /* CMI9738 */
case 0x434d4961: /* CMI9739 */
@@ -906,6 +973,9 @@ ac97mix_init(struct snd_mixer *m)
for (i = 0; i < 32; i++)
mask |= codec->mix[i].recidx? 1 << i : 0;
mix_setrecdevs(m, mask);
+
+ ac97_init_sysctl(codec);
+
return 0;
}
@@ -975,5 +1045,3 @@ ac97_getmixerclass(void)
{
return &ac97mixer_class;
}
-
-
diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h
index 8266fb5..f73dc76 100644
--- a/sys/dev/sound/pcm/ac97.h
+++ b/sys/dev/sound/pcm/ac97.h
@@ -82,7 +82,6 @@
#define AC97_F_EAPD_INV 0x00000001
#define AC97_F_RDCD_BUG 0x00000002
-#define AC97_F_SOFTVOL 0x00000004
#define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
#define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class)
@@ -105,4 +104,3 @@ u_int16_t ac97_getcaps(struct ac97_info *codec);
u_int16_t ac97_rdcd(struct ac97_info *codec, int reg);
void ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val);
-
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
index 11c1bfb..e12765e 100644
--- a/sys/dev/sound/pcm/buffer.c
+++ b/sys/dev/sound/pcm/buffer.c
@@ -254,10 +254,7 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
if (length > b->bufsize)
length = b->bufsize;
- if (b->fmt & AFMT_SIGNED)
- data = 0x00;
- else
- data = 0x80;
+ data = sndbuf_zerodata(b->fmt);
i = sndbuf_getfreeptr(b);
p = sndbuf_getbuf(b);
@@ -278,18 +275,8 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
void
sndbuf_fillsilence(struct snd_dbuf *b)
{
- int i;
- u_char data, *p;
-
- if (b->fmt & AFMT_SIGNED)
- data = 0x00;
- else
- data = 0x80;
-
- i = 0;
- p = sndbuf_getbuf(b);
- while (i < b->bufsize)
- p[i++] = data;
+ if (b->bufsize > 0)
+ memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
b->rp = 0;
b->rl = b->bufsize;
}
@@ -543,6 +530,52 @@ sndbuf_updateprevtotal(struct snd_dbuf *b)
b->prev_total = b->total;
}
+unsigned int
+snd_xbytes(unsigned int v, unsigned int from, unsigned int to)
+{
+ unsigned int w, x, y;
+
+ if (from == to)
+ return v;
+
+ if (from == 0 || to == 0 || v == 0)
+ return 0;
+
+ x = from;
+ y = to;
+ while (y != 0) {
+ w = x % y;
+ x = y;
+ y = w;
+ }
+ from /= x;
+ to /= x;
+
+ return (unsigned int)(((u_int64_t)v * to) / from);
+}
+
+unsigned int
+sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
+{
+ if (from == NULL || to == NULL || v == 0)
+ return 0;
+
+ return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from),
+ sndbuf_getbps(to) * sndbuf_getspd(to));
+}
+
+u_int8_t
+sndbuf_zerodata(u_int32_t fmt)
+{
+ if (fmt & AFMT_SIGNED)
+ return (0x00);
+ else if (fmt & AFMT_MU_LAW)
+ return (0x7f);
+ else if (fmt & AFMT_A_LAW)
+ return (0x55);
+ return (0x80);
+}
+
/************************************************************/
/**
@@ -624,15 +657,20 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
int
sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
{
+ unsigned int cnt;
+
KASSERT(count > 0, ("can't feed 0 bytes"));
if (sndbuf_getfree(to) < count)
return EINVAL;
- count = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
- if (count)
- sndbuf_acquire(to, to->tmpbuf, count);
- /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
+ do {
+ cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
+ if (cnt)
+ sndbuf_acquire(to, to->tmpbuf, cnt);
+ /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */
+ count -= cnt;
+ } while (count && cnt);
return 0;
}
@@ -682,12 +720,8 @@ sndbuf_clearshadow(struct snd_dbuf *b)
KASSERT(b != NULL, ("b is a null pointer"));
KASSERT(b->sl >= 0, ("illegal shadow length"));
- if ((b->shadbuf != NULL) && (b->sl > 0)) {
- if (b->fmt & AFMT_SIGNED)
- memset(b->shadbuf, 0x00, b->sl);
- else
- memset(b->shadbuf, 0x80, b->sl);
- }
+ if ((b->shadbuf != NULL) && (b->sl > 0))
+ memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl);
}
#ifdef OSSV4_EXPERIMENT
diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h
index 07724be..67e08a7 100644
--- a/sys/dev/sound/pcm/buffer.h
+++ b/sys/dev/sound/pcm/buffer.h
@@ -107,6 +107,9 @@ unsigned int sndbuf_getreadyptr(struct snd_dbuf *b);
unsigned int sndbuf_getblocks(struct snd_dbuf *b);
unsigned int sndbuf_getprevblocks(struct snd_dbuf *b);
unsigned int sndbuf_gettotal(struct snd_dbuf *b);
+unsigned int snd_xbytes(unsigned int v, unsigned int from, unsigned int to);
+unsigned int sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to);
+u_int8_t sndbuf_zerodata(u_int32_t fmt);
void sndbuf_updateprevtotal(struct snd_dbuf *b);
int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count);
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 43ef365..d88f0e2 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -41,32 +41,60 @@ SND_DECLARE_FILE("$FreeBSD$");
#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
+#define BUF_PARENT(c, b) \
+ (((c) != NULL && (c)->parentchannel != NULL && \
+ (c)->parentchannel->bufhard != NULL) ? \
+ (c)->parentchannel->bufhard : (b))
+
/*
#define DEB(x) x
*/
-static int chn_targetirqrate = 32;
-TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
+int report_soft_formats = 1;
+SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
+ &report_soft_formats, 1, "report software-emulated formats");
+
+int chn_latency = CHN_LATENCY_DEFAULT;
+TUNABLE_INT("hw.snd.latency", &chn_latency);
static int
-sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = chn_targetirqrate;
+ val = chn_latency;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val < 16 || val > 512)
+ if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX)
err = EINVAL;
else
- chn_targetirqrate = val;
+ chn_latency = val;
return err;
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
-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");
+SYSCTL_PROC(_hw_snd, OID_AUTO, latency, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_latency, "I",
+ "buffering latency (0=low ... 10=high)");
+
+int chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
+TUNABLE_INT("hw.snd.latency_profile", &chn_latency_profile);
+
+static int
+sysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = chn_latency_profile;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX)
+ err = EINVAL;
+ else
+ chn_latency_profile = val;
+
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_latency_profile, "I",
+ "buffering latency profile (0=aggresive 1=safe)");
/**
* @brief Channel sync group lock
@@ -173,7 +201,7 @@ chn_wakeup(struct pcm_channel *c)
}
}
- wakeup(bs);
+ wakeup_one(bs);
}
static int
@@ -220,13 +248,23 @@ chn_dmaupdate(struct pcm_channel *c)
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 (snd_verbose > 2 && (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;
}
@@ -270,9 +308,11 @@ chn_wrfeed(struct pcm_channel *c)
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;
/*
@@ -313,10 +353,10 @@ chn_wrintr(struct pcm_channel *c)
int
chn_write(struct pcm_channel *c, struct uio *buf)
{
- int ret, timeout, newsize, count, sz;
+ int ret, newsize, count, sz;
struct snd_dbuf *bs = c->bufsoft;
void *off;
- int t, x,togo,p;
+ int t, x, togo, p;
CHN_LOCKASSERT(c);
/*
@@ -356,14 +396,13 @@ chn_write(struct pcm_channel *c, struct uio *buf)
*/
ret = EAGAIN;
} else {
- timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
- if (timeout < 1)
- timeout = 1;
- timeout = 1;
- ret = chn_sleep(c, "pcmwr", timeout);
+ ret = chn_sleep(c, "pcmwr", c->timeout);
if (ret == EWOULDBLOCK) {
- count -= timeout;
+ count -= c->timeout;
ret = 0;
+ } else if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
+ return ret;
} else if (ret == 0)
count = hz;
}
@@ -378,7 +417,7 @@ chn_write(struct pcm_channel *c, struct uio *buf)
* unlock-uiomove-lock sequence.
*/
togo = sz;
- while (ret == 0 && togo> 0) {
+ while (ret == 0 && togo > 0) {
p = sndbuf_getfreeptr(bs);
t = MIN(togo, sndbuf_getsize(bs) - p);
off = sndbuf_getbufofs(bs, p);
@@ -389,7 +428,7 @@ chn_write(struct pcm_channel *c, struct uio *buf)
x = sndbuf_acquire(bs, NULL, t);
}
ret = 0;
- if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
+ if (!(c->flags & CHN_F_TRIGGERED))
chn_start(c, 0);
}
}
@@ -411,7 +450,7 @@ chn_rddump(struct pcm_channel *c, unsigned int cnt)
CHN_LOCKASSERT(c);
#if 0
- static uint32_t kk = 0;
+ static u_int32_t kk = 0;
printf("%u: dumping %d bytes\n", ++kk, cnt);
#endif
c->xruns++;
@@ -501,10 +540,10 @@ chn_rdintr(struct pcm_channel *c)
int
chn_read(struct pcm_channel *c, struct uio *buf)
{
- int ret, timeout, sz, count;
- struct snd_dbuf *bs = c->bufsoft;
+ int ret, sz, count;
+ struct snd_dbuf *bs = c->bufsoft;
void *off;
- int t, x,togo,p;
+ int t, x, togo, p;
CHN_LOCKASSERT(c);
if (!(c->flags & CHN_F_TRIGGERED))
@@ -522,7 +561,7 @@ chn_read(struct pcm_channel *c, struct uio *buf)
* unlock-uiomove-lock sequence.
*/
togo = sz;
- while (ret == 0 && togo> 0) {
+ while (ret == 0 && togo > 0) {
p = sndbuf_getreadyptr(bs);
t = MIN(togo, sndbuf_getsize(bs) - p);
off = sndbuf_getbufofs(bs, p);
@@ -534,20 +573,18 @@ chn_read(struct pcm_channel *c, struct uio *buf)
}
ret = 0;
} else {
- if (c->flags & CHN_F_NBIO) {
+ if (c->flags & CHN_F_NBIO)
ret = EWOULDBLOCK;
- } else {
- timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
- if (timeout < 1)
- timeout = 1;
- ret = chn_sleep(c, "pcmrd", timeout);
+ else {
+ ret = chn_sleep(c, "pcmrd", c->timeout);
if (ret == EWOULDBLOCK) {
- count -= timeout;
+ count -= c->timeout;
ret = 0;
- } else {
+ } else if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
+ return ret;
+ } else
count = hz;
- }
-
}
}
}
@@ -584,27 +621,49 @@ chn_start(struct pcm_channel *c, int force)
if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
return EINVAL;
- i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
- j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
- if (force || (i >= j)) {
- c->flags |= CHN_F_TRIGGERED;
- /*
- * if we're starting because a vchan started, don't feed any data
- * or it becomes impossible to start vchans synchronised with the
- * first one. the hardbuf should be empty so we top it up with
- * silence to give it something to chew. the real data will be
- * fed at the first irq.
- */
- if (c->direction == PCMDIR_PLAY) {
- /*
- * Reduce pops during playback startup.
- */
- sndbuf_fillsilence(b);
- if (SLIST_EMPTY(&c->children))
- chn_wrfeed(c);
+ if (force) {
+ i = 1;
+ j = 0;
+ } else {
+ if (c->direction == PCMDIR_REC) {
+ i = sndbuf_getfree(bs);
+ j = sndbuf_getready(b);
+ } else {
+ struct snd_dbuf *pb;
+
+ i = sndbuf_getready(bs);
+
+ pb = BUF_PARENT(c, b);
+ j = min(sndbuf_xbytes(sndbuf_getsize(pb), pb, bs),
+ sndbuf_getsize(bs));
}
+ if (snd_verbose > 3 && SLIST_EMPTY(&c->children))
+ printf("%s: PCMDIR_%s (%s) threshold i=%d j=%d\n",
+ __func__,
+ (c->direction == PCMDIR_PLAY) ? "PLAY" : "REC",
+ (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ i, j);
+ }
+
+ if (i >= j) {
+ c->flags |= CHN_F_TRIGGERED;
sndbuf_setrun(b, 1);
+ c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0;
+ c->interrupts = 0;
c->xruns = 0;
+ if (c->direction == PCMDIR_PLAY && c->parentchannel == NULL) {
+ chn_wrfeed(c);
+ if (snd_verbose > 3)
+ printf("%s: %s starting! (%s) (ready=%d "
+ "force=%d i=%d j=%d intrtimeout=%u)\n",
+ __func__,
+ (c->flags & CHN_F_HAS_VCHAN) ?
+ "VCHAN" : "HW",
+ (c->flags & CHN_F_CLOSING) ? "closing" :
+ "running",
+ sndbuf_getready(b),
+ force, i, j, c->timeout);
+ }
chn_trigger(c, PCMTRIG_START);
return 0;
}
@@ -631,34 +690,91 @@ chn_resetbuf(struct pcm_channel *c)
int
chn_sync(struct pcm_channel *c, int threshold)
{
- u_long rdy;
- int ret;
- struct snd_dbuf *bs = c->bufsoft;
+ int ret, count, hcount, minflush, resid, residp;
+ struct snd_dbuf *b, *bs;
CHN_LOCKASSERT(c);
+ if (c->flags & (CHN_F_DEAD | CHN_F_ABORTING))
+ return 0;
+
+ if (c->direction != PCMDIR_PLAY)
+ return EINVAL;
+
+ bs = c->bufsoft;
+
/* if we haven't yet started and nothing is buffered, else start*/
if (!(c->flags & CHN_F_TRIGGERED)) {
if (sndbuf_getready(bs) > 0) {
ret = chn_start(c, 1);
if (ret)
return ret;
- } else {
+ } else
return 0;
- }
}
- for (;;) {
- rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
- if (rdy <= threshold) {
- ret = chn_sleep(c, "pcmsyn", 1);
- if (ret == ERESTART || ret == EINTR) {
- DEB(printf("chn_sync: tsleep returns %d\n", ret));
- return -1;
- }
- } else
+ b = BUF_PARENT(c, c->bufhard);
+
+ threshold += sndbuf_getready(b);
+ minflush = sndbuf_xbytes(threshold, b, bs);
+ minflush -= minflush % sndbuf_getbps(bs);
+
+ if (minflush > 0) {
+ threshold = min(minflush, sndbuf_getfree(bs));
+ sndbuf_clear(bs, threshold);
+ sndbuf_acquire(bs, NULL, threshold);
+ minflush -= threshold;
+ }
+
+ resid = sndbuf_getready(bs);
+ residp = resid;
+ count = sndbuf_xbytes(minflush + resid, bs, b) / sndbuf_getblksz(b);
+ hcount = count;
+ ret = 0;
+
+ while (count > 0 && resid > 0) {
+ ret = chn_sleep(c, "pcmsyn", c->timeout);
+ if (ret == ERESTART || ret == EINTR) {
+ c->flags |= CHN_F_ABORTING;
break;
- }
+ }
+ if (ret == 0 || ret == EWOULDBLOCK) {
+ resid = sndbuf_getready(bs);
+ if (resid == residp) {
+ --count;
+ if (snd_verbose > 3)
+ printf("%s: [stalled] timeout=%d "
+ "count=%d hcount=%d "
+ "resid=%d minflush=%d\n",
+ __func__, c->timeout, count,
+ hcount, resid, minflush);
+ } else if (resid < residp && count < hcount) {
+ ++count;
+ if (snd_verbose > 3)
+ printf("%s: [resume] timeout=%d "
+ "count=%d hcount=%d "
+ "resid=%d minflush=%d\n",
+ __func__, c->timeout, count,
+ hcount, resid, minflush);
+ }
+ if (minflush > 0 && sndbuf_getfree(bs) > 0) {
+ threshold = min(minflush,
+ sndbuf_getfree(bs));
+ sndbuf_clear(bs, threshold);
+ sndbuf_acquire(bs, NULL, threshold);
+ resid = sndbuf_getready(bs);
+ minflush -= threshold;
+ }
+ residp = resid;
+ }
+ }
+
+ if (snd_verbose > 3)
+ printf("%s: timeout=%d count=%d hcount=%d resid=%d residp=%d "
+ "minflush=%d ret=%d\n",
+ __func__, c->timeout, count, hcount, resid, residp,
+ minflush, ret);
+
return 0;
}
@@ -724,48 +840,14 @@ chn_abort(struct pcm_channel *c)
int
chn_flush(struct pcm_channel *c)
{
- int ret, count, resid, resid_p;
struct snd_dbuf *b = c->bufhard;
- struct snd_dbuf *bs = c->bufsoft;
CHN_LOCKASSERT(c);
KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
- /* if we haven't yet started and nothing is buffered, else start*/
- if (!(c->flags & CHN_F_TRIGGERED)) {
- if (sndbuf_getready(bs) > 0) {
- ret = chn_start(c, 1);
- if (ret)
- return ret;
- } else {
- return 0;
- }
- }
-
c->flags |= CHN_F_CLOSING;
- resid = sndbuf_getready(bs) + sndbuf_getready(b);
- resid_p = resid;
- count = 10;
- ret = 0;
- while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
- /* still pending output data. */
- ret = chn_sleep(c, "pcmflu", hz / 10);
- if (ret == EWOULDBLOCK)
- ret = 0;
- if (ret == 0) {
- resid = sndbuf_getready(bs) + sndbuf_getready(b);
- if (resid == resid_p)
- count--;
- if (resid > resid_p)
- DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
- resid_p = resid;
- }
- }
- if (count == 0)
- DEB(printf("chn_flush: timeout, hw %d, sw %d\n",
- sndbuf_getready(b), sndbuf_getready(bs)));
-
+ chn_sync(c, 0);
c->flags &= ~CHN_F_TRIGGERED;
/* kill the channel */
chn_trigger(c, PCMTRIG_ABORT);
@@ -786,14 +868,141 @@ fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
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)
{
int hwspd, r;
CHN_LOCKASSERT(c);
+ c->feedcount = 0;
c->flags &= CHN_F_RESET;
c->interrupts = 0;
+ c->timeout = 1;
c->xruns = 0;
r = CHANNEL_RESET(c->methods, c->devinfo);
@@ -818,7 +1027,7 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt)
#endif
}
if (r == 0)
- r = chn_setblocksize(c, 0, 0);
+ r = chn_setlatency(c, chn_latency);
if (r == 0) {
chn_resetbuf(c);
r = CHANNEL_RESETDONE(c->methods, c->devinfo);
@@ -839,6 +1048,8 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
bs = NULL;
c->devinfo = NULL;
c->feeder = NULL;
+ c->latency = -1;
+ c->timeout = 1;
ret = ENOMEM;
b = sndbuf_create(c->dev, c->name, "primary", c);
@@ -981,6 +1192,355 @@ chn_setvolume(struct pcm_channel *c, int left, int right)
return 0;
}
+static u_int32_t
+round_pow2(u_int32_t v)
+{
+ u_int32_t ret;
+
+ if (v < 2)
+ v = 2;
+ ret = 0;
+ while (v >> ret)
+ ret++;
+ ret = 1 << (ret - 1);
+ while (ret < v)
+ ret <<= 1;
+ return ret;
+}
+
+/*
+ * 4Front call it DSP Policy, while we call it "Latency Profile". The idea
+ * is to keep 2nd buffer short so that it doesn't cause long queue during
+ * buffer transfer.
+ *
+ * Latency reference table for 48khz stereo 16bit: (PLAY)
+ *
+ * +---------+------------+-----------+------------+
+ * | Latency | Blockcount | Blocksize | Buffersize |
+ * +---------+------------+-----------+------------+
+ * | 0 | 2 | 64 | 128 |
+ * +---------+------------+-----------+------------+
+ * | 1 | 4 | 128 | 512 |
+ * +---------+------------+-----------+------------+
+ * | 2 | 8 | 512 | 4096 |
+ * +---------+------------+-----------+------------+
+ * | 3 | 16 | 512 | 8192 |
+ * +---------+------------+-----------+------------+
+ * | 4 | 32 | 512 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 5 | 32 | 1024 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 6 | 16 | 2048 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 7 | 8 | 4096 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 8 | 4 | 8192 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 9 | 2 | 16384 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 10 | 2 | 32768 | 65536 |
+ * +---------+------------+-----------+------------+
+ *
+ * Recording need a different reference table. All we care is
+ * gobbling up everything within reasonable buffering threshold.
+ *
+ * Latency reference table for 48khz stereo 16bit: (REC)
+ *
+ * +---------+------------+-----------+------------+
+ * | Latency | Blockcount | Blocksize | Buffersize |
+ * +---------+------------+-----------+------------+
+ * | 0 | 512 | 32 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 1 | 256 | 64 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 2 | 128 | 128 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 3 | 64 | 256 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 4 | 32 | 512 | 16384 |
+ * +---------+------------+-----------+------------+
+ * | 5 | 32 | 1024 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 6 | 16 | 2048 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 7 | 8 | 4096 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 8 | 4 | 8192 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 9 | 2 | 16384 | 32768 |
+ * +---------+------------+-----------+------------+
+ * | 10 | 2 | 32768 | 65536 |
+ * +---------+------------+-----------+------------+
+ *
+ * Calculations for other data rate are entirely based on these reference
+ * tables. For normal operation, Latency 5 seems give the best, well
+ * balanced performance for typical workload. Anything below 5 will
+ * eat up CPU to keep up with increasing context switches because of
+ * shorter buffer space and usually require the application to handle it
+ * aggresively through possibly real time programming technique.
+ *
+ */
+#define CHN_LATENCY_PBLKCNT_REF \
+ {{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}, \
+ {1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}}
+#define CHN_LATENCY_PBUFSZ_REF \
+ {{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16}, \
+ {11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}}
+
+#define CHN_LATENCY_RBLKCNT_REF \
+ {{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}, \
+ {9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}}
+#define CHN_LATENCY_RBUFSZ_REF \
+ {{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16}, \
+ {15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}}
+
+#define CHN_LATENCY_DATA_REF 192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */
+
+static int
+chn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
+ u_int32_t max, int *rblksz, int *rblkcnt)
+{
+ u_int32_t bufsz;
+ int blksz, blkcnt;
+ static int pblkcnts[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_PBLKCNT_REF;
+ static int pbufszs[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_PBUFSZ_REF;
+ static int rblkcnts[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_RBLKCNT_REF;
+ static int rbufszs[CHN_LATENCY_PROFILE_MAX+1][CHN_LATENCY_MAX+1] =
+ CHN_LATENCY_RBUFSZ_REF;
+
+ if (CHN_LATENCY_MIN != 0 || CHN_LATENCY_MAX != 10 ||
+ latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX ||
+ bps < 1 || datarate < 1 ||
+ !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) {
+ if (rblksz != NULL)
+ *rblksz = CHN_2NDBUFMAXSIZE >> 1;
+ if (rblkcnt != NULL)
+ *rblkcnt = 2;
+ printf("%s: FAILED dir=%d latency=%d bps=%d "
+ "datarate=%u max=%u\n",
+ __func__, dir, latency, bps, datarate, max);
+ return CHN_2NDBUFMAXSIZE;
+ }
+
+ if (dir == PCMDIR_PLAY) {
+ blkcnt = pblkcnts[chn_latency_profile][latency];
+ bufsz = pbufszs[chn_latency_profile][latency];
+ } else {
+ blkcnt = rblkcnts[chn_latency_profile][latency];
+ bufsz = rbufszs[chn_latency_profile][latency];
+ }
+ bufsz = snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF, datarate);
+ if (bufsz > max)
+ bufsz = max;
+ if (bufsz < 32)
+ bufsz = 32;
+ blksz = bufsz >> blkcnt;
+ blksz -= blksz % bps;
+ while (blksz < 16 || blksz < bps)
+ blksz += bps;
+ while ((blksz << blkcnt) > bufsz && blkcnt > 1)
+ blkcnt--;
+ if (rblksz != NULL)
+ *rblksz = blksz;
+ if (rblkcnt != NULL)
+ *rblkcnt = 1 << blkcnt;
+
+ return blksz << blkcnt;
+}
+
+/*
+ * Note that there is no strict requirement to align blksz to the
+ * nearest ^2, except for hardware CHANNEL_SETBLOCKSIZE. If the application
+ * trying to act smarter and requesting for specific blksz/blkcnt, so be it.
+ */
+static int
+chn_resizebuf(struct pcm_channel *c, int latency,
+ int blkcnt, int blksz)
+{
+ struct snd_dbuf *b, *bs, *pb;
+ int sblksz, sblkcnt, hblksz, limit = 1;
+ int ret;
+
+ CHN_LOCKASSERT(c);
+
+ if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED) ||
+ !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC))
+ return EINVAL;
+
+ if (latency == -1) {
+ c->latency = -1;
+ latency = chn_latency;
+ } else if (latency == -2) {
+ latency = c->latency;
+ if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
+ latency = chn_latency;
+ } else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
+ return EINVAL;
+ else {
+ c->latency = latency;
+ limit = 0;
+ }
+
+ bs = c->bufsoft;
+ b = c->bufhard;
+
+ if (!(blksz == 0 || blkcnt == -1) &&
+ (blksz < 16 || blksz < sndbuf_getbps(bs) || blkcnt < 2 ||
+ (blksz * blkcnt) > CHN_2NDBUFMAXSIZE))
+ return EINVAL;
+
+ chn_calclatency(c->direction, latency, sndbuf_getbps(bs),
+ sndbuf_getbps(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE,
+ &sblksz, &sblkcnt);
+
+ if (blksz == 0 || blkcnt == -1) {
+ if (blkcnt == -1)
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (c->flags & CHN_F_HAS_SIZE) {
+ blksz = sndbuf_getblksz(bs);
+ blkcnt = sndbuf_getblkcnt(bs);
+ }
+ } else
+ c->flags |= CHN_F_HAS_SIZE;
+
+ if (c->flags & CHN_F_HAS_SIZE) {
+ /*
+ * The application has requested their own blksz/blkcnt.
+ * Just obey with it, and let them toast alone. We can
+ * clamp it to the nearest latency profile, but that would
+ * defeat the purpose of having custom control. The least
+ * we can do is round it to the nearest ^2 and align it.
+ */
+ sblksz = round_pow2(blksz);
+ sblksz -= sblksz % sndbuf_getbps(bs);
+ sblkcnt = blkcnt;
+ while (sblksz < 16 || sblksz < sndbuf_getbps(bs))
+ sblksz += sndbuf_getbps(bs);
+ if (snd_verbose > 3 && !(blksz == 0 || blkcnt == -1))
+ printf("%s: requested blksz=%d blkcnt=%d -> %d/%d\n",
+ __func__, blksz, blkcnt, sblksz, sblkcnt);
+ limit = 0;
+ }
+
+ if (c->parentchannel != NULL) {
+ pb = BUF_PARENT(c, NULL);
+ CHN_UNLOCK(c);
+ chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
+ CHN_LOCK(c);
+ limit = (limit != 0 && pb != NULL) ?
+ sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
+ c->timeout = c->parentchannel->timeout;
+ } else {
+ if (c->flags & CHN_F_HAS_SIZE) {
+ hblksz = sndbuf_xbytes(sblksz, bs, b);
+ if (snd_verbose > 3)
+ printf("%s: sblksz=%d -> hblksz=%d\n",
+ __func__, sblksz, hblksz);
+ } else
+ chn_calclatency(c->direction, latency,
+ sndbuf_getbps(b),
+ sndbuf_getbps(b) * sndbuf_getspd(b),
+ CHN_2NDBUFMAXSIZE, &hblksz, NULL);
+
+ hblksz = round_pow2(hblksz);
+ if ((hblksz << 1) > sndbuf_getmaxsize(b))
+ hblksz = sndbuf_getmaxsize(b) >> 1;
+ hblksz -= hblksz % sndbuf_getbps(b);
+ while (hblksz < 16 || hblksz < sndbuf_getbps(b))
+ hblksz += sndbuf_getbps(b);
+
+#if 0
+ hblksz = sndbuf_getmaxsize(b) >> 1;
+#endif
+ CHN_UNLOCK(c);
+ sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods,
+ c->devinfo, hblksz));
+ CHN_LOCK(c);
+
+ if (!SLIST_EMPTY(&c->children)) {
+ /*
+ * Virtual channels underneath. Set the biggest
+ * possible value for their mixing space.
+ */
+ sblksz = CHN_2NDBUFMAXSIZE >> 1;
+ sblksz -= sblksz % sndbuf_getbps(bs);
+ sblkcnt = 2;
+ limit = 0;
+ } else if (limit != 0)
+ limit = sndbuf_xbytes(sndbuf_getsize(b), b, bs);
+
+ /*
+ * Interrupt timeout
+ */
+ c->timeout = ((u_int64_t)hz * sndbuf_getblksz(b)) /
+ ((u_int64_t)sndbuf_getspd(b) * sndbuf_getbps(b));
+ if (c->timeout < 1)
+ c->timeout = 1;
+ c->timeout <<= 1;
+ }
+
+ if (limit > CHN_2NDBUFMAXSIZE)
+ limit = CHN_2NDBUFMAXSIZE;
+
+ hblksz = sblksz;
+ while ((sblksz * sblkcnt) < limit) {
+ sblksz += hblksz;
+ if ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) {
+ sblksz -= hblksz;
+ break;
+ }
+ }
+
+ if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz ||
+ sndbuf_getsize(bs) != (sblkcnt * sblksz)) {
+ ret = sndbuf_remalloc(bs, sblkcnt, sblksz);
+ if (ret != 0) {
+ printf("%s: Failed: %d %d\n", __func__,
+ sblkcnt, sblksz);
+ return ret;
+ }
+ }
+
+ /*
+ * OSSv4 docs: "By default OSS will set the low water level equal
+ * to the fragment size which is optimal in most cases."
+ */
+ c->lw = sndbuf_getblksz(bs);
+ chn_resetbuf(c);
+
+ if (snd_verbose > 3)
+ printf("%s: PCMDIR_%s (%s) timeout=%u "
+ "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n",
+ __func__, (c->direction == PCMDIR_REC) ? "REC" : "PLAY",
+ (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ c->timeout,
+ sndbuf_getsize(b), sndbuf_getblksz(b),
+ sndbuf_getblkcnt(b),
+ sndbuf_getsize(bs), sndbuf_getblksz(bs),
+ sndbuf_getblkcnt(bs), limit);
+
+ return 0;
+}
+
+int
+chn_setlatency(struct pcm_channel *c, int latency)
+{
+ CHN_LOCKASSERT(c);
+ /* Destroy blksz/blkcnt, enforce latency profile. */
+ return chn_resizebuf(c, latency, -1, 0);
+}
+
+int
+chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
+{
+ CHN_LOCKASSERT(c);
+ /* Destroy latency profile, enforce blksz/blkcnt */
+ return chn_resizebuf(c, -1, blkcnt, blksz);
+}
+
static int
chn_tryspeed(struct pcm_channel *c, int speed)
{
@@ -1012,7 +1572,7 @@ chn_tryspeed(struct pcm_channel *c, int speed)
/*
* Used to be 500. It was too big!
*/
- if (delta > 25)
+ if (delta > feeder_rate_round)
c->feederflags |= 1 << FEEDER_RATE;
else
sndbuf_setspd(bs, sndbuf_getspd(b));
@@ -1022,10 +1582,6 @@ chn_tryspeed(struct pcm_channel *c, int speed)
if (r)
goto out;
- r = chn_setblocksize(c, 0, 0);
- if (r)
- goto out;
-
if (!(c->feederflags & (1 << FEEDER_RATE)))
goto out;
@@ -1050,6 +1606,8 @@ out:
sndbuf_getfmt(b));
if (!r)
sndbuf_setfmt(bs, c->format);
+ if (!r)
+ r = chn_resizebuf(c, -2, 0, 0);
DEB(printf("setspeed done, r = %d\n", r));
return r;
} else
@@ -1063,7 +1621,9 @@ chn_setspeed(struct pcm_channel *c, int speed)
r = chn_tryspeed(c, speed);
if (r) {
- DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
+ if (snd_verbose > 3)
+ printf("Failed to set speed %d falling back to %d\n",
+ speed, oldspeed);
r = chn_tryspeed(c, oldspeed);
}
return r;
@@ -1101,190 +1661,14 @@ chn_setformat(struct pcm_channel *c, u_int32_t fmt)
r = chn_tryformat(c, fmt);
if (r) {
- DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
+ if (snd_verbose > 3)
+ printf("Format change 0x%08x failed, reverting to 0x%08x\n",
+ fmt, oldfmt);
chn_tryformat(c, oldfmt);
}
return r;
}
-/*
- * given a bufsz value, round it to a power of 2 in the min-max range
- * XXX only works if min and max are powers of 2
- */
-static int
-round_bufsz(int bufsz, int min, int max)
-{
- int tmp = min * 2;
-
- KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min));
- KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max));
- while (tmp <= bufsz)
- tmp <<= 1;
- tmp >>= 1;
- if (tmp > max)
- tmp = max;
- return tmp;
-}
-
-/*
- * set the channel's blocksize both for soft and hard buffers.
- *
- * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
- * that it has the same value for both bufsoft and bufhard.
- * blksz == -1 computes values according to a target irq rate.
- * blksz == 0 reuses previous values if available, otherwise
- * behaves as for -1
- *
- * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
- * but should be a power of 2 for bufhard to simplify life to low
- * level drivers.
- * Note, for the rec channel a large blkcnt is ok,
- * but for the play channel we want blksz as small as possible to keep
- * the delay small, because routines in the write path always try to
- * keep bufhard full.
- *
- * Unless we have good reason to, use the values suggested by the caller.
- */
-int
-chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
-{
- struct snd_dbuf *b = c->bufhard;
- struct snd_dbuf *bs = c->bufsoft;
- int irqhz, ret, maxsz, maxsize, reqblksz;
-
- 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)));
- return EINVAL;
- }
- c->flags |= CHN_F_SETBLOCKSIZE;
-
- ret = 0;
- DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
- if (blksz == 0 || blksz == -1) { /* let the driver choose values */
- if (blksz == -1) /* delete previous values */
- c->flags &= ~CHN_F_HAS_SIZE;
- if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
- /*
- * compute a base blksz according to the target irq
- * rate, then round to a suitable power of 2
- * in the range 16.. 2^17/2.
- * Finally compute a suitable blkcnt.
- */
- blksz = round_bufsz( (sndbuf_getbps(bs) *
- sndbuf_getspd(bs)) / chn_targetirqrate,
- 16, CHN_2NDBUFMAXSIZE / 2);
- blkcnt = CHN_2NDBUFMAXSIZE / blksz;
- } else { /* use previously defined value */
- blkcnt = sndbuf_getblkcnt(bs);
- blksz = sndbuf_getblksz(bs);
- }
- } else {
- /*
- * use supplied values if reasonable. Note that here we
- * might have blksz which is not a power of 2 if the
- * ioctl() to compute it allows such values.
- */
- ret = EINVAL;
- if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
- goto out;
- ret = 0;
- c->flags |= CHN_F_HAS_SIZE;
- }
-
- reqblksz = blksz;
- if (reqblksz < sndbuf_getbps(bs))
- reqblksz = sndbuf_getbps(bs);
- if (reqblksz % sndbuf_getbps(bs))
- reqblksz -= reqblksz % sndbuf_getbps(bs);
-
- /* adjust for different hw format/speed */
- /*
- * Now compute the approx irq rate for the given (soft) blksz,
- * reduce to the acceptable range and compute a corresponding blksz
- * for the hard buffer. Then set the channel's blocksize and
- * corresponding hardbuf value. The number of blocks used should
- * be set by the device-specific routine. In fact, even the
- * call to sndbuf_setblksz() should not be here! XXX
- */
-
- irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
- RANGE(irqhz, 16, 512);
-
- maxsz = sndbuf_getmaxsize(b);
- if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
- maxsz = CHN_2NDBUFMAXSIZE;
- blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
- 16, maxsz / 2);
-
- /* Increase the size of bufsoft if before increasing bufhard. */
- maxsize = sndbuf_getsize(b);
- if (sndbuf_getsize(bs) > maxsize)
- maxsize = sndbuf_getsize(bs);
- if (reqblksz * blkcnt > maxsize)
- maxsize = reqblksz * blkcnt;
- if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
- ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
- if (ret)
- goto out1;
- }
-
- CHN_UNLOCK(c);
- sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
- CHN_LOCK(c);
-
- /* Decrease the size of bufsoft after decreasing bufhard. */
- maxsize = sndbuf_getsize(b);
- if (reqblksz * blkcnt > maxsize)
- maxsize = reqblksz * blkcnt;
- if (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) {
- ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
- if (ret)
- goto out1;
- }
-
- /*
- * OSSv4 docs: "By default OSS will set the low water level equal
- * to the fragment size which is optimal in most cases."
- */
- c->lw = sndbuf_getblksz(bs);
-
- 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",
- __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
- blksz, maxsize, blkcnt));
-out:
- c->flags &= ~CHN_F_SETBLOCKSIZE;
-#if 0
- if (1) {
- static uint32_t kk = 0;
- printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
- sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
- sndbuf_getbps(b),
- sndbuf_getspd(b), sndbuf_getfmt(b),
- sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
- sndbuf_getbps(bs),
- sndbuf_getspd(bs), sndbuf_getfmt(bs));
- if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
- sndbuf_getblksz(b) % sndbuf_getbps(b) ||
- sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
- sndbuf_getblksz(b) % sndbuf_getbps(b)) {
- printf("%u: bps/blksz alignment screwed!\n", kk);
- }
- }
-#endif
- return ret;
-}
-
int
chn_trigger(struct pcm_channel *c, int go)
{
@@ -1372,6 +1756,7 @@ chn_buildfeeder(struct pcm_channel *c)
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);
@@ -1393,7 +1778,7 @@ chn_buildfeeder(struct pcm_channel *c)
} 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;
@@ -1432,7 +1817,42 @@ chn_buildfeeder(struct pcm_channel *c)
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));
@@ -1441,7 +1861,7 @@ chn_buildfeeder(struct pcm_channel *c)
}
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"));
@@ -1450,7 +1870,7 @@ chn_buildfeeder(struct pcm_channel *c)
}
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));
@@ -1475,7 +1895,7 @@ chn_buildfeeder(struct pcm_channel *c)
sndbuf_setfmt(c->bufhard, hwfmt);
if ((flags & (1 << FEEDER_VOLUME))) {
- uint32_t parent = SOUND_MIXER_NONE;
+ u_int32_t parent = SOUND_MIXER_NONE;
int vol, left, right;
vol = 100 | (100 << 8);
@@ -1553,20 +1973,10 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
*/
}
if (flags & CHN_N_BLOCKSIZE) {
- int blksz;
/*
- * scan the children, find the lowest blocksize and use that
- * for the hard blocksize
+ * Set to default latency profile
*/
- blksz = sndbuf_getmaxsize(c->bufhard) / 2;
- SLIST_FOREACH(pce, &c->children, link) {
- child = pce->channel;
- CHN_LOCK(child);
- if (sndbuf_getblksz(child->bufhard) < blksz)
- blksz = sndbuf_getblksz(child->bufhard);
- CHN_UNLOCK(child);
- }
- chn_setblocksize(c, 2, blksz);
+ chn_setlatency(c, chn_latency);
}
if (flags & CHN_N_TRIGGER) {
int nrun;
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index d6426f7..3d4f537 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -79,6 +79,7 @@ struct pcm_channel {
u_int32_t align;
int volume;
+ int latency;
u_int32_t speed;
u_int32_t format;
u_int32_t flags;
@@ -86,7 +87,8 @@ struct pcm_channel {
u_int32_t blocks;
int direction;
- unsigned int interrupts, xruns;
+ unsigned int interrupts, xruns, feedcount;
+ unsigned int timeout;
struct snd_dbuf *bufhard, *bufsoft;
struct snddev_info *parentsnddev;
struct pcm_channel *parentchannel;
@@ -142,6 +144,7 @@ int chn_setvolume(struct pcm_channel *c, int left, int right);
int chn_setspeed(struct pcm_channel *c, int speed);
int chn_setformat(struct pcm_channel *c, u_int32_t fmt);
int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz);
+int chn_setlatency(struct pcm_channel *c, int latency);
int chn_trigger(struct pcm_channel *c, int go);
int chn_getptr(struct pcm_channel *c);
struct pcmchan_caps *chn_getcaps(struct pcm_channel *c);
@@ -181,6 +184,30 @@ int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
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);
+
+extern int chn_latency;
+extern int chn_latency_profile;
+extern int report_soft_formats;
+
#define PCMDIR_VIRTUAL 2
#define PCMDIR_PLAY 1
#define PCMDIR_REC -1
@@ -218,6 +245,17 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
#define CHN_N_BLOCKSIZE 0x00000008
#define CHN_N_TRIGGER 0x00000010
+#define CHN_LATENCY_MIN 0
+#define CHN_LATENCY_MAX 10
+#define CHN_LATENCY_DEFAULT 5
+#define CHN_POLICY_MIN CHN_LATENCY_MIN
+#define CHN_POLICY_MAX CHN_LATENCY_MAX
+#define CHN_POLICY_DEFAULT CHN_LATENCY_DEFAULT
+
+#define CHN_LATENCY_PROFILE_MIN 0
+#define CHN_LATENCY_PROFILE_MAX 1
+#define CHN_LATENCY_PROFILE_DEFAULT CHN_LATENCY_PROFILE_MAX
+
/*
* This should be large enough to hold all pcm data between
* tsleeps in chn_{read,write} at the highest sample rate.
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 56f50a7..940c3fd 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -215,10 +215,12 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
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;
@@ -768,7 +770,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
/* chn_sync may sleep */
if (wrch) {
CHN_LOCK(wrch);
- chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
+ chn_sync(wrch, 0);
CHN_UNLOCK(wrch);
}
break;
@@ -1085,12 +1087,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SNDCTL_DSP_GETODELAY:
if (wrch) {
- struct snd_dbuf *b = wrch->bufhard;
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
/* XXX abusive DMA update: chn_wrupdate(wrch); */
- *arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
+ *arg_i = sndbuf_getready(bs);
CHN_UNLOCK(wrch);
} else
ret = EINVAL;
@@ -2131,31 +2132,29 @@ dsp_oss_syncstart(int sg_id)
static int
dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
{
- int fragln, fragsz, maxfrags, ret;
+ int ret;
+
+ if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
+ return EIO;
/* Default: success */
ret = 0;
- /* Scale policy [0..10] to fragment size [2^4..2^16]. */
- fragln = policy;
- RANGE(fragln, 0, 10);
- fragln += 4;
- fragsz = 1 << fragln;
-
- maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
-
if (rdch) {
CHN_LOCK(rdch);
- ret = chn_setblocksize(rdch, maxfrags, fragsz);
+ ret = chn_setlatency(rdch, policy);
CHN_UNLOCK(rdch);
}
if (wrch && ret == 0) {
CHN_LOCK(wrch);
- ret = chn_setblocksize(wrch, maxfrags, fragsz);
+ ret = chn_setlatency(wrch, policy);
CHN_UNLOCK(wrch);
}
+ if (ret)
+ ret = EIO;
+
return ret;
}
diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c
index 7143902..0c94319 100644
--- a/sys/dev/sound/pcm/fake.c
+++ b/sys/dev/sound/pcm/fake.c
@@ -142,10 +142,11 @@ fkchan_setup(device_t dev)
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;
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
index b70bd58..4f0de4f 100644
--- a/sys/dev/sound/pcm/feeder.c
+++ b/sys/dev/sound/pcm/feeder.c
@@ -35,6 +35,11 @@ MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
#define MAXFEEDERS 256
#undef FEEDER_DEBUG
+int feeder_buffersize = FEEDBUFSZ;
+TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize);
+SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD,
+ &feeder_buffersize, FEEDBUFSZ, "feeder buffer size");
+
struct feedertab_entry {
SLIST_ENTRY(feedertab_entry) link;
struct feeder_class *feederclass;
@@ -72,6 +77,53 @@ feeder_register(void *p)
SLIST_INSERT_HEAD(&feedertab, fte, link);
feedercnt++;
+ /* initialize global variables */
+
+ if (snd_verbose < 0 || snd_verbose > 3)
+ snd_verbose = 1;
+
+ if (snd_unit < 0 || snd_unit > PCMMAXDEV)
+ snd_unit = 0;
+
+ if (snd_maxautovchans < 0 ||
+ snd_maxautovchans > SND_MAXVCHANS)
+ snd_maxautovchans = 0;
+
+ if (chn_latency < CHN_LATENCY_MIN ||
+ chn_latency > CHN_LATENCY_MAX)
+ chn_latency = CHN_LATENCY_DEFAULT;
+
+ if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
+ chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
+ chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
+
+ if (feeder_buffersize < FEEDBUFSZ_MIN ||
+ feeder_buffersize > FEEDBUFSZ_MAX)
+ feeder_buffersize = FEEDBUFSZ;
+
+ if (feeder_rate_min < FEEDRATE_MIN ||
+ feeder_rate_max < FEEDRATE_MIN ||
+ feeder_rate_min > FEEDRATE_MAX ||
+ feeder_rate_max > FEEDRATE_MAX ||
+ !(feeder_rate_min < feeder_rate_max)) {
+ feeder_rate_min = FEEDRATE_RATEMIN;
+ feeder_rate_max = FEEDRATE_RATEMAX;
+ }
+
+ if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
+ feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
+ feeder_rate_round = FEEDRATE_ROUNDHZ;
+
+ if (bootverbose)
+ printf("%s: snd_unit=%d snd_maxautovchans=%d "
+ "latency=%d feeder_buffersize=%d "
+ "feeder_rate_min=%d feeder_rate_max=%d "
+ "feeder_rate_round=%d\n",
+ __func__, snd_unit, snd_maxautovchans,
+ chn_latency, feeder_buffersize,
+ feeder_rate_min, feeder_rate_max,
+ feeder_rate_round);
+
/* we've got our root feeder so don't veto pcm loading anymore */
pcm_veto_load = 0;
@@ -259,122 +311,152 @@ chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
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 +471,184 @@ chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
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 +661,15 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
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 +682,60 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
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;
@@ -521,28 +794,78 @@ static int
feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source)
{
struct snd_dbuf *src = source;
- int l;
- u_int8_t x;
+ int l, offset;
KASSERT(count > 0, ("feed_root: count == 0"));
/* count &= ~((1 << ch->align) - 1); */
KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
+ if (++ch->feedcount == 0)
+ ch->feedcount = 2;
+
l = min(count, sndbuf_getready(src));
- sndbuf_dispose(src, buffer, l);
/* When recording only return as much data as available */
- if (ch->direction == PCMDIR_REC)
+ if (ch->direction == PCMDIR_REC) {
+ sndbuf_dispose(src, buffer, l);
return l;
+ }
-/*
- if (l < count)
- printf("appending %d bytes\n", count - l);
-*/
- x = (sndbuf_getfmt(src) & AFMT_SIGNED)? 0 : 0x80;
- while (l < count)
- buffer[l++] = x;
+ offset = count - l;
+
+ if (offset > 0) {
+ if (snd_verbose > 3)
+ printf("%s: (%s) %spending %d bytes "
+ "(count=%d l=%d feed=%d)\n",
+ __func__,
+ (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
+ (ch->feedcount == 1) ? "pre" : "ap",
+ offset, count, l, ch->feedcount);
+
+ if (ch->feedcount == 1) {
+ if (offset > 0)
+ memset(buffer,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (l > 0)
+ sndbuf_dispose(src, buffer + offset, l);
+ else
+ ch->feedcount--;
+ } else {
+ if (l > 0)
+ sndbuf_dispose(src, buffer, l);
+ if (offset > 0) {
+#if 1
+ memset(buffer + l,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (!(ch->flags & CHN_F_CLOSING))
+ ch->xruns++;
+#else
+ if (l < 1 || (ch->flags & CHN_F_CLOSING)) {
+ memset(buffer + l,
+ sndbuf_zerodata(sndbuf_getfmt(src)),
+ offset);
+ if (!(ch->flags & CHN_F_CLOSING))
+ ch->xruns++;
+ } else {
+ int cp, tgt;
+
+ tgt = l;
+ while (offset > 0) {
+ cp = min(l, offset);
+ memcpy(buffer + tgt, buffer, cp);
+ offset -= cp;
+ tgt += cp;
+ }
+ ch->xruns++;
+ }
+#endif
+ }
+ }
+ } else if (l > 0)
+ sndbuf_dispose(src, buffer, l);
return count;
}
@@ -561,8 +884,3 @@ static struct feeder_class feeder_root_class = {
};
SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class);
SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL);
-
-
-
-
-
diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h
index 545fd24..a3985c0 100644
--- a/sys/dev/sound/pcm/feeder.h
+++ b/sys/dev/sound/pcm/feeder.h
@@ -53,7 +53,7 @@ struct pcm_feeder {
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,11 +72,11 @@ static struct feeder_class feeder ## _class = { \
.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
-#define FEEDER_MIXER 3
+#define FEEDER_MIXER 3
#define FEEDER_RATE 4
#define FEEDER_FILTER 5
#define FEEDER_VOLUME 6
@@ -85,4 +85,27 @@ SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _cl
#define FEEDRATE_SRC 1
#define FEEDRATE_DST 2
+#define FEEDRATE_RATEMIN 1
+#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */
+#define FEEDRATE_MIN 1
+#define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */
+
+#define FEEDRATE_ROUNDHZ 25
+#define FEEDRATE_ROUNDHZ_MIN 0
+#define FEEDRATE_ROUNDHZ_MAX 500
+
+/*
+ * Default buffer size for feeder processing.
+ *
+ * Big = less sndbuf_feed(), more memory usage.
+ * Small = aggresive sndbuf_feed() (perhaps too much), less memory usage.
+ */
+#define FEEDBUFSZ 16384
+#define FEEDBUFSZ_MIN 2048
+#define FEEDBUFSZ_MAX 131072
+
+extern int feeder_rate_min;
+extern int feeder_rate_max;
+extern int feeder_rate_round;
+extern int feeder_buffersize;
diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c
index 17cd030..0ccc7b9 100644
--- a/sys/dev/sound/pcm/feeder_fmt.c
+++ b/sys/dev/sound/pcm/feeder_fmt.c
@@ -43,12 +43,13 @@ SND_DECLARE_FILE("$FreeBSD$");
MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder");
-#define FEEDBUFSZ 8192
-#define FEEDBUF24SZ 8190
-
#define FMT_TRACE(x...) /* printf(x) */
#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */
-#define FMT_ALIGNBYTE(x) /* x */
+
+struct feed_fmt_info {
+ int bps, bufsz;
+ uint8_t *buffer;
+};
/*
* Sign inverted ulaw/alaw -> 8 table
@@ -194,33 +195,48 @@ static uint8_t u8_to_alaw_tbl[] = {
};
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;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 1)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count, source);
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;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
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,188 +245,286 @@ static int
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;
uint8_t *tbl = (uint8_t *)f->data;
+ if (count < 1)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count, source);
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.
*/
static int
-feed_common_init(struct pcm_feeder *f)
+feed_fmt_init(struct pcm_feeder *f)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT|M_ZERO);
- if (f->data == NULL)
+ struct feed_fmt_info *info;
+
+ info = malloc(sizeof(*info), M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ if (info == NULL)
+ return ENOMEM;
+
+ info->bps = 1;
+ info->bps <<= (f->desc->in & AFMT_STEREO) ? 1 : 0;
+ if (f->desc->in & AFMT_16BIT)
+ info->bps <<= 1;
+ else if (f->desc->in & AFMT_24BIT)
+ info->bps *= 3;
+ else if (f->desc->in & AFMT_32BIT)
+ info->bps <<= 2;
+ info->bufsz = feeder_buffersize - (feeder_buffersize % info->bps);
+
+ info->buffer = malloc(info->bufsz, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ if (info->buffer == NULL) {
+ free(info, M_FMTFEEDER);
return ENOMEM;
+ }
+
+ f->data = info;
+
return 0;
}
static int
-feed_common_free(struct pcm_feeder *f)
+feed_fmt_free(struct pcm_feeder *f)
{
- if (f->data)
- free(f->data, M_FMTFEEDER);
+ struct feed_fmt_info *info = f->data;
+
+ if (info) {
+ if (info->buffer)
+ free(info->buffer, M_FMTFEEDER);
+ free(info, M_FMTFEEDER);
+ }
f->data = NULL;
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 i, j, k;
+ int sign;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
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;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
+ if (count < 1)
+ return 0;
k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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;
- 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[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_16leto8),
+static kobj_method_t feeder_16to8_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ 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;
+ if (count < 3)
+ return 0;
k = (count / 3) << 1;
k = FEEDER_FEED(f->source, c, b, k, source);
if (k < 2) {
@@ -419,146 +533,370 @@ feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
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;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
- k = (count * 3) >> 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source);
+ if (count < 2)
+ return 0;
+ k = (count >> 1) * 3;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__);
- 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[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_24leto16le),
+static kobj_method_t feeder_24to16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ 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;
+
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 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;
- uint8_t *src = (uint8_t *)f->data;
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 2)
+ return 0;
+ k = (count & ~1) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__);
- 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_32to16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_32to16),
+ {0, 0}
+};
+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;
+
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count >> 2) * 3, 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_32leto16le_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
- KOBJMETHOD(feeder_feed, feed_32leto16le),
+static kobj_method_t feeder_24to32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_24to32),
{0, 0}
};
-FEEDER_DECLARE(feeder_32leto16le, 1, NULL);
+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)
+{
+ struct feed_fmt_info *info = f->data;
+ int i, j, k, sign;
+ uint8_t *src = info->buffer;
+
+ if (count < 3)
+ return 0;
+ k = (count / 3) << 2;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
+ KOBJMETHOD(feeder_feed, feed_32to24),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_32to24, 1, NULL);
/*
* Bit conversion end
*/
@@ -570,8 +908,11 @@ static int
feed_monotostereo8(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, k;
+ if (count < 2)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, count >> 1, source);
i = k;
j = k << 1;
while (i > 0) {
@@ -583,6 +924,8 @@ feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
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[] = {
@@ -595,19 +938,22 @@ static int
feed_monotostereo16(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, k;
uint8_t l, m;
+ if (count < 4)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 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;
- while (i > 0) {
+ while (i > 1) {
l = b[--i];
m = b[--i];
b[--j] = l;
@@ -634,19 +980,22 @@ static int
feed_monotostereo24(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, k;
uint8_t l, m, n;
+ if (count < 6)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, ((count / 6) * 6) >> 1, 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__);
- 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];
@@ -676,19 +1025,22 @@ static int
feed_monotostereo32(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, k;
uint8_t l, m, n, o;
+ if (count < 8)
+ return 0;
+ k = FEEDER_FEED(f->source, c, b, (count & ~7) >> 1, 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__);
- 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];
@@ -727,18 +1079,21 @@ static int
feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
+ if (count < 1)
+ return 0;
k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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 8bit (stereo) aligned.\n", __func__);
- FMT_ALIGNBYTE(k &= ~1);
+ k &= ~1;
i = k >> 1;
j = i;
while (i > 0) {
@@ -750,11 +1105,13 @@ feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
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[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono8),
{0, 0}
};
@@ -764,21 +1121,24 @@ static int
feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 2)
+ return 0;
+ k = (count & ~1) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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 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];
@@ -793,8 +1153,8 @@ static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono16_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono16),
{0, 0}
};
@@ -804,21 +1164,24 @@ static int
feed_stereotomono24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source);
+ if (count < 3)
+ return 0;
+ k = ((count / 3) * 3) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 6) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
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];
@@ -834,8 +1197,8 @@ static struct pcm_feederdesc feeder_stereotomono24_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono24_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono24),
{0, 0}
};
@@ -845,21 +1208,24 @@ static int
feed_stereotomono32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
uint32_t count, void *source)
{
+ struct feed_fmt_info *info = f->data;
int i, j, k;
- uint8_t *src = (uint8_t *)f->data;
+ uint8_t *src = info->buffer;
- k = count << 1;
- k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (count < 4)
+ return 0;
+ k = (count & ~3) << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source);
if (k < 8) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, k);
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];
@@ -876,8 +1242,8 @@ static struct pcm_feederdesc feeder_stereotomono32_desc[] = {
{0, 0, 0, 0},
};
static kobj_method_t feeder_stereotomono32_methods[] = {
- KOBJMETHOD(feeder_init, feed_common_init),
- KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_init, feed_fmt_init),
+ KOBJMETHOD(feeder_free, feed_fmt_free),
KOBJMETHOD(feeder_feed, feed_stereotomono32),
{0, 0}
};
@@ -893,8 +1259,11 @@ static int
feed_sign8(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);
+ int i, j;
+ if (count < 1)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count, source);
i = j;
while (i > 0)
b[--i] ^= 0x80;
@@ -914,103 +1283,145 @@ static kobj_method_t feeder_sign8_methods[] = {
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);
+ int i, j;
+ if (count < 2)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~1, source);
if (j < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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);
+ int i, j;
+ if (count < 3)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source);
if (j < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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);
+ int i, j;
+ if (count < 4)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~3, source);
if (j < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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.
*/
@@ -1022,18 +1433,21 @@ static int
feed_endian16(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);
+ int i, j;
uint8_t v;
+ if (count < 2)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~1, source);
if (j < 2) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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;
@@ -1061,18 +1475,21 @@ static int
feed_endian24(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);
+ int i, j;
uint8_t v;
+ if (count < 3)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source);
if (j < 3) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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;
@@ -1100,18 +1517,21 @@ static int
feed_endian32(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);
+ int i, j;
uint8_t l, m;
+ if (count < 4)
+ return 0;
+ j = FEEDER_FEED(f->source, c, b, count & ~3, source);
if (j < 4) {
FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
__func__, j);
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];
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index b4496d1..a3fe99d 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -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,24 @@
SND_DECLARE_FILE("$FreeBSD$");
-#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 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
-
-#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))
+
+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,138 +102,148 @@ struct feed_rate_info {
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;
+int feeder_rate_min = FEEDRATE_RATEMIN;
+int feeder_rate_max = FEEDRATE_RATEMAX;
+int feeder_rate_round = FEEDRATE_ROUNDHZ;
-/*
- * 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_min", &feeder_rate_min);
+TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max);
+TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round);
static int
-sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = feeder_rate_ratemin;
+ val = feeder_rate_min;
err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val < 1 || val >= feeder_rate_ratemax)
- err = EINVAL;
+ if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max)
+ feeder_rate_min = val;
else
- feeder_rate_ratemin = val;
- 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", "");
-
-static int
-sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS)
-{
- int err, val;
-
- val = feeder_rate_ratemax;
- err = sysctl_handle_int(oidp, &val, sizeof(val), req);
- if (val <= feeder_rate_ratemin || val > 0x7fffff)
err = EINVAL;
- else
- feeder_rate_ratemax = val;
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", "");
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I",
+ "minimum allowable rate");
static int
-sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS)
{
int err, val;
- val = feeder_rate_scaling;
+ val = feeder_rate_max;
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.
- */
- if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST)
- err = EINVAL;
+ if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min)
+ feeder_rate_max = val;
else
- feeder_rate_scaling = val;
+ err = EINVAL;
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_max, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I",
+ "maximum allowable rate");
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)
+ if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX)
err = EINVAL;
else
- feeder_rate_buffersize = val & ~1;
+ feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ);
return err;
}
-/* XXX: this should be settable by an user via a control tool, the sysadmin
- needs a max and min sysctl to limit what an user can do */
-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",
+ "sample rate converter rounding threshold");
+
+#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;
@@ -242,242 +255,112 @@ feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
}
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 > 0) ? feeder_rate_round : 1));
+ info->dst = info->rdst - (info->rdst %
+ ((feeder_rate_round > 0) ? feeder_rate_round : 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 bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_rate_converter convert;
+ } convtbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_convert_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_convert_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be },
+ { 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 & ~AFMT_STEREO) == convtbl[i].format) {
+ 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->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
+ info->pos = info->bps * info->channels;
+ info->bpos = info->pos << 1;
+ info->bufsz -= info->bufsz % info->pos;
+
+ memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos);
+
+ 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->pos);
+
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)
+ if (value < feeder_rate_min || value > feeder_rate_max)
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);
}
@@ -487,16 +370,13 @@ feed_rate_get(struct pcm_feeder *f, int what)
{
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;
}
@@ -506,14 +386,17 @@ feed_rate_init(struct pcm_feeder *f)
{
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
*/
- info->bufsz = 2 + feeder_rate_buffersize;
- info->buffer = malloc(sizeof(*info->buffer) * info->bufsz,
+ info->bufsz_init = 8 + feeder_buffersize;
+ info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init,
M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info->buffer == NULL) {
free(info, M_RATEFEEDER);
@@ -539,233 +422,88 @@ feed_rate_free(struct pcm_feeder *f)
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,
@@ -779,7 +517,7 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
break;
}
if (info->pos == info->bpos) {
- RATE_TEST(info->pos == 2,
+ RATE_TEST(info->pos == smpsz,
("%s: EOF while in progress\n", __func__));
break;
}
@@ -788,10 +526,10 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
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));
@@ -802,31 +540,67 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
* 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),
- KOBJMETHOD(feeder_set, feed_rate_set),
- KOBJMETHOD(feeder_get, feed_rate_get),
- KOBJMETHOD(feeder_feed, feed_rate),
+ KOBJMETHOD(feeder_init, feed_rate_init),
+ KOBJMETHOD(feeder_free, feed_rate_free),
+ KOBJMETHOD(feeder_set, feed_rate_set),
+ KOBJMETHOD(feeder_get, feed_rate_get),
+ KOBJMETHOD(feeder_feed, feed_rate),
{0, 0}
};
+
FEEDER_DECLARE(feeder_rate, 2, NULL);
diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c
index bb46ea0..544194c 100644
--- a/sys/dev/sound/pcm/feeder_volume.c
+++ b/sys/dev/sound/pcm/feeder_volume.c
@@ -31,48 +31,193 @@
SND_DECLARE_FILE("$FreeBSD$");
+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 bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_volume_filter filter;
+ } voltbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be },
+ { 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 & ~AFMT_STEREO) == voltbl[i].format) {
+ info->bps = voltbl[i].bps;
+ info->filter = voltbl[i].filter;
+ break;
+ }
+ }
+
+ /* For now, this is mandatory! */
+ info->channels = 2;
+
+ return 0;
+}
+
static int
-feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
- uint32_t count, void *source)
+feed_volume_init(struct pcm_feeder *f)
{
- int i, j, k, vol[2];
- int16_t *buf;
+ 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);
+}
- 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",
+static int
+feed_volume_free(struct pcm_feeder *f)
+{
+ struct feed_volume_info *info = f->data;
+
+ 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;
+ if (count < smpsz)
+ return 0;
+ 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);
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index d0f6946..3deded7 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -631,7 +631,7 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
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);
@@ -663,9 +663,11 @@ mixer_hwvol_init(device_t dev)
m->hwvol_mixer = SOUND_MIXER_VOLUME;
m->hwvol_step = 5;
#ifdef SND_DYNSYSCTL
- SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
- SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
sysctl_hw_snd_hwvol_mixer, "A", "");
#endif
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index 5b07ece..32188ff 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -72,11 +72,11 @@ static int sndstat_files = 0;
static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
-static int sndstat_verbose = 1;
+int snd_verbose = 1;
#ifdef USING_MUTEX
-TUNABLE_INT("hw.snd.verbose", &sndstat_verbose);
+TUNABLE_INT("hw.snd.verbose", &snd_verbose);
#else
-TUNABLE_INT_DECL("hw.snd.verbose", 1, sndstat_verbose);
+TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose);
#endif
static int sndstat_prepare(struct sbuf *s);
@@ -86,20 +86,20 @@ sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
{
int error, verbose;
- verbose = sndstat_verbose;
+ verbose = snd_verbose;
error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req);
if (error == 0 && req->newptr != NULL) {
sx_xlock(&sndstat_lock);
- if (verbose < 0 || verbose > 3)
+ if (verbose < 0 || verbose > 4)
error = EINVAL;
else
- sndstat_verbose = verbose;
+ snd_verbose = verbose;
sx_xunlock(&sndstat_lock);
}
return error;
}
SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_sndverbose, "I", "");
+ 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level");
static int
sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
@@ -300,7 +300,8 @@ sndstat_prepare(struct sbuf *s)
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);
@@ -318,14 +319,14 @@ sndstat_prepare(struct sbuf *s)
sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
sbuf_printf(s, " %s", ent->str);
if (ent->handler)
- ent->handler(s, ent->dev, sndstat_verbose);
+ ent->handler(s, ent->dev, snd_verbose);
else
sbuf_printf(s, " [no handler]");
sbuf_printf(s, "\n");
}
}
- if (sndstat_verbose >= 3 && sndstat_files > 0) {
+ if (snd_verbose >= 3 && sndstat_files > 0) {
sbuf_printf(s, "\nFile Versions:\n");
SLIST_FOREACH(ent, &sndstat_devlist, link) {
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 5ab8343..719bd93 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -26,6 +26,7 @@
*/
#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
#include <dev/sound/pcm/vchan.h>
#include <dev/sound/pcm/dsp.h>
#include <sys/limits.h>
@@ -59,22 +60,6 @@ struct unrhdr *pcmsg_unrhdr = NULL;
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
-struct sysctl_ctx_list *
-snd_sysctl_tree(device_t dev)
-{
- struct snddev_info *d = device_get_softc(dev);
-
- return &d->sysctl_tree;
-}
-
-struct sysctl_oid *
-snd_sysctl_tree_top(device_t dev)
-{
- struct snddev_info *d = device_get_softc(dev);
-
- return d->sysctl_tree_top;
-}
-
void *
snd_mtxcreate(const char *desc, const char *type)
{
@@ -176,6 +161,11 @@ pcm_setvchans(struct snddev_info *d, int newcnt)
pcm_inprog(d, 1);
+ if (d->playcount < 1) {
+ err = ENODEV;
+ goto setvchans_out;
+ }
+
if (!(d->flags & SD_F_AUTOVCHAN)) {
err = EINVAL;
goto setvchans_out;
@@ -238,7 +228,7 @@ addok:
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
@@ -392,7 +382,7 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
}
/* XXX: do we need a way to let the user change the default unit? */
SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "");
+ 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
#endif
static int
@@ -419,7 +409,7 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
return (error);
}
SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
+ 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
struct pcm_channel *
pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
@@ -556,6 +546,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
unsigned rdevcount;
int device = device_get_unit(d->dev);
size_t namelen;
+ char dtype;
/*
* Note it's confusing nomenclature.
@@ -647,11 +638,20 @@ retry_chan_num_search_out:
}
#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);
@@ -673,11 +673,11 @@ retry_chan_num_search_out:
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;
}
@@ -770,7 +770,7 @@ pcm_setstatus(device_t dev, char *str)
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);
@@ -869,18 +869,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
chn_init(d->fakechan, NULL, 0, 0);
#ifdef SND_DYNSYSCTL
- sysctl_ctx_init(&d->sysctl_tree);
- d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
- SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
- device_get_nameunit(dev), CTLFLAG_RD, 0, "");
- if (d->sysctl_tree_top == NULL) {
- sysctl_ctx_free(&d->sysctl_tree);
- goto no;
- }
/* XXX: an user should be able to set this with a control tool, the
sysadmin then needs min+max sysctls for this */
- SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
- OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
#endif
if (numplay > 0) {
d->flags |= SD_F_AUTOVCHAN;
@@ -889,9 +882,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
sndstat_register(dev, d->status, sndstat_prepare_pcm);
return 0;
-no:
- snd_mtxfree(d->lock);
- return ENXIO;
}
int
@@ -945,9 +935,9 @@ pcm_unregister(device_t dev)
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;
@@ -967,9 +957,11 @@ pcm_unregister(device_t dev)
}
#ifdef SND_DYNSYSCTL
+#if 0
d->sysctl_tree_top = NULL;
sysctl_ctx_free(&d->sysctl_tree);
#endif
+#endif
#if 0
SLIST_FOREACH(sce, &d->channels, link) {
@@ -1068,15 +1060,15 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
sbuf_printf(s, "interrupts %d, ", c->interrupts);
if (c->direction == PCMDIR_REC)
- sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
+ sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
sndbuf_getblkcnt(c->bufhard),
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
sndbuf_getblkcnt(c->bufsoft));
else
- sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, sndbuf_getready(c->bufsoft),
+ sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
sndbuf_getblkcnt(c->bufhard),
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index eeb5c20..b62b1a5 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -58,6 +58,7 @@
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/rman.h>
+#include <sys/limits.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/sbuf.h>
@@ -148,6 +149,281 @@ nomenclature:
(((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)
@@ -185,7 +461,7 @@ int fkchan_kill(struct pcm_channel *c);
#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
@@ -194,6 +470,8 @@ int fkchan_kill(struct pcm_channel *c);
extern int pcm_veto_load;
extern int snd_unit;
+extern int snd_maxautovchans;
+extern int snd_verbose;
extern devclass_t pcm_devclass;
extern struct unrhdr *pcmsg_unrhdr;
@@ -210,9 +488,6 @@ extern struct unrhdr *pcmsg_unrhdr;
SYSCTL_DECL(_hw_snd);
-struct sysctl_ctx_list *snd_sysctl_tree(device_t dev);
-struct sysctl_oid *snd_sysctl_tree_top(device_t dev);
-
struct pcm_channel *pcm_getfakechan(struct snddev_info *d);
int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum);
int pcm_chnrelease(struct pcm_channel *c);
@@ -287,7 +562,7 @@ struct snddev_channel {
struct cdev *dsp_devt;
struct cdev *dspW_devt;
struct cdev *audio_devt;
- struct cdev *dspr_devt;
+ struct cdev *dspHW_devt;
};
struct snddev_info {
@@ -300,8 +575,6 @@ struct snddev_info {
void *devinfo;
device_t dev;
char status[SND_STATUSLEN];
- struct sysctl_ctx_list sysctl_tree;
- struct sysctl_oid *sysctl_tree_top;
struct mtx *lock;
struct cdev *mixer_dev;
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index b3fbd25..b210dca 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -1,5 +1,6 @@
/*-
- * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
+ * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
+ * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
* 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 <dev/sound/pcm/sound.h>
@@ -30,74 +34,210 @@
SND_DECLARE_FILE("$FreeBSD$");
+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 bps; /* bytes-per-sample, regardless of
+ total channels */
+ feed_vchan_mixer mix;
+ } vchan_mix_tbl[] = {
+ { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne },
+ { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
+ { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
+ { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
+ { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
+ { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
+ { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
+ /* unsigned */
+ { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne },
+ { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
+ { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
+ { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
+ { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
+ { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
+ { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
+ { 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 & ~AFMT_STEREO) == vchan_mix_tbl[i].format) {
+ info->bps = vchan_mix_tbl[i].bps;
+ info->mix = vchan_mix_tbl[i].mix;
+ break;
}
- to[i] = x & 0x0000ffff;
}
+
+ info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
+ info->zero_sample = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80;
+
+ return 0;
+}
+
+static int
+feed_vchan_init(struct pcm_feeder *f)
+{
+ 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_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- /* we're going to abuse things a bit */
+ 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 +245,68 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32
* 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);
+ 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);
+ CHN_UNLOCK(ch);
}
- return rcnt & ~1;
+ if (++c->feedcount == 0)
+ c->feedcount = 2;
+
+ 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 +338,7 @@ vchan_free(kobj_t obj, void *data)
}
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;
@@ -180,15 +353,15 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
ch->bps *= 3;
else if (ch->fmt & AFMT_32BIT)
ch->bps <<= 2;
- CHN_UNLOCK(channel);
+ CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_FORMAT);
- CHN_LOCK(channel);
+ CHN_LOCK(channel);
sndbuf_setfmt(channel->bufsoft, format);
return 0;
}
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 +377,7 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
}
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;
@@ -213,18 +386,27 @@ vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
int prate, crate;
ch->blksz = blocksize;
- /* CHN_UNLOCK(channel); */
+ /* CHN_UNLOCK(channel); */
sndbuf_setblksz(channel->bufhard, blocksize);
chn_notify(parent, CHN_N_BLOCKSIZE);
- CHN_LOCK(parent);
- /* CHN_LOCK(channel); */
+ CHN_LOCK(parent);
+ /* CHN_LOCK(channel); */
crate = ch->spd * ch->bps;
prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
blocksize = sndbuf_getblksz(parent->bufsoft);
- CHN_UNLOCK(parent);
+ 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;
}
@@ -240,9 +422,9 @@ vchan_trigger(kobj_t obj, void *data, int go)
return 0;
ch->run = (go == PCMTRIG_START)? 1 : 0;
- CHN_UNLOCK(channel);
+ CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_TRIGGER);
- CHN_LOCK(channel);
+ CHN_LOCK(channel);
return 0;
}
@@ -251,24 +433,34 @@ static struct pcmchan_caps *
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;
}
static kobj_method_t vchan_methods[] = {
- KOBJMETHOD(channel_init, vchan_init),
- KOBJMETHOD(channel_free, vchan_free),
- KOBJMETHOD(channel_setformat, vchan_setformat),
- KOBJMETHOD(channel_setspeed, vchan_setspeed),
- KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
- KOBJMETHOD(channel_trigger, vchan_trigger),
- KOBJMETHOD(channel_getcaps, vchan_getcaps),
- { 0, 0 }
+ KOBJMETHOD(channel_init, vchan_init),
+ KOBJMETHOD(channel_free, vchan_free),
+ KOBJMETHOD(channel_setformat, vchan_setformat),
+ KOBJMETHOD(channel_setspeed, vchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
+ KOBJMETHOD(channel_trigger, vchan_trigger),
+ KOBJMETHOD(channel_getcaps, vchan_getcaps),
+ {0, 0}
};
CHANNEL_DECLARE(vchan);
@@ -280,7 +472,7 @@ static int
sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
- struct snddev_channel *sce;
+ struct snddev_channel *sce;
struct pcm_channel *c, *ch = NULL, *fake;
struct pcmchan_caps *caps;
int err = 0;
@@ -329,18 +521,20 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
}
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
if (err == 0 && req->newptr != NULL) {
- if (newspd < 1 || newspd < feeder_rate_ratemin ||
- newspd > feeder_rate_ratemax) {
+ if (newspd < 1 || newspd < feeder_rate_min ||
+ newspd > feeder_rate_max) {
pcm_inprog(d, -1);
return EINVAL;
}
CHN_LOCK(ch);
- caps = chn_getcaps(ch);
- if (caps == NULL || newspd < caps->minspeed ||
- newspd > caps->maxspeed) {
- CHN_UNLOCK(ch);
- pcm_inprog(d, -1);
- return EINVAL;
+ if (feeder_rate_round) {
+ caps = chn_getcaps(ch);
+ if (caps == NULL || newspd < caps->minspeed ||
+ newspd > caps->maxspeed) {
+ CHN_UNLOCK(ch);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
}
if (newspd != ch->speed) {
err = chn_setspeed(ch, newspd);
@@ -348,7 +542,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
* Try to avoid FEEDER_RATE on parent channel if the
* requested value is not supported by the hardware.
*/
- if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
+ if (!err && feeder_rate_round &&
+ (ch->feederflags & (1 << FEEDER_RATE))) {
newspd = sndbuf_getspd(ch->bufhard);
err = chn_setspeed(ch, newspd);
}
@@ -367,6 +562,97 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
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 */
@@ -374,11 +660,12 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
int
vchan_create(struct pcm_channel *parent)
{
- struct snddev_info *d = parent->parentsnddev;
+ struct snddev_info *d = parent->parentsnddev;
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;
@@ -388,7 +675,7 @@ vchan_create(struct pcm_channel *parent)
pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
if (!pce) {
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
return ENOMEM;
}
@@ -396,7 +683,7 @@ vchan_create(struct pcm_channel *parent)
child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
if (!child) {
free(pce, M_DEVBUF);
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
return ENODEV;
}
pce->channel = child;
@@ -413,7 +700,7 @@ vchan_create(struct pcm_channel *parent)
return err;
}
- CHN_LOCK(parent);
+ CHN_LOCK(parent);
/* add us to our parent channel's children */
first = SLIST_EMPTY(&parent->children);
SLIST_INSERT_HEAD(&parent->children, pce, link);
@@ -424,23 +711,50 @@ vchan_create(struct pcm_channel *parent)
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 +762,6 @@ vchan_create(struct pcm_channel *parent)
* 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),
@@ -456,6 +769,9 @@ vchan_create(struct pcm_channel *parent)
CHN_LOCK(parent);
if (r != 0) {
/*
+ * No saved value from fake channel,
+ * no hint, NOTHING.
+ *
* Workaround for sb16 running
* poorly at 45k / 49k.
*/
@@ -466,38 +782,45 @@ vchan_create(struct pcm_channel *parent)
break;
default:
speed = VCHAN_DEFAULT_SPEED;
+ if (speed > parent_caps->maxspeed)
+ speed = parent_caps->maxspeed;
break;
}
+ if (speed < parent_caps->minspeed)
+ speed = parent_caps->minspeed;
}
}
- /*
- * Limit speed based on driver caps.
- * This is supposed to help fixed rate, non-VRA
- * AC97 cards, but.. (see below)
- */
- if (speed < parent_caps->minspeed)
- speed = parent_caps->minspeed;
- if (speed > parent_caps->maxspeed)
- speed = parent_caps->maxspeed;
+ if (feeder_rate_round) {
+ /*
+ * Limit speed based on driver caps.
+ * This is supposed to help fixed rate, non-VRA
+ * AC97 cards, but.. (see below)
+ */
+ if (speed < parent_caps->minspeed)
+ speed = parent_caps->minspeed;
+ if (speed > parent_caps->maxspeed)
+ speed = parent_caps->maxspeed;
+ }
/*
* We still need to limit the speed between
- * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
+ * feeder_rate_min <-> feeder_rate_max. This is
* just an escape goat if all of the above failed
* miserably.
*/
- if (speed < feeder_rate_ratemin)
- speed = feeder_rate_ratemin;
- if (speed > feeder_rate_ratemax)
- speed = feeder_rate_ratemax;
+ if (speed < feeder_rate_min)
+ speed = feeder_rate_min;
+ if (speed > feeder_rate_max)
+ speed = feeder_rate_max;
err = chn_setspeed(parent, speed);
/*
* Try to avoid FEEDER_RATE on parent channel if the
* requested value is not supported by the hardware.
*/
- if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
+ if (!err && feeder_rate_round &&
+ (parent->feederflags & (1 << FEEDER_RATE))) {
speed = sndbuf_getspd(parent->bufhard);
err = chn_setspeed(parent, speed);
}
@@ -509,6 +832,7 @@ vchan_create(struct pcm_channel *parent)
CHN_UNLOCK(parent);
CHN_LOCK(fake);
fake->speed = speed;
+ fake->format = vchanfmt;
CHN_UNLOCK(fake);
CHN_LOCK(parent);
}
@@ -533,7 +857,7 @@ int
vchan_destroy(struct pcm_channel *c)
{
struct pcm_channel *parent = c->parentchannel;
- struct snddev_info *d = parent->parentsnddev;
+ struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct snddev_channel *sce;
uint32_t spd;
@@ -571,9 +895,9 @@ gotch:
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;
@@ -606,16 +930,19 @@ vchan_initsys(device_t dev)
#ifdef SND_DYNSYSCTL
struct snddev_info *d;
- d = device_get_softc(dev);
- /* XXX: the user should be able to set this with a control tool, the
- sysadmin needs a sysctl so set a max value for "vchan" and min+max
- values for "vchanrate" to limit what an user can do */
- 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_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", "");
+ d = device_get_softc(dev);
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
+ sysctl_hw_snd_vchanformat, "A", "virtual channel format");
#endif
return 0;
OpenPOWER on IntegriCloud