Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 07 Feb 2015 16:21:01 +0100
From:      Hans Petter Selasky <hps@selasky.org>
To:        freebsd-usb@freebsd.org, freebsd-multimedia@FreeBSD.org
Subject:   USB audio patch
Message-ID:  <54D62D5D.2000201@selasky.org>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------080502000008090701010708
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit

Hi,

If you have a USB audio device which uses the feedback endpoint, where 
the sample rate is not locked to the USB, you may want to try the 
attached patch. I will test it a bit more and then it will hit the tree 
next week.

The basic of the change is to reduce the number of sample rate 
adjustments. Some devices only update the sample rate very slowly, and 
the current USB audio driver is polling very fast, so the algorithm 
simply becomes unreliable with some kinds of USB audio devices.

--HPS

--------------080502000008090701010708
Content-Type: text/x-patch;
 name="uaudio.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="uaudio.patch"

Index: sys/dev/sound/usb/uaudio.c
===================================================================
--- sys/dev/sound/usb/uaudio.c	(revision 278206)
+++ sys/dev/sound/usb/uaudio.c	(working copy)
@@ -111,6 +111,7 @@
     &uaudio_default_channels, 0, "uaudio default sample channels");
 #endif
 
+#define	UAUDIO_IRQS	(8000 / UAUDIO_NFRAMES)	/* interrupts per second */
 #define	UAUDIO_NFRAMES		64	/* must be factor of 8 due HS-USB */
 #define	UAUDIO_NCHANBUFS	2	/* number of outstanding request */
 #define	UAUDIO_RECURSE_LIMIT	255	/* rounds */
@@ -189,7 +190,6 @@
 	uint8_t	iface_index;
 	uint8_t	iface_alt_index;
 	uint8_t channels;
-	uint8_t enable_sync;
 };
 
 struct uaudio_chan {
@@ -226,11 +226,10 @@
 #define	CHAN_OP_STOP 2
 #define	CHAN_OP_DRAIN 3
 
-	uint8_t last_sync_time;
-	uint8_t last_sync_state;
-#define	UAUDIO_SYNC_NONE 0
-#define	UAUDIO_SYNC_MORE 1
-#define	UAUDIO_SYNC_LESS 2
+	/* USB audio feedback endpoint state */
+	int16_t last_sync_time;
+	int16_t last_sync_constant;	/* sample rate adjustment in Hz */
+	int16_t last_sync_remainder;
 };
 
 #define	UMIDI_EMB_JACK_MAX   16		/* units */
@@ -1799,14 +1798,6 @@
 		chan_alt->iface_index = curidx;
 		chan_alt->iface_alt_index = alt_index;
 
-		if (UEP_HAS_SYNCADDR(ed1) && ed1->bSynchAddress != 0) {
-			DPRINTF("Sync endpoint will be used, if present\n");
-			chan_alt->enable_sync = 1;
-		} else {
-			DPRINTF("Sync endpoint will not be used\n");
-			chan_alt->enable_sync = 0;
-		}
-
 		usbd_set_parent_iface(sc->sc_udev, curidx,
 		    sc->sc_mixer_iface_index);
 
@@ -2028,17 +2019,21 @@
 		while (temp > (sample_rate + (sample_rate / 2)))
 			temp /= 2;
 
-		/* compare */
-
 		DPRINTF("Comparing %d < %d\n",
 		    (int)temp, (int)sample_rate);
 
-		if (temp == sample_rate)
-			ch->last_sync_state = UAUDIO_SYNC_NONE;
-		else if (temp > sample_rate)
-			ch->last_sync_state = UAUDIO_SYNC_MORE;
-		else
-			ch->last_sync_state = UAUDIO_SYNC_LESS;
+		/* Update sync constant */
+		ch->last_sync_constant += (temp - sample_rate);
+
+		/*
+		 * Range check sync constant. We cannot change the
+		 * number of samples per second by more than the value
+		 * defined by "UAUDIO_IRQS":
+		 */
+		if (ch->last_sync_constant > UAUDIO_IRQS)
+			ch->last_sync_constant = UAUDIO_IRQS;
+		else if (ch->last_sync_constant < -UAUDIO_IRQS)
+			ch->last_sync_constant = -UAUDIO_IRQS;
 		break;
 
 	case USB_ST_SETUP:
@@ -2082,10 +2077,10 @@
 		}
 		chn_intr(ch->pcm_ch);
 
-		/* start SYNC transfer, if any */
-		if (ch->usb_alt[ch->cur_alt].enable_sync != 0) {
-			if ((ch->last_sync_time++ & 7) == 0)
-				usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
+		/* start the SYNC transfer one time per second, if any */
+		if (++(ch->last_sync_time) >= UAUDIO_IRQS) {
+			ch->last_sync_time = 0;
+			usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
 		}
 
 	case USB_ST_SETUP:
@@ -2120,21 +2115,22 @@
 			}
 
 			if (n == (blockcount - 1)) {
-				switch (ch->last_sync_state) {
-				case UAUDIO_SYNC_MORE:
+				/*
+				 * Update sync remainder and check if
+				 * we should transmit more or less
+				 * data:
+				 */
+				ch->last_sync_remainder += ch->last_sync_constant;
+				if (ch->last_sync_remainder >= UAUDIO_IRQS) {
+					ch->last_sync_remainder -= UAUDIO_IRQS;
 					DPRINTFN(6, "sending one sample more\n");
 					if ((frame_len + sample_size) <= mfl)
 						frame_len += sample_size;
-					ch->last_sync_state = UAUDIO_SYNC_NONE;
-					break;
-				case UAUDIO_SYNC_LESS:
+				} else if (ch->last_sync_remainder <= -UAUDIO_IRQS) {
+					ch->last_sync_remainder += UAUDIO_IRQS;
 					DPRINTFN(6, "sending one sample less\n");
 					if (frame_len >= sample_size)
 						frame_len -= sample_size;
-					ch->last_sync_state = UAUDIO_SYNC_NONE;
-					break;
-				default:
-					break;
 				}
 			}
 

--------------080502000008090701010708--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?54D62D5D.2000201>