Date: Sat, 15 Sep 2001 11:41:16 +0400 From: Maxim Sobolev <sobomax@mail.ru> To: cg@FreeBSD.org Cc: multimedia@FreeBSD.org, current@FreeBSD.org Subject: Making DMA buffer resizeable depending on audio speed/format Message-ID: <E15iA4g-0007aK-00@smtp6.port.ru>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi there,
I want to get your comments on the attached patch, which makes sound
driver resizing its DMA buffer according to the currently selected
audio speed/format. This is necessary because most audio hardware
supports wide range of speeds/formats, which makes it hard to define
one buffer size that will satisfy all supported formats and providing
minimal latency for the formats with low datarates, while good skip
protection for formats with high datarates. For example 4096 bytes used
now in most drivers doesn't protect data playing on 44kHz from skipping
when there is some kernel activity going on (for example output on
console or switching between consoles), while at the same time this
size means 0.5s latency for games that use 8kHz/8 bit audio formats,
which is a quite noticeable delay.
Attached patch fixes some maximal buffer size, which is necessary
for proper registering with the DMA subsystem, while scales this
buffer down when format with lower datarate is selected. I'm running
this patch for a month on my -current system with OPL3-SA hardware
and so far it works like a charm - mpg123 no longer skips when I'm
scrolling in the editor running on console, while audio delay in
digger (22kHz, 8 bit, mono) is absolutely unnoticeable.
This patch only improves mss driver, but it should be relatively
easy to modify other drivers as well (I do not have a hardware to
test changes on).
Thank you in advance!
-Maxim
[-- Attachment #2 --]
? isa/mss.c,v
? pcm/buffer.c,v
? pcm/sound.c,v
Index: isa/mss.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/isa/mss.c,v
retrieving revision 1.71
diff -d -u -r1.71 mss.c
--- isa/mss.c 2001/08/23 11:30:50 1.71
+++ isa/mss.c 2001/09/15 04:30:39
@@ -37,7 +37,9 @@
#include "mixer_if.h"
-#define MSS_BUFFSIZE (4096)
+#define MSS_MAXDATARATE (48000 * 2 * 2) /* 48kHz, 16bit, stereo */
+#define MSS_MAXBUFFSIZE (16384)
+#define MSS_MINBUFFSIZE (1024)
#define abs(x) (((x) < 0) ? -(x) : (x))
#define MSS_INDEXED_REGS 0x20
#define OPL_INDEXED_REGS 0x19
@@ -92,6 +94,7 @@
static int opti_detect(device_t dev, struct mss_info *mss);
static char *ymf_test(device_t dev, struct mss_info *mss);
static void ad_unmute(struct mss_info *mss);
+static void mss_adjustbuf(struct mss_chinfo *ch, int speed, u_int32_t format);
/* mixer set funcs */
static int mss_mixer_set(struct mss_info *mss, int dev, int left, int right);
@@ -328,12 +331,12 @@
if (ok) {
pdma = rman_get_start(mss->drq1);
isa_dma_acquire(pdma);
- isa_dmainit(pdma, MSS_BUFFSIZE);
+ isa_dmainit(pdma, MSS_MAXBUFFSIZE);
mss->bd_flags &= ~BD_F_DUPLEX;
if (mss->drq2) {
rdma = rman_get_start(mss->drq2);
isa_dma_acquire(rdma);
- isa_dmainit(rdma, MSS_BUFFSIZE);
+ isa_dmainit(rdma, MSS_MAXBUFFSIZE);
mss->bd_flags |= BD_F_DUPLEX;
} else mss->drq2 = mss->drq1;
}
@@ -925,6 +928,32 @@
wait_for_calibration(mss);
}
+/*
+ * Resize the DMA buffer, so that amount of data it holds is such that the time
+ * to play that data remains approximately constant.
+ */
+static void
+mss_adjustbuf(struct mss_chinfo *ch, int speed, u_int32_t format)
+{
+ int newsize, rate;
+
+ rate = sndbuf_datarate(speed, format);
+ if (rate == -1) {
+ /* Should never happen */
+ printf("mss: unrecognised audio format: %u\n", format);
+ return;
+ }
+ newsize = (rate * (MSS_MAXBUFFSIZE / 1024) / MSS_MAXDATARATE) * 1024;
+ if (newsize < MSS_MINBUFFSIZE)
+ newsize = MSS_MINBUFFSIZE;
+ else if (newsize > MSS_MAXBUFFSIZE) {
+ /* Should never happen */
+ printf("mss: bogus MSS_MAXBUFFSIZE, perhaps error in the driver\n");
+ newsize = MSS_MAXBUFFSIZE;
+ }
+ sndbuf_resize(ch->buffer, 2, newsize / 2);
+}
+
static int
mss_speed(struct mss_chinfo *ch, int speed)
{
@@ -960,6 +989,7 @@
speed = speeds[sel];
ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel);
}
+ mss_adjustbuf(ch, speed, ch->channel->format);
ad_leave_MCE(mss);
return speed;
@@ -999,6 +1029,7 @@
ad_enter_MCE(mss);
ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg);
if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */
+ mss_adjustbuf(ch, ch->channel->speed, format);
ad_leave_MCE(mss);
return format;
}
@@ -1120,7 +1151,8 @@
ch->channel = c;
ch->buffer = b;
ch->dir = dir;
- if (sndbuf_alloc(ch->buffer, mss->parent_dmat, MSS_BUFFSIZE) == -1) return NULL;
+ if (sndbuf_alloc(ch->buffer, mss->parent_dmat, MSS_MAXBUFFSIZE) == -1)
+ return NULL;
sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? mss->drq1 : mss->drq2);
return ch;
}
@@ -1708,7 +1740,7 @@
/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
- /*maxsize*/MSS_BUFFSIZE, /*nsegments*/1,
+ /*maxsize*/MSS_MAXBUFFSIZE, /*nsegments*/1,
/*maxsegz*/0x3ffff,
/*flags*/0, &mss->parent_dmat) != 0) {
device_printf(dev, "unable to create dma tag\n");
@@ -2208,5 +2240,3 @@
DRIVER_MODULE(snd_guspcm, gusc, guspcm_driver, pcm_devclass, 0, 0);
MODULE_DEPEND(snd_guspcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
MODULE_VERSION(snd_guspcm, 1);
-
-
Index: pcm/buffer.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/buffer.c,v
retrieving revision 1.7
diff -d -u -r1.7 buffer.c
--- pcm/buffer.c 2001/08/23 11:30:52 1.7
+++ pcm/buffer.c 2001/09/15 04:30:39
@@ -646,3 +646,30 @@
/* tell isa_dma to bounce data in/out */
}
+/*
+ * Given a speed and audio encoding format calculate and return data rate of
+ * audio stream, i.e. number of bytes required to hold 1 second of audio data.
+ * Return -1 if supplied format is incorrect (i.e. doesn't contain any of
+ * recogniseable AFMT_* values.
+ */
+int
+sndbuf_datarate(int speed, u_int32_t fmt)
+{
+ int i, j;
+ /* Use static arrays to avoid using valuable kernel's stack */
+ static u_int32_t f4[] = {4, AFMT_IMA_ADPCM, 0};
+ static u_int32_t f8[] = {8, AFMT_MU_LAW, AFMT_A_LAW, AFMT_U8,
+ AFMT_S8, 0};
+ static u_int32_t f16[] = {16, AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE,
+ AFMT_U16_BE, 0};
+ static u_int32_t f32[] = {32, AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE,
+ AFMT_U32_BE, 0};
+ static u_int32_t *fmts[] = {f4, f8, f16, f32, NULL};
+
+ for (i = 0; fmts[i] != NULL; i++)
+ for (j = 1; *(fmts[i] + j) != 0; j++)
+ if ((fmt & *(fmts[i] + j)) != 0)
+ return ((fmt & AFMT_STEREO ? 2 : 1 ) *
+ (*fmts[i]) * speed) / 8;
+ return -1;
+}
Index: pcm/buffer.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/buffer.h,v
retrieving revision 1.3
diff -d -u -r1.3 buffer.h
--- pcm/buffer.h 2001/05/27 14:39:34 1.3
+++ pcm/buffer.h 2001/09/15 04:30:39
@@ -93,4 +93,4 @@
int sndbuf_isadmaptr(struct snd_dbuf *b);
void sndbuf_isadmabounce(struct snd_dbuf *b);
-
+int sndbuf_datarate(int, u_int32_t);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E15iA4g-0007aK-00>
