Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Feb 2015 07:51:16 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r278663 - stable/10/sys/dev/sound/usb
Message-ID:  <201502130751.t1D7pG0i012737@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri Feb 13 07:51:15 2015
New Revision: 278663
URL: https://svnweb.freebsd.org/changeset/base/278663

Log:
  MFC r278503:
  Revert r274918 and make a better solution. Poll the synchronisation
  endpoint less frequently to make the sample rate adjustment more
  accurate. This should resolve problems with the DN32-USB module for
  Midas audio systems and possibly other similar products from Klark
  Teknik.

Modified:
  stable/10/sys/dev/sound/usb/uaudio.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/10/sys/dev/sound/usb/uaudio.c	Fri Feb 13 07:48:54 2015	(r278662)
+++ stable/10/sys/dev/sound/usb/uaudio.c	Fri Feb 13 07:51:15 2015	(r278663)
@@ -117,6 +117,7 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, def
     &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 */
@@ -195,7 +196,6 @@ struct uaudio_chan_alt {
 	uint8_t	iface_index;
 	uint8_t	iface_alt_index;
 	uint8_t channels;
-	uint8_t enable_sync;
 };
 
 struct uaudio_chan {
@@ -232,11 +232,12 @@ struct uaudio_chan {
 #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 */
+	struct {
+		uint16_t time;		/* I/O interrupt count */
+		int16_t constant;	/* sample rate adjustment in Hz */
+		int16_t remainder;	/* current remainder */
+	} feedback;
 };
 
 #define	UMIDI_EMB_JACK_MAX   16		/* units */
@@ -1805,14 +1806,6 @@ uaudio_chan_fill_info_sub(struct uaudio_
 		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);
 
@@ -2022,29 +2015,44 @@ uaudio_chan_play_sync_callback(struct us
 		if (temp == 0)
 			break;
 
-		/* correctly scale value */
-
-		temp = (temp * 125ULL) - 64;
+		temp *= 125ULL;
 
 		/* auto adjust */
-
 		while (temp < (sample_rate - (sample_rate / 4)))
 			temp *= 2;
-
+ 
 		while (temp > (sample_rate + (sample_rate / 2)))
 			temp /= 2;
 
-		/* compare */
+		/*
+		 * Some USB audio devices only report a sample rate
+		 * different from the nominal one when they want one
+		 * more or less sample. Make sure we catch this case
+		 * by pulling the sample rate offset slowly towards
+		 * zero if the reported value is equal to the sample
+		 * rate.
+		 */
+		if (temp > sample_rate)
+			ch->feedback.constant += 1;
+		else if (temp < sample_rate)
+			ch->feedback.constant -= 1;
+		else if (ch->feedback.constant > 0)
+			ch->feedback.constant--;
+		else if (ch->feedback.constant < 0)
+			ch->feedback.constant++;
 
-		DPRINTF("Comparing %d < %d\n",
-		    (int)temp, (int)sample_rate);
+		DPRINTF("Comparing %d Hz :: %d Hz :: %d samples drift\n",
+		    (int)temp, (int)sample_rate, (int)ch->feedback.constant);
 
-		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;
+		/*
+		 * Range check sync constant. We cannot change the
+		 * number of samples per second by more than the value
+		 * defined by "UAUDIO_IRQS":
+		 */
+		if (ch->feedback.constant > UAUDIO_IRQS)
+			ch->feedback.constant = UAUDIO_IRQS;
+		else if (ch->feedback.constant < -UAUDIO_IRQS)
+			ch->feedback.constant = -UAUDIO_IRQS;
 		break;
 
 	case USB_ST_SETUP:
@@ -2088,10 +2096,10 @@ tr_transferred:
 		}
 		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->feedback.time) >= UAUDIO_IRQS) {
+			ch->feedback.time = 0;
+			usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
 		}
 
 	case USB_ST_SETUP:
@@ -2126,21 +2134,22 @@ tr_transferred:
 			}
 
 			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->feedback.remainder += ch->feedback.constant;
+				if (ch->feedback.remainder >= UAUDIO_IRQS) {
+					ch->feedback.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->feedback.remainder <= -UAUDIO_IRQS) {
+					ch->feedback.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;
 				}
 			}
 
@@ -2458,6 +2467,9 @@ uaudio_chan_start(struct uaudio_chan *ch
 	}
 	usb_proc_explore_unlock(sc->sc_udev);
 
+	/* reset feedback endpoint state */
+	memset(&ch->feedback, 0, sizeof(ch->feedback));
+
 	if (do_start) {
 		usbd_transfer_start(ch->xfer[0]);
 		usbd_transfer_start(ch->xfer[1]);



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