summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/sound/pcm/buffer.c8
-rw-r--r--sys/dev/sound/pcm/channel.c53
-rw-r--r--sys/dev/sound/pcm/dsp.c9
-rw-r--r--sys/dev/sound/pcm/fake.c22
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c982
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c961
-rw-r--r--sys/dev/sound/pcm/sound.h7
-rw-r--r--sys/dev/sound/pcm/vchan.c194
8 files changed, 1496 insertions, 740 deletions
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
index 9ca6a32..b8570f7 100644
--- a/sys/dev/sound/pcm/buffer.c
+++ b/sys/dev/sound/pcm/buffer.c
@@ -286,8 +286,12 @@ sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
b->fmt = fmt;
b->bps = 1;
b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
- b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0;
- b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0;
+ if (b->fmt & AFMT_16BIT)
+ b->bps <<= 1;
+ else if (b->fmt & AFMT_24BIT)
+ b->bps *= 3;
+ else if (b->fmt & AFMT_32BIT)
+ b->bps <<= 2;
return 0;
}
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index b43680c..fa56199 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -240,10 +240,30 @@ chn_wrfeed(struct pcm_channel *c)
KASSERT(amt <= sndbuf_getsize(bs),
("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
amt, sndbuf_getsize(bs), c->flags));
- if (sndbuf_getready(bs) < amt)
- c->xruns++;
- ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
+ if (SLIST_EMPTY(&c->children)) {
+ /*
+ * Hardware channel
+ */
+ if (sndbuf_getready(bs) < amt)
+ c->xruns++;
+ ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
+ } else {
+ /*
+ * vchan
+ */
+ if (amt > 0) {
+ ret = sndbuf_feed(bs, b, c, c->feeder, amt);
+ /*
+ * Possible vchan xruns. There should be no empty space
+ * left in buffer.
+ */
+ if (sndbuf_getfree(b) > 0)
+ c->xruns++;
+ } else
+ ret = ENOSPC;
+ }
+
if (ret == 0 && sndbuf_getfree(b) < amt)
chn_wakeup(c);
@@ -918,7 +938,10 @@ chn_tryspeed(struct pcm_channel *c, int speed)
delta = -delta;
c->feederflags &= ~(1 << FEEDER_RATE);
- if (delta > 500)
+ /*
+ * Used to be 500. It was too big!
+ */
+ if (delta > 25)
c->feederflags |= 1 << FEEDER_RATE;
else
sndbuf_setspd(bs, sndbuf_getspd(b));
@@ -932,8 +955,13 @@ chn_tryspeed(struct pcm_channel *c, int speed)
if (r)
goto out;
- if (!(c->feederflags & (1 << FEEDER_RATE)))
+ if (!(c->feederflags & (1 << FEEDER_RATE))) {
+ if (c->direction == PCMDIR_PLAY &&
+ !(c->flags & CHN_F_VIRTUAL))
+ r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
+ c->feeder->desc->out);
goto out;
+ }
r = EINVAL;
f = chn_findfeeder(c, FEEDER_RATE);
@@ -950,6 +978,17 @@ chn_tryspeed(struct pcm_channel *c, int speed)
x = (c->direction == PCMDIR_REC)? bs : b;
r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
+ if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL)
+ && !((c->format & AFMT_S16_LE) &&
+ (c->format & AFMT_STEREO))) {
+ uint32_t fmt;
+
+ fmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
+ if (fmt != 0)
+ r = CHANNEL_SETFORMAT(c->methods, c->devinfo, fmt);
+ else
+ r = EINVAL;
+ }
out:
DEB(printf("setspeed done, r = %d\n", r));
return r;
@@ -1176,7 +1215,9 @@ chn_getformats(struct pcm_channel *c)
/* report software-supported formats */
if (report_soft_formats)
- fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
+ fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
+ AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
+ AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
return fmts;
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 689bec6..c57b59f 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -942,7 +942,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SOUND_PCM_READ_BITS:
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
- *arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
+ if (chn->format & AFMT_8BIT)
+ *arg_i = 8;
+ else if (chn->format & AFMT_16BIT)
+ *arg_i = 16;
+ else if (chn->format & AFMT_24BIT)
+ *arg_i = 24;
+ else if (chn->format & AFMT_32BIT)
+ *arg_i = 32;
CHN_UNLOCK(chn);
break;
diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c
index ba9c4b6..ee27c5c 100644
--- a/sys/dev/sound/pcm/fake.c
+++ b/sys/dev/sound/pcm/fake.c
@@ -41,6 +41,22 @@ static u_int32_t fk_fmt[] = {
AFMT_STEREO | AFMT_S16_BE,
AFMT_U16_BE,
AFMT_STEREO | AFMT_U16_BE,
+ AFMT_S24_LE,
+ AFMT_STEREO | AFMT_S24_LE,
+ AFMT_U24_LE,
+ AFMT_STEREO | AFMT_U24_LE,
+ AFMT_S24_BE,
+ AFMT_STEREO | AFMT_S24_BE,
+ AFMT_U24_BE,
+ AFMT_STEREO | AFMT_U24_BE,
+ AFMT_S32_LE,
+ AFMT_STEREO | AFMT_S32_LE,
+ AFMT_U32_LE,
+ AFMT_STEREO | AFMT_U32_LE,
+ AFMT_S32_BE,
+ AFMT_STEREO | AFMT_S32_BE,
+ AFMT_U32_BE,
+ AFMT_STEREO | AFMT_U32_BE,
0
};
static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0};
@@ -120,6 +136,12 @@ fkchan_setup(device_t dev)
c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK);
c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK);
c->parentsnddev = d;
+ /*
+ * Fake channel is such a blessing in disguise. Using this,
+ * we can keep track prefered virtual channel speed without
+ * querying kernel hint repetitively (see vchan_create / vchan.c).
+ */
+ c->speed = 0;
snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
return c;
diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c
index 7eb66f5..d1e001c 100644
--- a/sys/dev/sound/pcm/feeder_fmt.c
+++ b/sys/dev/sound/pcm/feeder_fmt.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard@MyBSD.org.my>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,10 +23,20 @@
* 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.
+ *
+ * *New* and rewritten soft format converter, supporting 24/32bit pcm data,
+ * simplified and optimized.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This new implementation is fully dedicated in memory of Cameron Grant, *
+ * the creator of magnificent, highly addictive feeder infrastructure. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
*/
#include <dev/sound/pcm/sound.h>
-
#include "feeder_if.h"
SND_DECLARE_FILE("$FreeBSD$");
@@ -33,190 +44,183 @@ SND_DECLARE_FILE("$FreeBSD$");
MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder");
#define FEEDBUFSZ 8192
+#define FEEDBUF24SZ 8190
-static unsigned char ulaw_to_u8[] = {
- 3, 7, 11, 15, 19, 23, 27, 31,
- 35, 39, 43, 47, 51, 55, 59, 63,
- 66, 68, 70, 72, 74, 76, 78, 80,
- 82, 84, 86, 88, 90, 92, 94, 96,
- 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113,
- 113, 114, 114, 115, 115, 116, 116, 117,
- 117, 118, 118, 119, 119, 120, 120, 121,
- 121, 121, 122, 122, 122, 122, 123, 123,
- 123, 123, 124, 124, 124, 124, 125, 125,
- 125, 125, 125, 125, 126, 126, 126, 126,
- 126, 126, 126, 126, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 253, 249, 245, 241, 237, 233, 229, 225,
- 221, 217, 213, 209, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 174, 172, 170, 168, 166, 164, 162, 160,
- 158, 157, 156, 155, 154, 153, 152, 151,
- 150, 149, 148, 147, 146, 145, 144, 143,
- 143, 142, 142, 141, 141, 140, 140, 139,
- 139, 138, 138, 137, 137, 136, 136, 135,
- 135, 135, 134, 134, 134, 134, 133, 133,
- 133, 133, 132, 132, 132, 132, 131, 131,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 130, 130, 130, 130, 129, 129, 129, 129,
- 129, 129, 129, 129, 129, 129, 129, 129,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
-};
-
-static unsigned char u8_to_ulaw[] = {
- 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 2, 2, 2, 2, 3, 3, 3,
- 3, 4, 4, 4, 4, 5, 5, 5,
- 5, 6, 6, 6, 6, 7, 7, 7,
- 7, 8, 8, 8, 8, 9, 9, 9,
- 9, 10, 10, 10, 10, 11, 11, 11,
- 11, 12, 12, 12, 12, 13, 13, 13,
- 13, 14, 14, 14, 14, 15, 15, 15,
- 15, 16, 16, 17, 17, 18, 18, 19,
- 19, 20, 20, 21, 21, 22, 22, 23,
- 23, 24, 24, 25, 25, 26, 26, 27,
- 27, 28, 28, 29, 29, 30, 30, 31,
- 31, 32, 33, 34, 35, 36, 37, 38,
- 39, 40, 41, 42, 43, 44, 45, 46,
- 47, 49, 51, 53, 55, 57, 59, 61,
- 63, 66, 70, 74, 78, 84, 92, 104,
- 254, 231, 219, 211, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 175, 174, 173, 172, 171, 170, 169, 168,
- 167, 166, 165, 164, 163, 162, 161, 160,
- 159, 159, 158, 158, 157, 157, 156, 156,
- 155, 155, 154, 154, 153, 153, 152, 152,
- 151, 151, 150, 150, 149, 149, 148, 148,
- 147, 147, 146, 146, 145, 145, 144, 144,
- 143, 143, 143, 143, 142, 142, 142, 142,
- 141, 141, 141, 141, 140, 140, 140, 140,
- 139, 139, 139, 139, 138, 138, 138, 138,
- 137, 137, 137, 137, 136, 136, 136, 136,
- 135, 135, 135, 135, 134, 134, 134, 134,
- 133, 133, 133, 133, 132, 132, 132, 132,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 129, 129, 129, 129, 128, 128, 128, 128,
-};
-
-static unsigned char alaw_to_ulaw[] = {
- 42, 43, 40, 41, 46, 47, 44, 45,
- 34, 35, 32, 33, 38, 39, 36, 37,
- 57, 58, 55, 56, 61, 62, 59, 60,
- 49, 50, 48, 48, 53, 54, 51, 52,
- 10, 11, 8, 9, 14, 15, 12, 13,
- 2, 3, 0, 1, 6, 7, 4, 5,
- 26, 27, 24, 25, 30, 31, 28, 29,
- 18, 19, 16, 17, 22, 23, 20, 21,
- 98, 99, 96, 97, 102, 103, 100, 101,
- 93, 93, 92, 92, 95, 95, 94, 94,
- 116, 118, 112, 114, 124, 126, 120, 122,
- 106, 107, 104, 105, 110, 111, 108, 109,
- 72, 73, 70, 71, 76, 77, 74, 75,
- 64, 65, 63, 63, 68, 69, 66, 67,
- 86, 87, 84, 85, 90, 91, 88, 89,
- 79, 79, 78, 78, 82, 83, 80, 81,
- 170, 171, 168, 169, 174, 175, 172, 173,
- 162, 163, 160, 161, 166, 167, 164, 165,
- 185, 186, 183, 184, 189, 190, 187, 188,
- 177, 178, 176, 176, 181, 182, 179, 180,
- 138, 139, 136, 137, 142, 143, 140, 141,
- 130, 131, 128, 129, 134, 135, 132, 133,
- 154, 155, 152, 153, 158, 159, 156, 157,
- 146, 147, 144, 145, 150, 151, 148, 149,
- 226, 227, 224, 225, 230, 231, 228, 229,
- 221, 221, 220, 220, 223, 223, 222, 222,
- 244, 246, 240, 242, 252, 254, 248, 250,
- 234, 235, 232, 233, 238, 239, 236, 237,
- 200, 201, 198, 199, 204, 205, 202, 203,
- 192, 193, 191, 191, 196, 197, 194, 195,
- 214, 215, 212, 213, 218, 219, 216, 217,
- 207, 207, 206, 206, 210, 211, 208, 209,
-};
-
-static unsigned char ulaw_to_alaw[] = {
- 42, 43, 40, 41, 46, 47, 44, 45,
- 34, 35, 32, 33, 38, 39, 36, 37,
- 58, 59, 56, 57, 62, 63, 60, 61,
- 50, 51, 48, 49, 54, 55, 52, 53,
- 10, 11, 8, 9, 14, 15, 12, 13,
- 2, 3, 0, 1, 6, 7, 4, 5,
- 27, 24, 25, 30, 31, 28, 29, 18,
- 19, 16, 17, 22, 23, 20, 21, 106,
- 104, 105, 110, 111, 108, 109, 98, 99,
- 96, 97, 102, 103, 100, 101, 122, 120,
- 126, 127, 124, 125, 114, 115, 112, 113,
- 118, 119, 116, 117, 75, 73, 79, 77,
- 66, 67, 64, 65, 70, 71, 68, 69,
- 90, 91, 88, 89, 94, 95, 92, 93,
- 82, 82, 83, 83, 80, 80, 81, 81,
- 86, 86, 87, 87, 84, 84, 85, 85,
- 170, 171, 168, 169, 174, 175, 172, 173,
- 162, 163, 160, 161, 166, 167, 164, 165,
- 186, 187, 184, 185, 190, 191, 188, 189,
- 178, 179, 176, 177, 182, 183, 180, 181,
- 138, 139, 136, 137, 142, 143, 140, 141,
- 130, 131, 128, 129, 134, 135, 132, 133,
- 155, 152, 153, 158, 159, 156, 157, 146,
- 147, 144, 145, 150, 151, 148, 149, 234,
- 232, 233, 238, 239, 236, 237, 226, 227,
- 224, 225, 230, 231, 228, 229, 250, 248,
- 254, 255, 252, 253, 242, 243, 240, 241,
- 246, 247, 244, 245, 203, 201, 207, 205,
- 194, 195, 192, 193, 198, 199, 196, 197,
- 218, 219, 216, 217, 222, 223, 220, 221,
- 210, 210, 211, 211, 208, 208, 209, 209,
- 214, 214, 215, 215, 212, 212, 213, 213,
-};
-
-/*****************************************************************************/
+#define FMT_TRACE(x...) /* fprintf(stderr, x) */
+#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */
+#define FMT_ALIGNBYTE(x) /* x */
+
+/*
+ * Sign inverted ulaw/alaw -> 8 table
+ */
+static uint8_t ulaw_to_s8_tbl[] = {
+ 131, 135, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 183, 187, 191,
+ 194, 196, 198, 200, 202, 204, 206, 208,
+ 210, 212, 214, 216, 218, 220, 222, 224,
+ 226, 227, 228, 229, 230, 231, 232, 233,
+ 234, 235, 236, 237, 238, 239, 240, 241,
+ 241, 242, 242, 243, 243, 244, 244, 245,
+ 245, 246, 246, 247, 247, 248, 248, 249,
+ 249, 249, 250, 250, 250, 250, 251, 251,
+ 251, 251, 252, 252, 252, 252, 253, 253,
+ 253, 253, 253, 253, 254, 254, 254, 254,
+ 254, 254, 254, 254, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 125, 121, 117, 113, 109, 105, 101, 97,
+ 93, 89, 85, 81, 77, 73, 69, 65,
+ 62, 60, 58, 56, 54, 52, 50, 48,
+ 46, 44, 42, 40, 38, 36, 34, 32,
+ 30, 29, 28, 27, 26, 25, 24, 23,
+ 22, 21, 20, 19, 18, 17, 16, 15,
+ 15, 14, 14, 13, 13, 12, 12, 11,
+ 11, 10, 10, 9, 9, 8, 8, 7,
+ 7, 7, 6, 6, 6, 6, 5, 5,
+ 5, 5, 4, 4, 4, 4, 3, 3,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static uint8_t alaw_to_s8_tbl[] = {
+ 236, 237, 234, 235, 240, 241, 238, 239,
+ 228, 229, 226, 227, 232, 233, 230, 231,
+ 246, 246, 245, 245, 248, 248, 247, 247,
+ 242, 242, 241, 241, 244, 244, 243, 243,
+ 171, 175, 163, 167, 187, 191, 179, 183,
+ 139, 143, 131, 135, 155, 159, 147, 151,
+ 214, 216, 210, 212, 222, 224, 218, 220,
+ 198, 200, 194, 196, 206, 208, 202, 204,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 251, 251, 251, 251, 252, 252, 252, 252,
+ 249, 249, 249, 249, 250, 250, 250, 250,
+ 254, 254, 254, 254, 254, 254, 254, 254,
+ 253, 253, 253, 253, 253, 253, 253, 253,
+ 20, 19, 22, 21, 16, 15, 18, 17,
+ 28, 27, 30, 29, 24, 23, 26, 25,
+ 10, 10, 11, 11, 8, 8, 9, 9,
+ 14, 14, 15, 15, 12, 12, 13, 13,
+ 85, 81, 93, 89, 69, 65, 77, 73,
+ 117, 113, 125, 121, 101, 97, 109, 105,
+ 42, 40, 46, 44, 34, 32, 38, 36,
+ 58, 56, 62, 60, 50, 48, 54, 52,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 5, 5, 5, 4, 4, 4, 4,
+ 7, 7, 7, 7, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+};
static int
-feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_table_u8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i, j, k;
+ int j, k = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t *tbl = (uint8_t *)f->data;
+
+ j = k;
+ while (j > 0) {
+ j--;
+ b[j] = tbl[b[j]] ^ 0x80;
+ }
+ return k;
+}
- k = FEEDER_FEED(f->source, c, b, count / 2, source);
- j = k - 1;
- i = j * 2 + 1;
- while (i > 0 && j >= 0) {
- b[i--] = b[j--];
- b[i--] = 0;
+static int
+feed_table_s16le(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);
+ uint8_t *tbl = (uint8_t *)f->data;
+
+ i = k;
+ k <<= 1;
+ j = k;
+ while (i > 0) {
+ b[--j] = tbl[b[--i]];
+ b[--j] = 0;
}
- return k * 2;
+ return k;
}
-static struct pcm_feederdesc feeder_8to16le_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},
- {0},
+static struct pcm_feederdesc feeder_ulawtou8_desc[] = {
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_8to16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_8to16le),
- { 0, 0 }
+static kobj_method_t feeder_ulawtou8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_u8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_s8_tbl);
-/*****************************************************************************/
+static struct pcm_feederdesc feeder_ulawtos16le_desc[] = {
+ {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_ulawtos16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_s16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_ulawtos16le, 0, ulaw_to_s8_tbl);
+
+static struct pcm_feederdesc feeder_alawtos16le_desc[] = {
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_alawtos16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_s16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_alawtos16le, 0, alaw_to_s8_tbl);
+
+static struct pcm_feederdesc feeder_alawtou8_desc[] = {
+ {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0},
+ {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_alawtou8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_table_u8),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_alawtou8, 0, alaw_to_s8_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_16to8_init(struct pcm_feeder *f)
+feed_common_init(struct pcm_feeder *f)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
+ f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT|M_ZERO);
if (f->data == NULL)
return ENOMEM;
return 0;
}
static int
-feed_16to8_free(struct pcm_feeder *f)
+feed_common_free(struct pcm_feeder *f)
{
if (f->data)
free(f->data, M_FMTFEEDER);
@@ -224,326 +228,498 @@ feed_16to8_free(struct pcm_feeder *f)
return 0;
}
+/*
+ * Bit conversion
+ */
static int
-feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 1, k;
-
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- j += 2;
+ int i, j, 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;
}
- return i;
+ return k;
}
+static struct pcm_feederdesc feeder_8to16le_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},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_8to16le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_8to16le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_8to16le, 0, NULL);
+static int
+feed_16leto8(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;
+
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), 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);
+ i = k;
+ j = k >> 1;
+ while (i > 0) {
+ b[--j] = src[--i];
+ i--;
+ }
+ return k >> 1;
+}
static struct pcm_feederdesc feeder_16leto8_desc[] = {
{FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 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},
- {0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_16leto8_methods[] = {
- KOBJMETHOD(feeder_init, feed_16to8_init),
- KOBJMETHOD(feeder_free, feed_16to8_free),
- KOBJMETHOD(feeder_feed, feed_16leto8),
- { 0, 0 }
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_16leto8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_16leto8, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_16leto8, 0, NULL);
static int
-feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_24leto16le(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 / 2, source);
-
- j = k - 1;
- i = j * 2 + 1;
- while (i > 0 && j >= 0) {
- b[i--] = b[j];
- b[i--] = b[j];
- j--;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
+
+ k = (count * 3) >> 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), 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);
+ i = (k / 3) << 1;
+ j = i;
+ while (i > 0) {
+ b[--i] = src[--k];
+ b[--i] = src[--k];
+ k--;
}
- return k * 2;
+ return j;
}
-
-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},
- {0},
+static struct pcm_feederdesc feeder_24leto16le_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},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_monotostereo8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_monotostereo8),
- { 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),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_monotostereo8, 0, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_24leto16le, 1, NULL);
static int
-feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_32leto16le(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 / 2, source);
- u_int8_t x, y;
-
- j = k - 1;
- i = j * 2 + 1;
- while (i >= 3 && j >= 1) {
- x = b[j--];
- y = b[j--];
- b[i--] = x;
- b[i--] = y;
- b[i--] = x;
- b[i--] = y;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
+
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
+ }
+ FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~3);
+ i = k;
+ k >>= 1;
+ j = k;
+ while (i > 0) {
+ b[--j] = src[--i];
+ b[--j] = src[--i];
+ i -= 2;
}
- return k * 2;
+ return k;
}
-
-static struct pcm_feederdesc feeder_monotostereo16_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_32leto16le_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},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_monotostereo16_methods[] = {
- KOBJMETHOD(feeder_feed, feed_monotostereo16),
- { 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),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_monotostereo16, 0, NULL);
-
-/*****************************************************************************/
-
-static int
-feed_stereotomono8_init(struct pcm_feeder *f)
-{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
- if (f->data == NULL)
- return ENOMEM;
- return 0;
-}
-
-static int
-feed_stereotomono8_free(struct pcm_feeder *f)
-{
- if (f->data)
- free(f->data, M_FMTFEEDER);
- f->data = NULL;
- return 0;
-}
+FEEDER_DECLARE(feeder_32leto16le, 1, NULL);
+/*
+ * Bit conversion end
+ */
+/*
+ * Channel conversion (mono -> stereo)
+ */
static int
-feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 0, k;
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- j += 2;
+ i = k;
+ j = k << 1;
+ while (i > 0) {
+ b[--j] = b[--i];
+ b[--j] = b[i];
}
- return i;
+ return k << 1;
}
-
-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},
- {0},
+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},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_stereotomono8_methods[] = {
- KOBJMETHOD(feeder_init, feed_stereotomono8_init),
- KOBJMETHOD(feeder_free, feed_stereotomono8_free),
- KOBJMETHOD(feeder_feed, feed_stereotomono8),
- { 0, 0 }
+static kobj_method_t feeder_monotostereo8_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_stereotomono8, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_monotostereo8, 0, NULL);
static int
-feed_stereotomono16_init(struct pcm_feeder *f)
+feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO);
- if (f->data == NULL)
- return ENOMEM;
- return 0;
-}
+ int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source);
+ uint8_t l, m;
-static int
-feed_stereotomono16_free(struct pcm_feeder *f)
-{
- if (f->data)
- free(f->data, M_FMTFEEDER);
- f->data = NULL;
- return 0;
+ 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);
+ i = k;
+ j = k << 1;
+ while (i > 0) {
+ l = b[--i];
+ m = b[--i];
+ b[--j] = l;
+ b[--j] = m;
+ b[--j] = l;
+ b[--j] = m;
+ }
+ return k << 1;
}
+static struct pcm_feederdesc feeder_monotostereo16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_monotostereo16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_monotostereo16),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_monotostereo16, 0, NULL);
+/*
+ * Channel conversion (mono -> stereo) end
+ */
+/*
+ * Channel conversion (stereo -> mono)
+ */
static int
-feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int32_t i = 0, toget = count * 2;
- int j = 0, k;
-
- k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source);
- while (j < k) {
- b[i++] = ((u_int8_t *)f->data)[j];
- b[i++] = ((u_int8_t *)f->data)[j + 1];
- j += 4;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
+
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 2) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
}
- return i;
+ FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~1);
+ i = k >> 1;
+ j = i;
+ while (i > 0) {
+ k--;
+ b[--i] = src[--k];
+ }
+ return j;
}
-
-static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0},
- {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0},
- {0},
+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},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_stereotomono16_methods[] = {
- KOBJMETHOD(feeder_init, feed_stereotomono16_init),
- KOBJMETHOD(feeder_free, feed_stereotomono16_free),
- KOBJMETHOD(feeder_feed, feed_stereotomono16),
- { 0, 0 }
+static kobj_method_t feeder_stereotomono8_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_stereotomono8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_stereotomono16, 1, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_stereotomono8, 0, NULL);
static int
-feed_endian(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- u_int8_t t;
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
-
- while (i < j) {
- t = b[i];
- b[i] = b[i + 1];
- b[i + 1] = t;
- i += 2;
+ int i, j, k;
+ uint8_t *src = (uint8_t *)f->data;
+
+ k = count << 1;
+ k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source);
+ if (k < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, k);
+ return 0;
}
- return i;
+ FMT_TEST(k & 3, "%s: Bytes not 16bit (stereo) aligned.\n", __func__);
+ FMT_ALIGNBYTE(k &= ~3);
+ i = k >> 1;
+ j = i;
+ while (i > 0) {
+ k -= 2;
+ b[--i] = src[--k];
+ b[--i] = src[--k];
+ }
+ return j;
}
-
-static struct pcm_feederdesc feeder_endian_desc[] = {
- {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0},
- {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0},
- {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0},
- {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
- {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0},
- {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_stereotomono16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S16_BE, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_endian_methods[] = {
- KOBJMETHOD(feeder_feed, feed_endian),
- { 0, 0 }
+static kobj_method_t feeder_stereotomono16_methods[] = {
+ KOBJMETHOD(feeder_init, feed_common_init),
+ KOBJMETHOD(feeder_free, feed_common_free),
+ KOBJMETHOD(feeder_feed, feed_stereotomono16),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_endian, 0, NULL);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_stereotomono16, 0, NULL);
+/*
+ * Channel conversion (stereo -> mono) end
+ */
+/*
+ * Sign conversion
+ */
static int
-feed_sign(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_sign8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
- intptr_t ssz = (intptr_t)f->data, ofs = ssz - 1;
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
- while (i < j) {
- b[i + ofs] ^= 0x80;
- i += ssz;
- }
- return i;
+ i = j;
+ while (i > 0)
+ b[--i] ^= 0x80;
+ return j;
}
-
static struct pcm_feederdesc feeder_sign8_desc[] = {
{FEEDER_FMT, AFMT_U8, AFMT_S8, 0},
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0},
{FEEDER_FMT, AFMT_S8, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
- {0},
+ {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_sign8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign),
- { 0, 0 }
+ KOBJMETHOD(feeder_feed, feed_sign8),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_sign8, 0, (void *)1);
+FEEDER_DECLARE(feeder_sign8, 0, NULL);
+static int
+feed_sign16le(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);
+
+ 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);
+ i = j;
+ while (i > 0) {
+ b[--i] ^= 0x80;
+ i--;
+ }
+ return j;
+}
static struct pcm_feederdesc feeder_sign16le_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_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},
- {0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
static kobj_method_t feeder_sign16le_methods[] = {
- KOBJMETHOD(feeder_feed, feed_sign),
- { 0, 0 }
+ KOBJMETHOD(feeder_feed, feed_sign16le),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_sign16le, -1, (void *)2);
-
-/*****************************************************************************/
+FEEDER_DECLARE(feeder_sign16le, 0, NULL);
+/*
+ * Sign conversion end.
+ */
+/*
+ * Endian conversion.
+ */
static int
-feed_table(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
+feed_endian16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
- int i = 0, j = FEEDER_FEED(f->source, c, b, count, source);
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+ uint8_t v;
- while (i < j) {
- b[i] = ((u_int8_t *)f->data)[b[i]];
- i++;
+ 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);
+ i = j;
+ while (i > 0) {
+ v = b[--i];
+ b[i] = b[i - 1];
+ b[--i] = v;
}
- return i;
+ return j;
}
-
-static struct pcm_feederdesc feeder_ulawtou8_desc[] = {
- {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0},
- {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
- {0},
+static struct pcm_feederdesc feeder_endian16_desc[] = {
+ {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0},
+ {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0},
+ {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0},
+ {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0},
+ {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_ulawtou8_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
+static kobj_method_t feeder_endian16_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian16),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_u8);
+FEEDER_DECLARE(feeder_endian16, 0, NULL);
-static struct pcm_feederdesc feeder_u8toulaw_desc[] = {
- {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0},
- {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0},
- {0},
-};
-static kobj_method_t feeder_u8toulaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
-};
-FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw);
+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);
+ uint8_t v;
-static struct pcm_feederdesc feeder_alawtoulaw_desc[] = {
- {FEEDER_FMT, AFMT_A_LAW, AFMT_MU_LAW, 0},
- {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0},
- {0},
+ 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);
+ i = j;
+ while (i > 0) {
+ v = b[--i];
+ b[i] = b[i - 2];
+ b[i -= 2] = v;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_endian24_desc[] = {
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_BE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_BE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_alawtoulaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
+static kobj_method_t feeder_endian24_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian24),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_alawtoulaw, 0, alaw_to_ulaw);
+FEEDER_DECLARE(feeder_endian24, 0, NULL);
-static struct pcm_feederdesc feeder_ulawtoalaw_desc[] = {
- {FEEDER_FMT, AFMT_MU_LAW, AFMT_A_LAW, 0},
- {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0},
- {0},
+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);
+ uint8_t l, m;
+
+ 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);
+ i = j;
+ while (i > 0) {
+ l = b[--i];
+ m = b[--i];
+ b[i] = b[i - 1];
+ b[i + 1] = b[i - 2];
+ b[--i] = m;
+ b[--i] = l;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_endian32_desc[] = {
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_BE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_BE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
};
-static kobj_method_t feeder_ulawtoalaw_methods[] = {
- KOBJMETHOD(feeder_feed, feed_table),
- { 0, 0 }
+static kobj_method_t feeder_endian32_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_endian32),
+ {0, 0}
};
-FEEDER_DECLARE(feeder_ulawtoalaw, 0, ulaw_to_alaw);
-
-
-
+FEEDER_DECLARE(feeder_endian32, 0, NULL);
+/*
+ * Endian conversion end
+ */
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index 55d51fc..34069ea 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -1,5 +1,7 @@
/*-
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright (c) 2003 Orion Hodson <orion@freebsd.org>
+ * Copyright (c) 2005 Ariff Abdullah <skywizard@MyBSD.org.my>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -23,7 +25,25 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * MAINTAINER: Orion Hodson <orion@freebsd.org>
+ * 2005-06-11:
+ * ==========
+ *
+ * *New* and rewritten soft sample rate converter supporting arbitary sample
+ * rate, fine grained scalling/coefficients and unified up/down stereo
+ * converter. Most of disclaimers from orion's previous version also applied
+ * here, regarding with linear interpolation deficiencies, pre/post
+ * anti-aliasing filtering issues. This version comes with much simpler and
+ * tighter interface, although it works almost exactly like the older one.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * *
+ * This new implementation is fully dedicated in memory of Cameron Grant, *
+ * the creator of magnificent, highly addictive feeder infrastructure. *
+ * *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Orion's notes:
+ * =============
*
* This rate conversion code uses linear interpolation without any
* pre- or post- interpolation filtering to combat aliasing. This
@@ -37,194 +57,423 @@
* with several portions based on whether _KERNEL is defined. It's a
* little ugly, but exceedingly useful. The testsuite and its
* revisions can be found at:
- * http://people.freebsd.org/~orion/feedrate/
+ * http://people.freebsd.org/~orion/files/feedrate/
*
* Special thanks to Ken Marx for exposing flaws in the code and for
* testing revisions.
*/
-#ifdef _KERNEL
-
#include <dev/sound/pcm/sound.h>
#include "feeder_if.h"
SND_DECLARE_FILE("$FreeBSD$");
-#endif /* _KERNEL */
+#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
+#define RATE_TRACE(x...) /* printf(x) */
MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
-#ifndef RATE_ASSERT
-#define RATE_ASSERT(x, y) /* KASSERT(x) */
-#endif /* RATE_ASSERT */
+#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
-#ifndef RATE_TRACE
-#define RATE_TRACE(x...) /* printf(x) */
-#endif
+struct feed_rate_info {
+ uint32_t src, dst; /* rounded source / destination rates */
+ uint32_t rsrc, rdst; /* original source / destination rates */
+ 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 */
+ int32_t scale, roll; /* scale / roll factor */
+ int16_t *buffer;
+ uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t);
+};
-/*****************************************************************************/
+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);
-/* The following coefficients are coupled. They are chosen to be
- * guarantee calculable factors for the interpolation routine. They
- * have been tested over the range of RATEMIN-RATEMAX Hz. Decreasing
- * the granularity increases the required buffer size and affects the
- * gain values at different points in the space. These values were
- * found by running the test program with -p (probe) and some trial
- * and error.
- *
- * ROUNDHZ the granularity of sample rates (fits n*11025 and n*8000).
- * FEEDBUFSZ the amount of buffer space.
- * MINGAIN the minimum acceptable gain in coefficients search.
+int feeder_rate_ratemin = RATEMIN;
+int feeder_rate_ratemax = RATEMAX;
+/*
+ * See 'Feeder Scaling Type' below..
*/
-#define ROUNDHZ 25
-#define FEEDBUFSZ 8192
-#define MINGAIN 92
-
-#define RATEMIN 4000
-#define RATEMAX 48000
+static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL;
+static int feeder_rate_buffersize = FEEDBUFSZ & ~1;
-struct feed_rate_info;
+/*
+ * 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);
-typedef int (*rate_convert_method)(struct feed_rate_info *,
- uint32_t, uint32_t, int16_t *);
+static int
+sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = feeder_rate_ratemin;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ if (val < 1 || val >= feeder_rate_ratemax)
+ err = EINVAL;
+ 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
-convert_stereo_up(struct feed_rate_info *info,
- uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst);
+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", "");
static int
-convert_stereo_down(struct feed_rate_info *info,
- uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst);
+sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
-struct feed_rate_info {
- uint32_t src, dst; /* source and destination rates */
- uint16_t buffer_ticks; /* number of available samples in buffer */
- uint16_t buffer_pos; /* next available sample in buffer */
- uint16_t rounds; /* maximum number of cycle rounds w buffer */
- uint16_t alpha; /* interpolation distance */
- uint16_t sscale; /* src clock scale */
- uint16_t dscale; /* dst clock scale */
- uint16_t mscale; /* scale factor to avoid divide per sample */
- uint16_t mroll; /* roll to again avoid divide per sample */
- uint16_t channels; /* 1 = mono, 2 = stereo */
-
- rate_convert_method convert;
- int16_t buffer[FEEDBUFSZ];
-};
+ val = feeder_rate_scaling;
+ 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;
+ else
+ feeder_rate_scaling = val;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", "");
-#define bytes_per_sample 2
-#define src_ticks_per_cycle(info) (info->dscale * info->rounds)
-#define dst_ticks_per_cycle(info) (info->sscale * info->rounds)
-#define bytes_per_tick(info) (info->channels * bytes_per_sample)
-#define src_bytes_per_cycle(info) \
- (src_ticks_per_cycle(info) * bytes_per_tick(info))
-#define dst_bytes_per_cycle(info) \
- (dst_ticks_per_cycle(info) * bytes_per_tick(info))
+static int
+sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
-static uint32_t
-gcd(uint32_t x, uint32_t y)
+ val = feeder_rate_buffersize;
+ err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+ /*
+ * Don't waste too much kernel space
+ */
+ if (val < 2 || val > 65536)
+ err = EINVAL;
+ else
+ feeder_rate_buffersize = val & ~1;
+ return err;
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", "");
+
+static void
+feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy)
{
- uint32_t w;
+ uint32_t w, src = x, dst = y;
+
while (y != 0) {
w = x % y;
x = y;
y = w;
}
- return x;
+ *gx = src / x;
+ *gy = dst / x;
+}
+
+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;
}
static int
feed_rate_setup(struct pcm_feeder *f)
{
struct feed_rate_info *info = f->data;
- uint32_t mscale, mroll, l, r, g;
-
- /* Beat sample rates down by greatest common divisor */
- g = gcd(info->src, info->dst);
- info->sscale = info->dst / g;
- info->dscale = info->src / g;
+ int r = 0;
+ info->pos = 2;
+ info->bpos = 4;
info->alpha = 0;
- info->buffer_ticks = 0;
- info->buffer_pos = 0;
-
- /* Pick suitable conversion routine */
- if (info->src > info->dst) {
- info->convert = convert_stereo_down;
+ 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 {
- info->convert = convert_stereo_up;
- }
-
- /*
- * Determine number of conversion rounds that will fit into
- * buffer. NB Must set info->rounds to one before using
- * src_ticks_per_cycle here since it used by src_ticks_per_cycle.
- */
- info->rounds = 1;
- r = (FEEDBUFSZ - bytes_per_tick(info)) /
- (src_ticks_per_cycle(info) * bytes_per_tick(info));
- if (r == 0) {
- RATE_TRACE("Insufficient buffer space for conversion %d -> %d "
- "(%d < %d)\n", info->src, info->dst, FEEDBUFSZ,
- src_ticks_per_cycle(info) * bytes_per_tick(info));
- return -1;
+ 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,
+ &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)
+ 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;
}
- info->rounds = r;
-
- /*
- * Find scale and roll combination that allows us to trade
- * costly divide operations in the main loop for multiply-rolls.
- */
- for (l = 96; l >= MINGAIN; l -= 3) {
- for (mroll = 0; mroll < 16; mroll ++) {
- mscale = (1 << mroll) / info->sscale;
-
- r = (mscale * info->sscale * 100) >> mroll;
- if (r > l && r <= 100) {
- info->mscale = mscale;
- info->mroll = mroll;
- RATE_TRACE("Converting %d to %d with "
- "mscale = %d and mroll = %d "
- "(gain = %d / 100)\n",
- info->src, info->dst,
- info->mscale, info->mroll, r);
- return 0;
- }
- }
- }
-
- RATE_TRACE("Failed to find a converter within %d%% gain for "
- "%d to %d.\n", l, info->src, info->dst);
-
- return -2;
+ return 0;
}
static int
feed_rate_set(struct pcm_feeder *f, int what, int value)
{
struct feed_rate_info *info = f->data;
- int rvalue;
-
- if (value < RATEMIN || value > RATEMAX) {
+
+ if (value < feeder_rate_ratemin || value > feeder_rate_ratemax)
return -1;
- }
- rvalue = (value / ROUNDHZ) * ROUNDHZ;
- if (value - rvalue > ROUNDHZ / 2) {
- rvalue += ROUNDHZ;
- }
-
- switch(what) {
- case FEEDRATE_SRC:
- info->src = rvalue;
- break;
- case FEEDRATE_DST:
- info->dst = rvalue;
- break;
- default:
- return -1;
+ switch (what) {
+ case FEEDRATE_SRC:
+ info->rsrc = value;
+ break;
+ case FEEDRATE_DST:
+ info->rdst = value;
+ break;
+ default:
+ return -1;
}
-
return feed_rate_setup(f);
}
@@ -233,13 +482,16 @@ feed_rate_get(struct pcm_feeder *f, int what)
{
struct feed_rate_info *info = f->data;
- switch(what) {
- case FEEDRATE_SRC:
- return info->src;
- case FEEDRATE_DST:
- return info->dst;
- default:
- return -1;
+ /*
+ * Return *real* src/dst rate.
+ */
+ switch (what) {
+ case FEEDRATE_SRC:
+ return info->rsrc;
+ case FEEDRATE_DST:
+ return info->rdst;
+ default:
+ return -1;
}
return -1;
}
@@ -252,12 +504,20 @@ feed_rate_init(struct pcm_feeder *f)
info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info == NULL)
return ENOMEM;
- info->src = DSP_DEFAULT_SPEED;
- info->dst = DSP_DEFAULT_SPEED;
- info->channels = 2;
-
+ /*
+ * bufsz = sample from last cycle + conversion space
+ */
+ info->bufsz = 2 + feeder_rate_buffersize;
+ info->buffer = malloc(sizeof(*info->buffer) * info->bufsz,
+ M_RATEFEEDER, M_NOWAIT | M_ZERO);
+ if (info->buffer == NULL) {
+ free(info, M_RATEFEEDER);
+ return ENOMEM;
+ }
+ info->rsrc = DSP_DEFAULT_SPEED;
+ info->rdst = DSP_DEFAULT_SPEED;
f->data = info;
- return 0;
+ return feed_rate_setup(f);
}
static int
@@ -266,211 +526,269 @@ feed_rate_free(struct pcm_feeder *f)
struct feed_rate_info *info = f->data;
if (info) {
+ if (info->buffer)
+ free(info->buffer, M_RATEFEEDER);
free(info, M_RATEFEEDER);
}
f->data = NULL;
return 0;
}
-static int
-convert_stereo_up(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+static uint32_t
+feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max)
{
- uint32_t max_dst_ticks;
- int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o;
+ int64_t x, alpha, distance;
+ uint32_t ret;
+ int32_t pos, bpos, gx, gy;
int16_t *src;
-
- sp = info->buffer_pos * 2;
- se = sp + src_ticks * 2;
-
- src = info->buffer;
- alpha = info->alpha * info->mscale;
- dalpha = info->dscale * info->mscale; /* Alpha increment */
- malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */
- mroll = info->mroll;
-
/*
- * For efficiency the main conversion loop should only depend on
- * one variable. We use the state to work out the maximum number
- * of output samples that are available and eliminate the checking of
- * sp from the loop.
+ * Plain, straight forward 64bit arith. No bit-magic applied here.
*/
- max_dst_ticks = src_ticks * info->dst / info->src - alpha / dalpha;
- if (max_dst_ticks < dst_ticks) {
- dst_ticks = max_dst_ticks;
+ 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;
+}
- dp = 0;
- de = dst_ticks * 2;
+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;
/*
- * Unrolling this loop manually does not help much here because
- * of the alpha, malpha comparison.
+ * 64bit scaling.
*/
- while (dp < de) {
- o = malpha - alpha;
- x = alpha * src[sp + 2] + o * src[sp];
- dst[dp++] = x >> mroll;
- x = alpha * src[sp + 3] + o * src[sp + 1];
- dst[dp++] = x >> mroll;
- alpha += dalpha;
- if (alpha >= malpha) {
- alpha -= malpha;
- sp += 2;
+ 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;
}
}
- RATE_ASSERT(sp <= se, ("%s: Source overrun\n", __func__));
-
- info->buffer_pos = sp / info->channels;
- info->alpha = alpha / info->mscale;
-
- return dp / info->channels;
+ info->alpha = alpha / info->scale;
+ info->pos = pos;
+ return ret;
}
-static int
-convert_stereo_down(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+static uint32_t
+feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max)
{
- int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o, m,
- mdalpha, mstep;
+ uint32_t ret;
+ int32_t x, pos, bpos, gx, gy, alpha, roll, distance;
int16_t *src;
-
- sp = info->buffer_pos * 2;
- se = sp + src_ticks * 2;
-
+ /*
+ * 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;
- alpha = info->alpha * info->mscale;
- dalpha = info->dscale * info->mscale; /* Alpha increment */
- malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */
- mroll = info->mroll;
-
- dp = 0;
- de = dst_ticks * 2;
-
- m = dalpha / malpha;
- mstep = m * 2;
- mdalpha = dalpha - m * malpha;
+ 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;
/*
- * TODO: eliminate sp or dp from this loop comparison for a few
- * extra % performance.
+ * Plain copy.
*/
- while (sp < se && dp < de) {
- o = malpha - alpha;
- x = alpha * src[sp + 2] + o * src[sp];
- dst[dp++] = x >> mroll;
- x = alpha * src[sp + 3] + o * src[sp + 1];
- dst[dp++] = x >> mroll;
-
- alpha += mdalpha;
- sp += mstep;
- if (alpha >= malpha) {
- alpha -= malpha;
- sp += 2;
+ 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->buffer_pos = sp / 2;
- info->alpha = alpha / info->mscale;
-
- RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
- ("%s: Source overrun\n", __func__));
-
- return dp / 2;
+ info->pos = pos;
+ info->alpha = alpha;
+ return ret;
}
-static int
-feed_rate(struct pcm_feeder *f,
- struct pcm_channel *c,
- uint8_t *b,
- uint32_t count,
- void *source)
+static int32_t
+feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
{
struct feed_rate_info *info = f->data;
-
- uint32_t done, s_ticks, d_ticks;
- done = 0;
-
- RATE_ASSERT(info->channels == 2,
- ("%s: channels (%d) != 2", __func__, info->channels));
-
- while (done < count) {
- /* Slurp in more data if input buffer is not full */
- while (info->buffer_ticks < src_ticks_per_cycle(info)) {
- uint8_t *u8b;
- int fetch;
- fetch = src_bytes_per_cycle(info) -
- info->buffer_ticks * bytes_per_tick(info);
- u8b = (uint8_t*)info->buffer +
- (info->buffer_ticks + 1) *
- bytes_per_tick(info);
- fetch = FEEDER_FEED(f->source, c, u8b, fetch, source);
- RATE_ASSERT(fetch % bytes_per_tick(info) == 0,
- ("%s: fetched unaligned bytes (%d)",
- __func__, fetch));
- info->buffer_ticks += fetch / bytes_per_tick(info);
- RATE_ASSERT(src_ticks_per_cycle(info) >=
- info->buffer_ticks,
- ("%s: buffer overfilled (%d > %d).",
- __func__, info->buffer_ticks,
- src_ticks_per_cycle(info)));
- if (fetch == 0)
+ uint32_t i;
+ int32_t fetch, slot;
+ int16_t *dst = (int16_t *)b;
+ /*
+ * This loop has been optimized to generalize both up / down
+ * sampling without causing missing samples or excessive buffer
+ * feeding.
+ */
+ RATE_ASSERT(count >= 4 && count % 4 == 0,
+ ("%s: Count size not byte integral\n", __func__));
+ count >>= 1;
+ slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1;
+ /*
+ * Optimize buffer feeding aggresively to ensure calculated slot
+ * can be fitted nicely into available buffer free space, hence
+ * avoiding multiple feeding.
+ */
+ if (info->pos != 2 && info->bpos - info->pos == 2 &&
+ 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;
+ }
+ RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n",
+ __func__, slot));
+ i = 0;
+ for (;;) {
+ for (;;) {
+ fetch = info->bufsz - info->bpos;
+ RATE_ASSERT(fetch >= 0,
+ ("%s: Buffer overrun: %d > %d\n",
+ __func__, info->bpos, info->bufsz));
+ if (slot < fetch)
+ fetch = slot;
+ if (fetch > 0) {
+ RATE_ASSERT(fetch % 2 == 0,
+ ("%s: Fetch size not sample integral\n",
+ __func__));
+ fetch = FEEDER_FEED(f->source, c,
+ (uint8_t *)(info->buffer + info->bpos),
+ fetch << 1, source);
+ if (fetch == 0)
+ break;
+ RATE_ASSERT(fetch % 4 == 0,
+ ("%s: Fetch size not byte integral\n",
+ __func__));
+ fetch >>= 1;
+ info->bpos += fetch;
+ slot -= fetch;
+ RATE_ASSERT(slot >= 0,
+ ("%s: Negative Slot: %d\n", __func__
+ slot));
+ if (slot == 0)
+ break;
+ if (info->bpos == info->bufsz)
+ break;
+ } else
break;
}
-
- /* Find amount of input buffer data that should be processed */
- d_ticks = (count - done) / bytes_per_tick(info);
- s_ticks = info->buffer_ticks - info->buffer_pos;
- if (info->buffer_ticks != src_ticks_per_cycle(info)) {
- if (s_ticks > 8)
- s_ticks -= 8;
- else
- s_ticks = 0;
- }
-
- d_ticks = info->convert(info, s_ticks, d_ticks,
- (int16_t*)(b + done));
- if (d_ticks == 0)
+ if (info->pos == info->bpos) {
+ RATE_ASSERT(info->pos == 2,
+ ("%s: EOF while in progress\n", __func__));
break;
- done += d_ticks * bytes_per_tick(info);
-
- RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
- ("%s: buffer_ticks too big\n", __func__));
- RATE_ASSERT(info->buffer_ticks <= src_ticks_per_cycle(info),
- ("too many ticks %d / %d\n",
- info->buffer_ticks, src_ticks_per_cycle(info)));
- RATE_TRACE("%s: ticks %5d / %d pos %d\n", __func__,
- info->buffer_ticks, src_ticks_per_cycle(info),
- info->buffer_pos);
-
- if (src_ticks_per_cycle(info) <= info->buffer_pos) {
- /* End of cycle reached, copy last samples to start */
- uint8_t *u8b;
- u8b = (uint8_t*)info->buffer;
- bcopy(u8b + src_bytes_per_cycle(info), u8b,
- bytes_per_tick(info));
-
- RATE_ASSERT(info->alpha == 0,
- ("%s: completed cycle with "
- "alpha non-zero", __func__, info->alpha));
-
- info->buffer_pos = 0;
- info->buffer_ticks = 0;
}
+ RATE_ASSERT(info->pos <= info->bpos,
+ ("%s: Buffer overrun: %d > %d\n", __func__,
+ info->pos, info->bpos));
+ RATE_ASSERT(info->pos < info->bpos,
+ ("%s: Zero buffer!\n", __func__));
+ RATE_ASSERT((info->bpos - info->pos) % 2 == 0,
+ ("%s: Buffer not sample integral\n", __func__));
+ i += info->convert(info, dst + i, count - i);
+ RATE_ASSERT(info->pos <= info->bpos,
+ ("%s: Buffer overrun: %d > %d\n",
+ __func__, info->pos, info->bpos));
+ if (info->pos == info->bpos) {
+ /*
+ * End of buffer cycle. Copy last unit sample
+ * to beginning of buffer so next cycle can
+ * interpolate using it.
+ */
+ info->buffer[0] = info->buffer[info->pos - 2];
+ info->buffer[1] = info->buffer[info->pos - 1];
+ info->bpos = 2;
+ info->pos = 2;
+ }
+ if (i == count)
+ break;
}
-
- RATE_ASSERT(count >= done,
- ("%s: generated too many bytes of data (%d > %d).",
- __func__, done, count));
-
- if (done != count) {
- RATE_TRACE("Only did %d of %d\n", done, count);
- }
-
- return done;
+ return i << 1;
}
static struct pcm_feederdesc feeder_rate_desc[] = {
@@ -486,4 +804,3 @@ static kobj_method_t feeder_rate_methods[] = {
{0, 0}
};
FEEDER_DECLARE(feeder_rate, 2, NULL);
-
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 56b76f0..facfb1d 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -144,10 +144,13 @@ nomenclature:
/* 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)
#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
#define AFMT_8BIT (AFMT_U8 | AFMT_S8)
-#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
-#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
+#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \
+ AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
+#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \
+ AFMT_S16_BE | AFMT_U16_BE)
struct pcm_channel *fkchan_setup(device_t dev);
int fkchan_kill(struct pcm_channel *c);
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index 91a114e..dfc0705 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -30,6 +30,14 @@
SND_DECLARE_FILE("$FreeBSD$");
+/*
+ * Default speed
+ */
+#define VCHAN_DEFAULT_SPEED 48000
+
+extern int feeder_rate_ratemin;
+extern int feeder_rate_ratemax;
+
struct vchinfo {
u_int32_t spd, fmt, blksz, bps, run;
struct pcm_channel *channel, *parent;
@@ -154,8 +162,12 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format)
ch->fmt = format;
ch->bps = 1;
ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
- ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
- ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0;
+ if (ch->fmt & AFMT_16BIT)
+ ch->bps <<= 1;
+ else if (ch->fmt & AFMT_24BIT)
+ ch->bps *= 3;
+ else if (ch->fmt & AFMT_32BIT)
+ ch->bps <<= 2;
CHN_UNLOCK(channel);
chn_notify(parent, CHN_N_FORMAT);
CHN_LOCK(channel);
@@ -243,6 +255,113 @@ static kobj_method_t vchan_methods[] = {
};
CHANNEL_DECLARE(vchan);
+/*
+ * On the fly vchan rate settings
+ */
+#ifdef SND_DYNSYSCTL
+static int
+sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
+{
+ struct snddev_info *d;
+ struct snddev_channel *sce;
+ struct pcm_channel *c, *fake;
+ struct pcmchan_caps *caps;
+ int err = 0;
+ int errcnt = 0;
+ int found = 0;
+ int newspd = 0;
+ int success = 0;
+
+ d = oidp->oid_arg1;
+ if (pcm_inprog(d, 1) != 1) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
+ }
+ if (d->vchancount < 1) {
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY) {
+ if (c->flags & CHN_F_VIRTUAL) {
+ if (req->newptr != NULL &&
+ (c->flags & CHN_F_BUSY)) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EBUSY;
+ }
+ } else if (!SLIST_EMPTY(&c->children)) {
+ if (found++ == 0)
+ newspd = c->speed;
+ }
+ }
+ CHN_UNLOCK(c);
+ }
+ if (found < 1) {
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
+ if (err == 0 && req->newptr != NULL) {
+ if (newspd < 1) {
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ SLIST_FOREACH(sce, &d->channels, link) {
+ c = sce->channel;
+ CHN_LOCK(c);
+ if (c->direction == PCMDIR_PLAY) {
+ if (c->flags & CHN_F_VIRTUAL) {
+ if (c->flags & CHN_F_BUSY) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EBUSY;
+ }
+ } else if (!SLIST_EMPTY(&c->children)) {
+ caps = chn_getcaps(c);
+ if (caps != NULL) {
+ if (newspd < caps->minspeed ||
+ newspd > caps->maxspeed ||
+ newspd < feeder_rate_ratemin ||
+ newspd > feeder_rate_ratemax) {
+ errcnt++;
+ } else {
+ if (newspd != c->speed) {
+ err = chn_setspeed(c, newspd);
+ if (err != 0)
+ errcnt++;
+ else
+ success++;
+ } else
+ success++;
+ }
+ } else
+ errcnt++;
+ }
+ }
+ CHN_UNLOCK(c);
+ }
+ /*
+ * Save new value to fake channel.
+ */
+ if (success > 0) {
+ fake = pcm_getfakechan(d);
+ if (fake != NULL) {
+ CHN_LOCK(fake);
+ fake->speed = newspd;
+ CHN_UNLOCK(fake);
+ }
+ }
+ }
+ pcm_inprog(d, -1);
+ if (errcnt > 0)
+ err = EINVAL;
+ return err;
+}
+#endif
+
/* virtual channel interface */
int
@@ -292,12 +411,74 @@ vchan_create(struct pcm_channel *parent)
CHN_LOCK(parent);
/* XXX gross ugly hack, murder death kill */
if (first && !err) {
+ struct pcm_channel *fake;
+ struct pcmchan_caps *parent_caps;
+ int speed = 0;
+
err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
if (err)
printf("chn_reset: %d\n", err);
- err = chn_setspeed(parent, 44100);
+
+ fake = pcm_getfakechan(d);
+ if (fake != NULL) {
+ /*
+ * Trying to avoid querying kernel hint, previous
+ * value already saved here.
+ */
+ CHN_LOCK(fake);
+ speed = fake->speed;
+ CHN_UNLOCK(fake);
+ }
+ /*
+ * This is very sad. Few soundcards advertised as being
+ * able to do (insanely) higher/lower speed, but in
+ * reality, they simply can't. At least, we give user chance
+ * to set sane value via kernel hints file or sysctl.
+ */
+ if (speed < 1 && resource_int_value(device_get_name(parent->dev),
+ device_get_unit(parent->dev),
+ "vchanrate", &speed) != 0) {
+ speed = VCHAN_DEFAULT_SPEED;
+ }
+
+ parent_caps = chn_getcaps(parent);
+
+ if (parent_caps != NULL) {
+ /*
+ * 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
+ * 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;
+
+ err = chn_setspeed(parent, speed);
if (err)
printf("chn_setspeed: %d\n", err);
+ else {
+ if (fake != NULL) {
+ /*
+ * Save new value to fake channel.
+ */
+ CHN_LOCK(fake);
+ fake->speed = speed;
+ CHN_UNLOCK(fake);
+ }
+ }
}
return err;
@@ -338,8 +519,10 @@ gotch:
/* remove us from our grandparent's channel list */
err = pcm_chn_remove(d, c);
- if (err)
+ if (err) {
+ CHN_UNLOCK(parent);
return err;
+ }
CHN_UNLOCK(parent);
/* destroy ourselves */
@@ -358,6 +541,9 @@ vchan_initsys(device_t dev)
SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
sysctl_hw_snd_vchans, "I", "");
+ SYSCTL_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", "");
#endif
return 0;
OpenPOWER on IntegriCloud