9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] Re: USB Audio on RPi 4
       [not found] <E1pRl0K-0003L2-BP@rmmprod05.runbox>
@ 2023-02-15  6:35 ` adr
  0 siblings, 0 replies; only message in thread
From: adr @ 2023-02-15  6:35 UTC (permalink / raw)
  To: Steve Taylor; +Cc: 9fans

[-- Attachment #1: Type: text/plain, Size: 11237 bytes --]

On Mon, 13 Feb 2023, Steve Taylor wrote:

> From: Steve Taylor <staylor@encom.us>
> Hello adr,
>
> I saw your note on the Plan 9 mailing list about USB audio on the RPi 4 with 9legacy. By USB audio device, do you mean something like a USB DAC? If so, I have spent several hours on this recently. Whenever I play audio through my USB DAC connected to my RPi4, using either 9legacy or 9front, I hear pops and other small noises like static. I think it is from dropped frames. If I use OpenBSD on the same hardware, these issues do not occur. The audio plays perfectly.
>
> Are you seeing issues like this?
>
> I am curious if using a USB DAC which is USB 1.0 would work properly. I identified a couple I could try, but they are about 20 years old and difficult to find for a decent price. The USB DAC I have now uses xhci.
>
> Thanks,
>
> Steve

Hi Steve,

I'm using this cheap one:

https://aliexpress.com/item/1005001394902427.html

ep6.0 enabled control rw speed full maxpkt 8 pollival 0 samplesz 0 hz 0 hub 3 port 4 rootport 1 addr 4 busy
audio csp 0x000101 csp 0x000201 csp 0x000201 csp 0x000003 vid 0x1b3f did 0x2008 GeneralPlus 'USB Audio Device' xhci
ep6.5 enabled iso w speed full maxpkt 180 pollival 1 samplesz 4 hz 44100 hub 3 port 4 rootport 1 addr 4 busy
ep6.6 enabled iso r speed full maxpkt 96 pollival 1 samplesz 2 hz 48000 hub 3 port 4 rootport 1 addr 4 idle
ep6.3 enabled interrupt r speed full maxpkt 8 pollival 32 samplesz 0 hz 0 hub 3 port 4 rootport 1 addr 4 busy

It works ok with 9front. If you just need decent audio output from
your pi, I recommend it.

Now about 9legacy. Richard Miller imported the xhci driver from
9front, so I compared them. The only real difference appart from
IN isochronous transfers support is the addition of sample delay
for buffering. This patch makes the already present control in
usb/audio Delay_control an internal one to set this delay. I used
the same default as 9front, 40ms. I can play audio perfectly now.
Also you can change now the sample rate (speed), the code was
reopening the endpoint with openep() every time setspeed() was
called. More work has to be done, but for now I hope this is helpful
for you.

Try increasing the delay with your device.

I'm sharing this with the list.

P.S. The patch is against the repo at github.com/0intro/plan9-contrib.

Regards,
adr

--- /n/downloads/devusb.c       Tue Feb 14 20:42:27 2023
+++ /sys/src/9/bcm/devusb.c     Tue Feb 14 11:31:28 2023
@@ -88,6 +88,7 @@
        CMdebugep,              /* debug n (set/clear debug for this ep) */
        CMname,                 /* name str (show up as #u/name as well) */
        CMtmout,                /* timeout n (activate timeouts for ep) */
+       CMsampledelay,          /* maximum delay introduced by buffering (iso) */
        CMpreset,               /* reset the port */

        /* Hub feature selectors */
@@ -128,6 +129,7 @@
        {CMclrhalt,     "clrhalt",      1},
        {CMname,        "name",         2},
        {CMtmout,       "timeout",      2},
+       {CMsampledelay, "sampledelay",  2},
        {CMpreset,      "reset",        1},
  };

@@ -1387,6 +1389,11 @@
                ep->tmout = strtoul(cb->f[1], nil, 0);
                if(ep->tmout != 0 && ep->tmout < Xfertmout)
                        ep->tmout = Xfertmout;
+               break;
+       case CMsampledelay:
+               if(ep->ttype != Tiso)
+                       error("ctl ignored for this endpoint type");
+               ep->sampledelay = strtoul(cb->f[1], nil, 0);
                break;
        case CMpreset:
                deprint("usb epctl %s\n", cb->f[0]);
--- /n/downloads/usbxhci.c      Tue Feb 14 20:41:33 2023
+++ /sys/src/9/bcm/usbxhci.c    Tue Feb 14 11:24:53 2023
@@ -194,6 +194,7 @@

        int     stopped;

+       int     *residue;
        Wait    *pending;
        Lock;
  };
@@ -277,6 +278,10 @@
        u32int  incr;
        u32int  tdsz;

+       /* isoread */
+       u32int  rp0;
+       u32int  frame0;
+
        int     nleft;
  };

@@ -1100,10 +1105,17 @@
  {
        if(io->ring == nil)
                return;
-       io->frame = 0;
-       io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
-       io->incr = (ep->hz*io->period<<8)/8000;
-       io->tdsz = (io->incr+255>>8)*ep->samplesz;
+       io->rp0 = io->ring->wp;
+       io->frame0 = io->frame = 0;
+       io->period = ep->pollival << 3*(ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed);
+       if(io->ring->id & 1){
+               io->ring->residue = smalloc((io->ring->mask+1)*sizeof(io->ring->residue[0]));
+               io->incr = 0;
+               io->tdsz = ep->maxpkt*ep->ntds;
+       } else {
+               io->incr = ((vlong)ep->hz*ep->pollival<<8)/1000;
+               io->tdsz = (io->incr+255>>8)*ep->samplesz;
+       }
        io->b = allocb((io->ring->mask+1)*io->tdsz);
  }

@@ -1336,10 +1348,10 @@
        }
        ?? = io->period;
        ctlr = ep->hp->aux;
-       if(needrecover(ctlr))
-               error(Erecover);
        for(i = io->frame;; i++){
                for(;;){
+                       if(needrecover(ctlr))
+                               error(Erecover);
                        m = (int)(io->ring->wp - io->ring->rp);
                        if(m <= 0)
                                i = (80 + ??frame(ctlr))/??;
@@ -1367,11 +1379,13 @@
        io->frame = i;
        while(io->ring->rp != io->ring->wp){
                int d = (int)(i*?? - ??frame(ctlr))/8;
-               //d -= ep->sampledelay*1000 / ep->hz;
+               d -= ep->sampledelay*1000 / ep->hz;
                if(d < 5)
                        break;
                *io->ring->doorbell = io->ring->id;
                tsleep(&up->sleep, return0, nil, d);
+                       if(needrecover(ctlr))
+                               error(Erecover);
        }
        qunlock(io);
        poperror();
--- /n/downloads/audio.h        Tue Feb 14 23:52:55 2023
+++ /sys/src/cmd/usb/audio/audio.h      Tue Feb 14 23:29:03 2023
@@ -39,6 +39,11 @@

  #define SAMPLING_FREQ_CONTROL 0x01

+#define DSPEEDPLAY 44100
+#define DSPEEDREC 44100
+/* 40ms */
+#define DDELAY 1764
+
  typedef struct Audioalt Audioalt;

  struct Audioalt {
--- /n/downloads/audio.c        Tue Feb 14 23:52:36 2023
+++ /sys/src/cmd/usb/audio/audio.c      Wed Feb 15 01:00:51 2023
@@ -25,7 +25,7 @@

  int verbose;
  int setrec = 0;
-int defaultspeed[2] = {44100, 44100};
+int defaultspeed[2]= { DSPEEDPLAY, DSPEEDREC};
  Dev *buttondev;
  Dev *epdev[2];

@@ -372,6 +372,8 @@
        value[0] = defaultspeed[Record];
        if(endpt[Record] >= 0 && setcontrol(Record, "speed", value) < 0)
                fprint(2, "%s: can't set record speed\n", argv0);
+       value[0] = DDELAY;
+       setcontrol(Play, "delay", value);
        value[0] = 0;
        setcontrol(Play, "mute", value);

--- /n/downloads/audioctl.c     Tue Feb 14 20:43:26 2023
+++ /sys/src/cmd/usb/audio/audioctl.c   Wed Feb 15 01:13:48 2023
@@ -18,7 +18,7 @@

  Audiocontrol controls[2][Ncontrol] = {
        {
-       [Speed_control] = {             "speed",        0, {0}, 0,      44100,  Undef},
+       [Speed_control] = {             "speed",        0, {0}, 0,      DSPEEDPLAY,     Undef},
        [Mute_control] = {              "mute",         0, {0}, 0,      0,      Undef},
        [Volume_control] = {            "volume",       0, {0}, 0,      0,      Undef},
        [Bass_control] = {              "bass",         0, {0}, 0,      0,      Undef},
@@ -26,14 +26,14 @@
        [Treble_control] = {            "treble",       0, {0}, 0,      0,      Undef},
        [Equalizer_control] = {         "equalizer",    0, {0}, 0,      0,      Undef},
        [Agc_control] = {               "agc",          0, {0}, 0,      0,      Undef},
-       [Delay_control] = {             "delay",        0, {0}, 0,      0,      Undef},
+       [Delay_control] = {             "delay",        1, {1}, 0,      DDELAY, Undef},
        [Bassboost_control] = {         "bassboost",    0, {0}, 0,      0,      Undef},
        [Loudness_control] = {          "loudness",     0, {0}, 0,      0,      Undef},
        [Channel_control] = {           "channels",     0, {0}, 0,      2,      Undef},
        [Resolution_control] = {        "resolution",   0, {0}, 0,      16,     Undef},
  //    [Selector_control] = {          "selector",     0, {0}, 0,      0,      Undef},
        }, {
-       [Speed_control] = {             "speed",        0, {0}, 0,      44100,  Undef},
+       [Speed_control] = {             "speed",        0, {0}, 0,      DSPEEDREC,      Undef},
        [Mute_control] = {              "mute",         0, {0}, 0,      0,      Undef},
        [Volume_control] = {            "volume",       0, {0}, 0,      0,      Undef},
        [Bass_control] = {              "bass",         0, {0}, 0,      0,      Undef},
@@ -129,6 +129,15 @@
  }

  int
+setdelay(int rec, int delay)
+{
+       if(rec == Record && !setrec)
+               return Undef;
+       devctl(epdev[rec], "sampledelay %ld", delay);
+       return delay;
+}
+
+int
  setspeed(int rec, int speed)
  {
        int ps, n, no, dist, i;
@@ -226,10 +235,11 @@
        }
        dprint(2, "Configuring %s endpoint for %d Hz\n",
                                rec?"record":"playback", speed);
-       epdev[rec] = openep(ad, endpt[rec]);
-       if(epdev[rec] == nil)
-               sysfatal("openep rec %d: %r", rec);
-
+       if(epdev[rec] == nil){
+               epdev[rec] = openep(ad, endpt[rec]);
+               if(epdev[rec] == nil)
+                       sysfatal("openep rec %d: %r", rec);
+       }
        devctl(epdev[rec], "pollival %d", da->interval);
        devctl(epdev[rec], "samplesz %ld", controls[rec][Channel_control].value[0] *
                                controls[rec][Resolution_control].value[0]/8);
@@ -391,8 +401,11 @@
                c->value[0] = value[0];
                controls[rec][Speed_control].value[0] = defaultspeed[rec];
                return 0;
-       case Volume_control:
        case Delay_control:
+               setdelay(rec, value[0]);
+               c->value[0] = value[0];
+               return 0;
+       case Volume_control:
                count = 2;
                /* fall through */
        case Mute_control:
@@ -480,8 +493,12 @@
                if(req == Rgetcur)
                        value[0] = controls[rec][ctl].value[0];
                return 0;
-       case Volume_control:
        case Delay_control:
+               if(req != Rgetcur)
+                       return Undef;
+               value[0] = controls[rec][ctl].value[0];
+               return 0;
+       case Volume_control:
                count = 2;
                /* fall through */
        case Bass_control:
------------------------------------------
9fans: 9fans
Permalink: https://9fans.topicbox.com/groups/9fans/Tf6820d74b730765b-M6771d495bc597d497acef054
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-diff; name=usbaudio.patch, Size: 6522 bytes --]

--- /n/downloads/devusb.c	Tue Feb 14 20:42:27 2023
+++ /sys/src/9/bcm/devusb.c	Tue Feb 14 11:31:28 2023
@@ -88,6 +88,7 @@
 	CMdebugep,		/* debug n (set/clear debug for this ep) */
 	CMname,			/* name str (show up as #u/name as well) */
 	CMtmout,		/* timeout n (activate timeouts for ep) */
+	CMsampledelay,		/* maximum delay introduced by buffering (iso) */
 	CMpreset,		/* reset the port */
 
 	/* Hub feature selectors */
@@ -128,6 +129,7 @@
 	{CMclrhalt,	"clrhalt",	1},
 	{CMname,	"name",		2},
 	{CMtmout,	"timeout",	2},
+	{CMsampledelay,	"sampledelay",	2},
 	{CMpreset,	"reset",	1},
 };
 
@@ -1387,6 +1389,11 @@
 		ep->tmout = strtoul(cb->f[1], nil, 0);
 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
 			ep->tmout = Xfertmout;
+		break;
+	case CMsampledelay:
+		if(ep->ttype != Tiso)
+			error("ctl ignored for this endpoint type");
+		ep->sampledelay = strtoul(cb->f[1], nil, 0);
 		break;
 	case CMpreset:
 		deprint("usb epctl %s\n", cb->f[0]);
--- /n/downloads/usbxhci.c	Tue Feb 14 20:41:33 2023
+++ /sys/src/9/bcm/usbxhci.c	Tue Feb 14 11:24:53 2023
@@ -194,6 +194,7 @@
 
 	int	stopped;
 
+	int	*residue;
 	Wait	*pending;
 	Lock;
 };
@@ -277,6 +278,10 @@
 	u32int	incr;
 	u32int	tdsz;
 
+	/* isoread */
+	u32int	rp0;
+	u32int	frame0;
+
 	int	nleft;
 };
 
@@ -1100,10 +1105,17 @@
 {
 	if(io->ring == nil)
 		return;
-	io->frame = 0;
-	io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
-	io->incr = (ep->hz*io->period<<8)/8000;
-	io->tdsz = (io->incr+255>>8)*ep->samplesz;
+	io->rp0 = io->ring->wp;
+	io->frame0 = io->frame = 0;
+	io->period = ep->pollival << 3*(ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed);
+	if(io->ring->id & 1){
+		io->ring->residue = smalloc((io->ring->mask+1)*sizeof(io->ring->residue[0]));
+		io->incr = 0;
+		io->tdsz = ep->maxpkt*ep->ntds;
+	} else {
+		io->incr = ((vlong)ep->hz*ep->pollival<<8)/1000;
+		io->tdsz = (io->incr+255>>8)*ep->samplesz;
+	}
 	io->b = allocb((io->ring->mask+1)*io->tdsz);
 }
 
@@ -1336,10 +1348,10 @@
 	}
 	µ = io->period;
 	ctlr = ep->hp->aux;
-	if(needrecover(ctlr))
-		error(Erecover);
 	for(i = io->frame;; i++){
 		for(;;){
+			if(needrecover(ctlr))
+				error(Erecover);
 			m = (int)(io->ring->wp - io->ring->rp);
 			if(m <= 0)
 				i = (80 + µframe(ctlr))/µ;
@@ -1367,11 +1379,13 @@
 	io->frame = i;
 	while(io->ring->rp != io->ring->wp){
 		int d = (int)(i*µ - µframe(ctlr))/8;
-		//d -= ep->sampledelay*1000 / ep->hz;
+		d -= ep->sampledelay*1000 / ep->hz;
 		if(d < 5)
 			break;
 		*io->ring->doorbell = io->ring->id;
 		tsleep(&up->sleep, return0, nil, d);
+			if(needrecover(ctlr))
+				error(Erecover);
 	}
 	qunlock(io);
 	poperror();
--- /n/downloads/audio.h	Tue Feb 14 23:52:55 2023
+++ /sys/src/cmd/usb/audio/audio.h	Tue Feb 14 23:29:03 2023
@@ -39,6 +39,11 @@
 
 #define SAMPLING_FREQ_CONTROL 0x01
 
+#define DSPEEDPLAY 44100
+#define DSPEEDREC 44100
+/* 40ms */
+#define DDELAY 1764
+
 typedef struct Audioalt Audioalt;
 
 struct Audioalt {
--- /n/downloads/audio.c	Tue Feb 14 23:52:36 2023
+++ /sys/src/cmd/usb/audio/audio.c	Wed Feb 15 01:00:51 2023
@@ -25,7 +25,7 @@
 
 int verbose;
 int setrec = 0;
-int defaultspeed[2] = {44100, 44100};
+int defaultspeed[2]= { DSPEEDPLAY, DSPEEDREC};
 Dev *buttondev;
 Dev *epdev[2];
 
@@ -372,6 +372,8 @@
 	value[0] = defaultspeed[Record];
 	if(endpt[Record] >= 0 && setcontrol(Record, "speed", value) < 0)
 		fprint(2, "%s: can't set record speed\n", argv0);
+	value[0] = DDELAY;
+	setcontrol(Play, "delay", value);
 	value[0] = 0;
 	setcontrol(Play, "mute", value);
 
--- /n/downloads/audioctl.c	Tue Feb 14 20:43:26 2023
+++ /sys/src/cmd/usb/audio/audioctl.c	Wed Feb 15 01:13:48 2023
@@ -18,7 +18,7 @@
 
 Audiocontrol controls[2][Ncontrol] = {
 	{
-	[Speed_control] = {		"speed",	0, {0}, 0,	44100,	Undef},
+	[Speed_control] = {		"speed",	0, {0}, 0,	DSPEEDPLAY,	Undef},
 	[Mute_control] = {		"mute",		0, {0}, 0,	0,	Undef},
 	[Volume_control] = {		"volume",	0, {0}, 0,	0,	Undef},
 	[Bass_control] = {		"bass",		0, {0}, 0,	0,	Undef},
@@ -26,14 +26,14 @@
 	[Treble_control] = {		"treble",	0, {0}, 0,	0,	Undef},
 	[Equalizer_control] = {		"equalizer",	0, {0}, 0,	0,	Undef},
 	[Agc_control] = {		"agc",		0, {0}, 0,	0,	Undef},
-	[Delay_control] = {		"delay",	0, {0}, 0,	0,	Undef},
+	[Delay_control] = {		"delay",	1, {1}, 0,	DDELAY,	Undef},
 	[Bassboost_control] = {		"bassboost",	0, {0}, 0,	0,	Undef},
 	[Loudness_control] = {		"loudness",	0, {0}, 0,	0,	Undef},
 	[Channel_control] = {		"channels",	0, {0}, 0,	2,	Undef},
 	[Resolution_control] = {	"resolution",	0, {0}, 0,	16,	Undef},
 //	[Selector_control] = {		"selector",	0, {0}, 0,	0,	Undef},
 	}, {
-	[Speed_control] = {		"speed",	0, {0}, 0,	44100,	Undef},
+	[Speed_control] = {		"speed",	0, {0}, 0,	DSPEEDREC,	Undef},
 	[Mute_control] = {		"mute",		0, {0}, 0,	0,	Undef},
 	[Volume_control] = {		"volume",	0, {0}, 0,	0,	Undef},
 	[Bass_control] = {		"bass",		0, {0}, 0,	0,	Undef},
@@ -129,6 +129,15 @@
 }
 
 int
+setdelay(int rec, int delay)
+{
+	if(rec == Record && !setrec)
+		return Undef;
+	devctl(epdev[rec], "sampledelay %ld", delay);
+	return delay;
+}
+
+int
 setspeed(int rec, int speed)
 {
 	int ps, n, no, dist, i;
@@ -226,10 +235,11 @@
 	}
 	dprint(2, "Configuring %s endpoint for %d Hz\n",
 				rec?"record":"playback", speed);
-	epdev[rec] = openep(ad, endpt[rec]);
-	if(epdev[rec] == nil)
-		sysfatal("openep rec %d: %r", rec);
-
+	if(epdev[rec] == nil){
+		epdev[rec] = openep(ad, endpt[rec]);
+		if(epdev[rec] == nil)
+			sysfatal("openep rec %d: %r", rec);
+	}
 	devctl(epdev[rec], "pollival %d", da->interval);
 	devctl(epdev[rec], "samplesz %ld", controls[rec][Channel_control].value[0] *
 				controls[rec][Resolution_control].value[0]/8);
@@ -391,8 +401,11 @@
 		c->value[0] = value[0];
 		controls[rec][Speed_control].value[0] = defaultspeed[rec];
 		return 0;
-	case Volume_control:
 	case Delay_control:
+		setdelay(rec, value[0]);
+		c->value[0] = value[0];
+		return 0;
+	case Volume_control:
 		count = 2;
 		/* fall through */
 	case Mute_control:
@@ -480,8 +493,12 @@
 		if(req == Rgetcur)
 			value[0] = controls[rec][ctl].value[0];
 		return 0;
-	case Volume_control:
 	case Delay_control:
+		if(req != Rgetcur)
+			return Undef;
+		value[0] = controls[rec][ctl].value[0];
+		return 0;
+	case Volume_control:
 		count = 2;
 		/* fall through */
 	case Bass_control:

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-02-15  6:35 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <E1pRl0K-0003L2-BP@rmmprod05.runbox>
2023-02-15  6:35 ` [9fans] Re: USB Audio on RPi 4 adr

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).