Curious sound playback slowdown

Ariff Abdullah skywizard at MyBSD.org.my
Fri Sep 23 22:13:40 PDT 2005


On Wed, 21 Sep 2005 17:17:40 +0800
Ariff Abdullah <skywizard at MyBSD.org.my> wrote:
> On Tue, 20 Sep 2005 22:18:17 -0700
> Jos Backus <jos at catnook.com> wrote:
> > On Fri, Sep 16, 2005 at 10:43:59PM -0701, Jos Backus wrote:
> > ...
> > >  92231 firefox-bin CALL  ioctl(0x13,SNDCTL_DSP_STEREO,0xbfbf6ca0)
> > >  92231 firefox-bin RET   ioctl 0
> > >  92231 firefox-bin CALL  ioctl(0x13,SNDCTL_DSP_SPEED,0xbfbf6cdc)
> > >  92231 firefox-bin RET   ioctl 0
> > >  92231 firefox-bin CALL 
> > >  ioctl(0x13,SNDCTL_DSP_SETFRAGMENT,0xbfbf6d08) 92231 firefox-bin
> > >  RET
> > >    ioctl 0
> > >  92231 firefox-bin CALL  ioctl(0x13,SNDCTL_DSP_GETCAPS,0xbfbf6d04)
> > >  92231 firefox-bin RET   ioctl 0
> > >  92231 firefox-bin CALL  fcntl(0x13,0x4,0)
> > >  92231 firefox-bin RET   fcntl 0
> > >  92231 firefox-bin CALL  close(0x13)
> > ...
> > 
> > It's unclear to me why the SNDCTL_DSP_SPEED ioctl is stuffing the
> > wrong value into the device. Running the program below as
> > `set-samplerate 22050' appears to mimic the change the Flash plugin
> > effects. Running `set-samplerate' restores sound pitch to a normal
> > value. So I have a workaround for now at least.
> > 
> Luckily I have this card and do realize this problem. That's because
> flash plugin trying to set speed for recording that differ from
> playback speed.
> 
> I will examine this issue later this day.
> 

Please try this attached patch. es1370 locked both playback (dac) and
recording (adc), conflicting each other while doing full duplex
operation, so the only way to solve this conflicting sampling rate is to
treat the chip as a single fixed rate codec. Fortunately our soft sample
rate converter is quite decent to handle varying sample rate both for
playback and recording operation, not a big issue unless you're
*extremely* picky about sound quality with hearing beyond average.

Note that you can return to the old behaviour either with
hint.pcm.0.fixed_rate=0 (/boot/device.hints) or simply sysctl
hw.snd.pcm0.fixed_rate=0. You can set you prefered default fixed rate
with these settings, but pay attention on hw.snd.pcm0.vchanrate too.


--

Ariff Abdullah
MyBSD

http://www.MyBSD.org.my (IPv6/IPv4)
http://staff.MyBSD.org.my (IPv6/IPv4)
http://tomoyo.MyBSD.org.my (IPv6/IPv4)
-------------- next part --------------
--- sys/dev/sound/pci/es137x.c.orig	Sun Jul 31 21:19:38 2005
+++ sys/dev/sound/pci/es137x.c	Sat Sep 24 11:04:48 2005
@@ -111,6 +111,7 @@
 	int num;
 	int spdif_en;
 	unsigned int bufsz;
+	struct pcmchan_caps caps;
 
 	/* Contents of board's registers */
 	u_long		ctrl;
@@ -128,27 +129,18 @@
 static void	es1371_src_write(struct es_info *, u_short, unsigned short);
 static u_int	es1371_adc_rate(struct es_info *, u_int, int);
 static u_int	es1371_dac_rate(struct es_info *, u_int, int);
-static int	es1371_init(struct es_info *, device_t);
+static int	es1371_init(struct es_info *);
 static int      es1370_init(struct es_info *);
 static int      es1370_wrcodec(struct es_info *, u_char, u_char);
 
-static u_int32_t es_playfmt[] = {
+static u_int32_t es_fmt[] = {
 	AFMT_U8,
 	AFMT_STEREO | AFMT_U8,
 	AFMT_S16_LE,
 	AFMT_STEREO | AFMT_S16_LE,
 	0
 };
-static struct pcmchan_caps es_playcaps = {4000, 48000, es_playfmt, 0};
-
-static u_int32_t es_recfmt[] = {
-	AFMT_U8,
-	AFMT_STEREO | AFMT_U8,
-	AFMT_S16_LE,
-	AFMT_STEREO | AFMT_S16_LE,
-	0
-};
-static struct pcmchan_caps es_reccaps = {4000, 48000, es_recfmt, 0};
+static struct pcmchan_caps es_caps = {4000, 48000, es_fmt, 0};
 
 static const struct {
 	unsigned        volidx:4;
@@ -309,15 +301,7 @@
 	snd_mtxunlock(es->lock);
 	if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0)
 		return NULL;
-	return ch;
-}
-
-static int
-eschan_setdir(kobj_t obj, void *data, int dir)
-{
-	struct es_chinfo *ch = data;
-	struct es_info *es = ch->parent;
-
+	snd_mtxlock(es->lock);
 	if (dir == PCMDIR_PLAY) {
 		es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8, 1);
 		es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4);
@@ -328,7 +312,8 @@
 		es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4);
 	}
 	ch->dir = dir;
-	return 0;
+	snd_mtxunlock(es->lock);
+	return ch;
 }
 
 static int
@@ -357,6 +342,13 @@
 	struct es_chinfo *ch = data;
 	struct es_info *es = ch->parent;
 
+	/* XXX Fixed rate , do nothing. */
+	if (es->caps.minspeed == es->caps.maxspeed)
+		return es->caps.maxspeed;
+	if (speed < es->caps.minspeed)
+		speed = es->caps.minspeed;
+	if (speed > es->caps.maxspeed)
+		speed = es->caps.maxspeed;
 	es->ctrl &= ~CTRL_PCLKDIV;
 	es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
 	es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
@@ -452,12 +444,13 @@
 eschan_getcaps(kobj_t obj, void *data)
 {
 	struct es_chinfo *ch = data;
-	return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
+	struct es_info *es = ch->parent;
+
+	return &es->caps;
 }
 
 static kobj_method_t eschan1370_methods[] = {
     	KOBJMETHOD(channel_init,		eschan_init),
-    	KOBJMETHOD(channel_setdir,		eschan_setdir),
     	KOBJMETHOD(channel_setformat,		eschan_setformat),
     	KOBJMETHOD(channel_setspeed,		eschan1370_setspeed),
     	KOBJMETHOD(channel_setblocksize,	eschan_setblocksize),
@@ -470,7 +463,6 @@
 
 static kobj_method_t eschan1371_methods[] = {
     	KOBJMETHOD(channel_init,		eschan_init),
-    	KOBJMETHOD(channel_setdir,		eschan_setdir),
     	KOBJMETHOD(channel_setformat,		eschan_setformat),
     	KOBJMETHOD(channel_setspeed,		eschan1371_setspeed),
     	KOBJMETHOD(channel_setblocksize,	eschan_setblocksize),
@@ -515,8 +507,26 @@
 static int
 es1370_init(struct es_info *es)
 {
+	int r;
+
+	/* XXX ES1370 default to fixed rate operation */
+	if (resource_int_value(device_get_name(es->dev),
+			device_get_unit(es->dev), "fixed_rate", &r) == 0) {
+		if (r != 0) {
+			if (r < es_caps.minspeed)
+				r = es_caps.minspeed;
+			if (r > es_caps.maxspeed)
+				r = es_caps.maxspeed;
+		}
+	} else
+		r = es_caps.maxspeed;
+	es->caps = es_caps;
+	if (r != 0) {
+		es->caps.minspeed = r;
+		es->caps.maxspeed = r;
+	}
 	es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
-		(DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
+		(DAC2_SRTODIV(es->caps.maxspeed) << CTRL_SH_PCLKDIV);
 	es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
 
 	es->sctrl = 0;
@@ -534,16 +544,17 @@
 
 /* ES1371 specific */
 int
-es1371_init(struct es_info *es, device_t dev)
+es1371_init(struct es_info *es)
 {
 	u_long cssr;
 	int idx;
-	int devid = pci_get_devid(dev);
-	int revid = pci_get_revid(dev);
+	int devid = pci_get_devid(es->dev);
+	int revid = pci_get_revid(es->dev);
 
 	es->num = 0;
 	es->ctrl = 0;
 	es->sctrl = 0;
+	es->caps = es_caps;
 	cssr = 0;
 	if (devid == CT4730_PCI_ID) {
 		/* XXX amplifier hack? */
@@ -870,7 +881,7 @@
 
 #ifdef SND_DYNSYSCTL
 static int
-sysctl_es1371x_spdif_enable(SYSCTL_HANDLER_ARGS)
+sysctl_es137x_spdif_enable(SYSCTL_HANDLER_ARGS)
 {
 	struct es_info *es;
 	device_t dev;
@@ -907,7 +918,7 @@
 }
 
 static int
-sysctl_es1371x_latency_timer(SYSCTL_HANDLER_ARGS)
+sysctl_es137x_latency_timer(SYSCTL_HANDLER_ARGS)
 {
 	struct es_info *es;
 	device_t dev;
@@ -930,6 +941,43 @@
 	snd_mtxunlock(es->lock);
 	return 0;
 }
+
+static int
+sysctl_es137x_fixed_rate(SYSCTL_HANDLER_ARGS)
+{
+	struct es_info *es;
+	device_t dev;
+	int err, val;
+
+	dev = oidp->oid_arg1;
+	es = pcm_getdevinfo(dev);
+	snd_mtxlock(es->lock);
+	if (es->caps.minspeed == es->caps.maxspeed)
+		val = es->caps.maxspeed;
+	else
+		val = 0;
+	snd_mtxunlock(es->lock);
+	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
+	
+	if (err || req->newptr == NULL)
+		return err;
+	if (val != 0 && (val < es_caps.minspeed || val> es_caps.maxspeed))
+		return EINVAL;
+
+	snd_mtxlock(es->lock);
+	if (val) {
+		es->caps.minspeed = val;
+		es->caps.maxspeed = val;
+		es->ctrl &= ~CTRL_PCLKDIV;
+		es->ctrl |= DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV;
+		es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4);
+	} else {
+		es->caps.minspeed = es_caps.minspeed;
+		es->caps.maxspeed = es_caps.maxspeed;
+	}
+	snd_mtxunlock(es->lock);
+	return 0;
+}
 #endif /* SND_DYNSYSCTL */
 
 static void
@@ -953,8 +1001,15 @@
 				SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
 				OID_AUTO, "spdif_enabled",
 				CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
-				sysctl_es1371x_spdif_enable, "I",
+				sysctl_es137x_spdif_enable, "I",
 				"Enable S/PDIF output on primary playback channel");
+	} else if (devid == ES1370_PCI_ID) {
+		SYSCTL_ADD_PROC(snd_sysctl_tree(dev),
+				SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
+				OID_AUTO, "fixed_rate",
+				CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
+				sysctl_es137x_fixed_rate, "I",
+				"Enable fixed rate playback/recording");
 	}
 	if (resource_int_value(device_get_name(dev),
 			device_get_unit(dev), "latency_timer", &r) == 0 &&
@@ -964,7 +1019,7 @@
 			SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
 			OID_AUTO, "latency_timer",
 			CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
-			sysctl_es1371x_latency_timer, "I",
+			sysctl_es137x_latency_timer, "I",
 			"PCI Latency Timer configuration");
 #endif /* SND_DYNSYSCTL */
 }
@@ -1025,7 +1080,7 @@
 
 	if (devid == ES1371_PCI_ID || devid == ES1371_PCI_ID2 ||
 	    devid == CT5880_PCI_ID || devid == CT4730_PCI_ID) {
-		if(-1 == es1371_init(es, dev)) {
+		if(-1 == es1371_init(es)) {
 			device_printf(dev, "unable to initialize the card\n");
 			goto bad;
 		}


More information about the freebsd-multimedia mailing list