Subject: Propose: improve audio driver
To: None <tech-kern@netbsd.org>
From: Tetsuya Isaki <isaki@par.odn.ne.jp>
List: tech-kern
Date: 02/24/2002 13:58:35
----Next_Part(Sun_Feb_24_13:58:17_2002_595)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi,

I'd like to introduce the mechanism of factor=1/n to
struct audio_params for x68k vs(4) driver.

for details:
MI audio driver prepares sw_code and factor to be able
to play 8bit/sampling format with the hardware which can
play only 16bit/sampling, for example.

However, so that factor appoints whether data after
sw_code become several times of original one with integer,
MI audio driver does not support the case that data after
sw_code become smaller than original one.

Because X68k built-in ADPCM is 4bit/samping, originally
vs (driver for X68k built-in ADPCM; arch/x68k/dev/vs.c)
can play no format of 8bit/samping and 16bit/sampling format
with mechanism of existing sw_code and factor.

# However, due to a bug (see dev/ic/msm6258.c of attached patch)
# and the easy fix (sample_rate *= 2 in arch/x68k/dev/vs.c of
# attached patch), current vs(4) driver can play it fortunately
# under some conditions.

Here is my proposal. I've improved MI audio driver to use
factor=1/n and I've fixed a bug of vs(4). I attach the
patch to this mail. With this patch, vs(4) can play mulaw and
ulinear8 correctly.  And I think there is no side effect on
any other audio drivers.  I've tested only yds driver on i386.


Recording is untouched.
Please comments.

Thanks.
---
Tetsuya Isaki <isaki@par.odn.ne.jp / isaki@NetBSD.org>

----Next_Part(Sun_Feb_24_13:58:17_2002_595)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="vs-reduce-20020224.diff"

Index: arch/x68k/dev/vs.c
===================================================================
RCS file: /home/isaki/cvsroot/vs/src/sys/arch/x68k/dev/vs.c,v
retrieving revision 1.1.1.3
retrieving revision 1.11
diff -u -r1.1.1.3 -r1.11
--- arch/x68k/dev/vs.c	2001/12/24 09:31:20	1.1.1.3
+++ arch/x68k/dev/vs.c	2002/02/17 09:48:35	1.11
@@ -389,13 +389,16 @@
 		rate = p->sample_rate;
 		p->sw_code = NULL;
 		p->factor = 1;
+		p->reduce_factor = 1;
+		DPRINTF(1, ("vs_set_params: encoding=%d, precision=%d\n",
+			p->encoding, p->precision));
 		switch (p->encoding) {
 		case AUDIO_ENCODING_ULAW:
 			if (p->precision != 8)
 				return EINVAL;
 			if (mode == AUMODE_PLAY) {
 				p->sw_code = msm6258_mulaw_to_adpcm;
-				rate = p->sample_rate * 2;
+				p->reduce_factor = 2;
 			} else {
 				p->sw_code = msm6258_adpcm_to_mulaw;
 				p->factor = 2;
@@ -407,7 +410,7 @@
 				return EINVAL;
 			if (mode == AUMODE_PLAY) {
 				p->sw_code = msm6258_ulinear8_to_adpcm;
-				rate = p->sample_rate * 2;
+				p->reduce_factor = 2;
 			} else {
 				p->sw_code = msm6258_adpcm_to_ulinear8;
 				p->factor = 2;
Index: dev/audio.c
===================================================================
RCS file: /home/isaki/cvsroot/vs/src/sys/dev/audio.c,v
retrieving revision 1.1.1.5
retrieving revision 1.22
diff -u -r1.1.1.5 -r1.22
--- dev/audio.c	2002/02/17 05:28:35	1.1.1.5
+++ dev/audio.c	2002/02/17 09:48:36	1.22
@@ -184,7 +184,7 @@
 
 /* The default audio mode: 8 kHz mono ulaw */
 struct audio_params audio_default = 
-	{ 8000, AUDIO_ENCODING_ULAW, 8, 1, 0, 1 };
+	{ 8000, AUDIO_ENCODING_ULAW, 8, 1, 0, 1, 1 };
 
 struct cfattach audio_ca = {
 	sizeof(struct audio_softc), audioprobe, audioattach, 
@@ -929,6 +929,14 @@
 	}
 #endif
 
+	/*
+	 * Allocate buffer for temporary space for sw_code
+	 */
+	sc->sc_tmpbufsize = 65536;	/* XXX */
+	sc->sc_tmpbuf = malloc(sc->sc_tmpbufsize, M_DEVBUF, M_WAITOK);
+	if (sc->sc_tmpbuf == NULL)
+		return ENOMEM;
+
 	AUDIO_INITINFO(&ai);
 	ai.record.sample_rate = sc->sc_rparams.sample_rate;
 	ai.record.encoding    = sc->sc_rparams.encoding;
@@ -954,6 +962,8 @@
 	sc->sc_open = 0;
 	sc->sc_mode = 0;
 	sc->sc_full_duplex = 0;
+	free(sc->sc_tmpbuf, M_DEVBUF);
+	sc->sc_tmpbuf = NULL;
 	return error;
 }
 
@@ -1100,6 +1110,8 @@
 
 	sc->sc_async_audio = 0;
 	sc->sc_full_duplex = 0;
+	free(sc->sc_tmpbuf, M_DEVBUF);
+	sc->sc_tmpbuf = NULL;
 	splx(s);
 	DPRINTF(("audio_close: done\n"));
 
@@ -1363,6 +1375,9 @@
 	struct audio_ringbuffer *cb = &sc->sc_pr;
 	u_char *inp, *einp;
 	int saveerror, error, s, n, cc, used;
+	int *resid;
+	int buf_resid;
+	int buf_offset;
 
 	DPRINTFN(2,("audio_write: sc=%p count=%lu used=%d(hi=%d)\n", 
 		    sc, (unsigned long)uio->uio_resid, sc->sc_pr.used, 
@@ -1404,7 +1419,28 @@
                      sc->sc_pparams.sw_code, sc->sc_pparams.factor));
 
 	error = 0;
-	while (uio->uio_resid > 0 && !error) {
+
+	/*
+	 * When sw_code reduces output length from input length,
+	 * a conversion by sw_code is needed before putting to
+	 * ring buffer.
+	 */
+   uio_loop:
+	if (sc->sc_pparams.reduce_factor > 1) {
+		n = uio->uio_resid;
+		cc = n < sc->sc_tmpbufsize ? n : sc->sc_tmpbufsize;
+		error = uiomove(sc->sc_tmpbuf, cc, uio);
+		buf_resid = n - uio->uio_resid;
+		if (!sc->sc_pparams.sw_code)
+			panic("why is sw_code NULL?");
+		sc->sc_pparams.sw_code(sc->hw_hdl, sc->sc_tmpbuf, buf_resid);
+		buf_resid /= sc->sc_pparams.reduce_factor;
+		buf_offset = 0;
+		resid = &buf_resid;
+	} else {
+		resid = &uio->uio_resid;
+	}
+	while (*resid > 0 && !error ) {
 		s = splaudio();
 		while (cb->used >= cb->usedhigh) {
 			DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d "
@@ -1435,8 +1471,8 @@
 		}
 		if (n < cc)
 			cc = n;		/* don't write beyond end of buffer */
-		if (uio->uio_resid < cc)
-			cc = uio->uio_resid; 	/* and no more than we have */
+		if (*resid < cc)
+			cc = *resid;	/* and no more than we have */
 
 #ifdef DIAGNOSTIC
 		/* 
@@ -1452,9 +1488,16 @@
 #endif
 		DPRINTFN(1, ("audio_write: uiomove cc=%d inp=%p, left=%lu\n", 
                              cc, inp, (unsigned long)uio->uio_resid));
+		if (sc->sc_pparams.reduce_factor > 1){
+			memcpy(inp, sc->sc_tmpbuf + buf_offset, cc);
+			*resid -= cc;
+			buf_offset += cc;
+		} else
+		{
 		n = uio->uio_resid;
 		error = uiomove(inp, cc, uio);
 		cc = n - uio->uio_resid; /* number of bytes actually moved */
+		}
 #ifdef AUDIO_DEBUG
 		if (error)
 		        printf("audio_write:(1) uiomove failed %d; cc=%d "
@@ -1464,6 +1507,7 @@
 		 * Continue even if uiomove() failed because we may have
 		 * gotten a partial block.
 		 */
+		if (sc->sc_pparams.reduce_factor == 1)
 		if (sc->sc_pparams.sw_code) {
 			sc->sc_pparams.sw_code(sc->hw_hdl, inp, cc);
 			/* Adjust count after the expansion. */
@@ -1513,6 +1557,8 @@
 			audio_fill_silence(&sc->sc_pparams, einp, cc);
 		}
 	}
+	if (uio->uio_resid > 0)
+		goto uio_loop;
 	return (error);
 }
 
@@ -1896,7 +1942,8 @@
 	cb->outp += blksize;
 	if (cb->outp >= cb->end)
 		cb->outp = cb->start;
-	cb->stamp += blksize / sc->sc_pparams.factor;
+	cb->stamp += blksize * sc->sc_pparams.reduce_factor
+	                     / sc->sc_pparams.factor;
 	if (cb->mmapped) {
 		DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n", 
                              cb->outp, blksize, cb->inp));
@@ -1947,7 +1994,7 @@
 		} else {
 			inp = cb->inp;
 			cc = blksize - (inp - cb->start) % blksize;
-			ccr = cc / sc->sc_pparams.factor;
+			ccr = cc * sc->sc_pparams.reduce_factor / sc->sc_pparams.factor;
 			if (cb->pause)
 				cb->pdrops += ccr;
 			else {
Index: dev/audio_if.h
===================================================================
RCS file: /home/isaki/cvsroot/vs/src/sys/dev/audio_if.h,v
retrieving revision 1.1.1.2
retrieving revision 1.4
diff -u -r1.1.1.2 -r1.4
--- dev/audio_if.h	2001/11/03 14:36:48	1.1.1.2
+++ dev/audio_if.h	2002/02/17 09:48:36	1.4
@@ -51,6 +51,7 @@
 	/* Software en/decode functions, set if SW coding required by HW */
 	void	(*sw_code)(void *, u_char *, int);
 	int	factor;				/* coding space change */
+	int	reduce_factor;			/* coding space change */
 };
 
 /* The default audio mode: 8 kHz mono ulaw */
Index: dev/audiovar.h
===================================================================
RCS file: /home/isaki/cvsroot/vs/src/sys/dev/audiovar.h,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -u -r1.1.1.1 -r1.4
--- dev/audiovar.h	2001/11/03 14:35:04	1.1.1.1
+++ dev/audiovar.h	2002/02/17 09:48:36	1.4
@@ -111,6 +111,8 @@
 	/* Ring buffers, separate for record and play. */
 	struct	audio_ringbuffer sc_rr; /* Record ring */
 	struct	audio_ringbuffer sc_pr; /* Play ring */
+	u_char	*sc_tmpbuf;
+	size_t	sc_tmpbufsize;
 
 	u_char	sc_blkset;	/* Blocksize has been set */
 
Index: dev/ic/msm6258.c
===================================================================
RCS file: /home/isaki/cvsroot/vs/src/sys/dev/ic/msm6258.c,v
retrieving revision 1.1.1.2
retrieving revision 1.3
diff -u -r1.1.1.2 -r1.3
--- dev/ic/msm6258.c	2001/11/17 12:00:28	1.1.1.2
+++ dev/ic/msm6258.c	2001/11/17 12:03:00	1.3
@@ -135,11 +135,12 @@
 	char *x = &(mc->mc_estim);
 	short *y = &(mc->mc_amp);
 	register int i;
+	u_char *d = p;
 	u_char f;
 
-	for (i = 0; i < cc; i++) {
-		f = pcm2adpcm_step(p[i], y, x);
-		p[i] = f + (pcm2adpcm_step(p[i], y, x) << 4);
+	for (i = 0; i < cc; ) {
+		f = pcm2adpcm_step(p[i++], y, x);
+		*d++ = f + (pcm2adpcm_step(p[i++], y, x) << 4);
 	}
 }
 

----Next_Part(Sun_Feb_24_13:58:17_2002_595)----