Subject: port-i386/1687: SoundBlaster driver mixer needs some help; input filter modes
To: None <gnats-bugs@gnats.netbsd.org>
From: John Kohl <jtk@kolvir.arlington.ma.us>
List: netbsd-bugs
Date: 10/24/1995 22:02:00
>Number:         1687
>Category:       port-i386
>Synopsis:       SoundBlaster driver mixer needs some help; input filter modes
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Oct 24 22:50:01 1995
>Last-Modified:
>Originator:     John Kohl
>Organization:
NetBSD Kernel Hackers `R` Us
>Release:        NetBSD-current, 1995/10/24
>Environment:
	
System: NetBSD pattern 1.1_ALPHA NetBSD 1.1_ALPHA (PATTERN) #195: Sun Oct 22 13:31:09 EDT 1995 jtk@pattern:/u1/NetBSD-current/src/sys/arch/i386/compile/PATTERN i386


>Description:
	The SoundBlaster driver doesn't quite do the right thing with
the mixer ioctls, leading programs to conclude it has far fewer
adjustable parameters than it really does.

>How-To-Repeat:
Try to run xmixer on a SBPro system.
>Fix:
Here's a patch to rework the mixer device ordering, and add the ability
to tweak the high & low band input filter settings.

===================================================================
RCS file: RCS/sbdsp.c,v
retrieving revision 1.1
diff -c -r1.1 sbdsp.c
*** sbdsp.c	1995/10/24 23:28:32	1.1
--- sbdsp.c	1995/10/25 01:47:51
***************
*** 162,172 ****
  		sbdsp_mix_write(sc, SBP_DAC_VOL,
  				sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL));
  		sbdsp_mix_write(sc, SBP_MASTER_VOL,
! 				sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL));
  		sbdsp_mix_write(sc, SBP_LINE_VOL,
  				sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL));
  		for (i = 0; i < SB_NDEVS; i++)
  		    sc->gain[i] = sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL);
  	}
  	printf(": dsp v%d.%02d\n",
  	       SBVER_MAJOR(sc->sc_model), SBVER_MINOR(sc->sc_model));
--- 162,173 ----
  		sbdsp_mix_write(sc, SBP_DAC_VOL,
  				sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL));
  		sbdsp_mix_write(sc, SBP_MASTER_VOL,
! 				sbdsp_stereo_vol(SBP_MAXVOL/2, SBP_MAXVOL/2));
  		sbdsp_mix_write(sc, SBP_LINE_VOL,
  				sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL));
  		for (i = 0; i < SB_NDEVS; i++)
  		    sc->gain[i] = sbdsp_stereo_vol(SBP_MAXVOL, SBP_MAXVOL);
+ 		sc->in_filter = 0;	/* no filters turned on, please */
  	}
  	printf(": dsp v%d.%02d\n",
  	       SBVER_MAJOR(sc->sc_model), SBVER_MINOR(sc->sc_model));
***************
*** 398,403 ****
--- 399,457 ----
  }
  
  int
+ sbdsp_set_ifilter(addr, which)
+ 	void *addr;
+ 	int which;
+ {
+ 	register struct sbdsp_softc *sc = addr;
+ 	int rval, mixval;
+ 
+ 	if (ISSBPROCLASS(sc)) {
+ 		mixval = sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_IFILTER_MASK;
+ 		switch (which) {
+ 		default:
+ 		    return EINVAL;
+ 		case 0:
+ 		    mixval |= SBP_FILTER_OFF;
+ 		    break;
+ 		case SBP_TREBLE_EQ:
+ 		    mixval |= (SBP_FILTER_ON|SBP_IFILTER_HIGH);
+ 		    break;
+ 		case SBP_BASS_EQ:
+ 		    mixval |= (SBP_FILTER_ON|SBP_IFILTER_LOW); 
+ 		    break;
+ 		}
+ 		sc->in_filter = mixval & SBP_IFILTER_MASK;
+ 		sbdsp_mix_write(sc, SBP_INFILTER, mixval);
+ 		return(0);
+ 	} else {
+ 		return(EINVAL);
+ 	}
+ }
+ 
+ int
+ sbdsp_get_ifilter(addr)
+ 	void *addr;
+ {
+ 	register struct sbdsp_softc *sc = addr;
+ 	
+ 	if (ISSBPROCLASS(sc)) {
+ 	    sc->in_filter = sbdsp_mix_read(sc, SBP_INFILTER) & SBP_IFILTER_MASK;
+ 
+ 	    switch (sc->in_filter) {
+ 	    case SBP_FILTER_OFF:
+ 	    default:
+ 		return 0;
+ 	    case SBP_FILTER_ON|SBP_IFILTER_HIGH:
+ 		return SBP_TREBLE_EQ;
+ 	    case SBP_FILTER_ON|SBP_IFILTER_LOW:
+ 		return SBP_BASS_EQ;
+ 	    }
+ 	} else
+ 	    return 0;
+ }
+ 
+ int
  sbdsp_set_out_port(addr, port)
  	void *addr;
  	int port;
***************
*** 466,472 ****
  		/* record from that port */
  		sbdsp_mix_write(sc, SBP_RECORD_SOURCE,
  				SBP_RECORD_FROM(sbport, SBP_FILTER_OFF,
! 						SBP_FILTER_HIGH));
  		/* fetch gain from that port */
  		sc->gain[port] = sbdsp_mix_read(sc, mixport);
  	}
--- 520,526 ----
  		/* record from that port */
  		sbdsp_mix_write(sc, SBP_RECORD_SOURCE,
  				SBP_RECORD_FROM(sbport, SBP_FILTER_OFF,
! 						SBP_IFILTER_HIGH));
  		/* fetch gain from that port */
  		sc->gain[port] = sbdsp_mix_read(sc, mixport);
  	}
***************
*** 959,965 ****
  			sbdsp_mix_write(sc, SBP_STEREO,
  					sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK);
  			sbdsp_mix_write(sc, SBP_INFILTER,
! 					sbdsp_mix_read(sc, SBP_INFILTER) | SBP_FILTER_OFF);
  		}
  		else {
  			if (sbdsp_wdsp(iobase, SB_DSP_RECORD_MONO) < 0)
--- 1013,1019 ----
  			sbdsp_mix_write(sc, SBP_STEREO,
  					sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK);
  			sbdsp_mix_write(sc, SBP_INFILTER,
! 					(sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_IFILTER_MASK) | SBP_FILTER_OFF);
  		}
  		else {
  			if (sbdsp_wdsp(iobase, SB_DSP_RECORD_MONO) < 0)
***************
*** 967,975 ****
  			sbdsp_mix_write(sc, SBP_STEREO,
  					sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK);
  			sbdsp_mix_write(sc, SBP_INFILTER,
! 					sc->sc_irate <= 8000 ? 
! 					sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_FILTER_MASK :
! 					sbdsp_mix_read(sc, SBP_INFILTER) | SBP_FILTER_OFF);
  		}
  		sc->sc_dmain_inprogress = 1;
  		sc->sc_last_hsr_size = 0;	/* restarting */
--- 1021,1027 ----
  			sbdsp_mix_write(sc, SBP_STEREO,
  					sbdsp_mix_read(sc, SBP_STEREO) & ~SBP_PLAYMODE_MASK);
  			sbdsp_mix_write(sc, SBP_INFILTER,
! 					(sbdsp_mix_read(sc, SBP_INFILTER) & ~SBP_IFILTER_MASK) | sc->in_filter);
  		}
  		sc->sc_dmain_inprogress = 1;
  		sc->sc_last_hsr_size = 0;	/* restarting */
***************
*** 1230,1241 ****
      DPRINTF(("sbdsp_mixer_set_port: port=%d num_channels=%d\n", cp->dev, cp->un.value.num_channels));
  
      /*
!      * Everything is a value except for SBPro special OUTPUT_MODE and
       * RECORD_SOURCE
       */
      if (cp->type != AUDIO_MIXER_VALUE) {
! 	if (!ISSBPROCLASS(sc) || (cp->dev != SB_OUTPUT_MODE &&
! 				  cp->dev != SB_RECORD_SOURCE))
  	    return EINVAL;
      }
      else {
--- 1282,1294 ----
      DPRINTF(("sbdsp_mixer_set_port: port=%d num_channels=%d\n", cp->dev, cp->un.value.num_channels));
  
      /*
!      * Everything is a value except for SBPro BASS/TREBLE and
       * RECORD_SOURCE
       */
      if (cp->type != AUDIO_MIXER_VALUE) {
! 	if (!ISSBPROCLASS(sc) || (cp->dev != SB_RECORD_SOURCE &&
! 				  cp->dev != SB_BASS &&
! 				  cp->dev != SB_TREBLE))
  	    return EINVAL;
      }
      else {
***************
*** 1244,1254 ****
  	 * If we get a single-channel gain value passed in, then we
  	 * duplicate it to both left and right channels.
  	 */
!     if (cp->un.value.num_channels == 2) {
! 	left  = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 	right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
!     }
!     else
  	    left = right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
      }    
      
--- 1297,1307 ----
  	 * If we get a single-channel gain value passed in, then we
  	 * duplicate it to both left and right channels.
  	 */
! 	if (cp->un.value.num_channels == 2) {
! 	    left  = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 	    right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
! 	}
! 	else
  	    left = right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
      }    
      
***************
*** 1286,1303 ****
          case SB_MASTER_VOL:
  	    src = SBP_MASTER_VOL;
  	    break;
! #if 0
! 	case SB_OUTPUT_MODE:
! 	    if (cp->type == AUDIO_MIXER_ENUM)
! 		return sbdsp_set_channels(addr, cp->un.ord);
! 	    /* fall through...carefully! */
! #endif
  	case SB_RECORD_SOURCE:
  	    if (cp->type == AUDIO_MIXER_ENUM)
  		return sbdsp_set_in_port(addr, cp->un.ord);
  	    /* else fall through: bad input */
-         case SB_TREBLE:
-         case SB_BASS:
          default:
  	    error =  EINVAL;
  	    break;
--- 1339,1360 ----
          case SB_MASTER_VOL:
  	    src = SBP_MASTER_VOL;
  	    break;
!         case SB_TREBLE:
! 	    if (cp->type == AUDIO_MIXER_ENUM) {
! 		return sbdsp_set_ifilter(addr, cp->un.ord ? SBP_TREBLE_EQ : 0);
! 	    }
! 	    error = EINVAL;
! 	    break;
!         case SB_BASS:
! 	    if (cp->type == AUDIO_MIXER_ENUM) {
! 		return sbdsp_set_ifilter(addr, cp->un.ord ? SBP_BASS_EQ : 0);
! 	    }
! 	    error = EINVAL;
! 	    break;
  	case SB_RECORD_SOURCE:
  	    if (cp->type == AUDIO_MIXER_ENUM)
  		return sbdsp_set_in_port(addr, cp->un.ord);
  	    /* else fall through: bad input */
          default:
  	    error =  EINVAL;
  	    break;
***************
*** 1327,1339 ****
      DPRINTF(("sbdsp_mixer_get_port: port=%d", cp->dev));
  
      if (ISSBPROCLASS(sc))
!     switch(cp->dev) {
!     case SB_MIC_PORT:
  	    if (cp->un.value.num_channels == 1) {
  		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
  		    SBP_MICGAIN_TO_AGAIN(sc->gain[cp->dev]);
  		return 0;
!     }
  	    else
  		return EINVAL;
  	    break;
--- 1384,1396 ----
      DPRINTF(("sbdsp_mixer_get_port: port=%d", cp->dev));
  
      if (ISSBPROCLASS(sc))
! 	switch(cp->dev) {
! 	case SB_MIC_PORT:
  	    if (cp->un.value.num_channels == 1) {
  		cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
  		    SBP_MICGAIN_TO_AGAIN(sc->gain[cp->dev]);
  		return 0;
! 	    }
  	    else
  		return EINVAL;
  	    break;
***************
*** 1346,1351 ****
--- 1403,1420 ----
  	case SB_SPEAKER:
  	    cp->dev = SB_MASTER_VOL;
  	    break;
+ 	case SB_TREBLE:
+ 	    if (sbdsp_get_ifilter(sc) == SBP_TREBLE_EQ)
+ 		cp->un.ord = 1;
+ 	    else
+ 		cp->un.ord = 0;
+ 	    return 0;
+ 	case SB_BASS:
+ 	    if (sbdsp_get_ifilter(sc) == SBP_BASS_EQ)
+ 		cp->un.ord = 1;
+ 	    else
+ 		cp->un.ord = 0;
+ 	    return 0;
          default:
  	    error =  EINVAL;
  	    break;
***************
*** 1353,1359 ****
      else {
  	if (cp->un.value.num_channels != 1) /* no stereo on SB classic */
  	    error = EINVAL;
!     else
  	    switch(cp->dev) {
  	    case SB_MIC_PORT:
  		break;
--- 1422,1428 ----
      else {
  	if (cp->un.value.num_channels != 1) /* no stereo on SB classic */
  	    error = EINVAL;
! 	else
  	    switch(cp->dev) {
  	    case SB_MIC_PORT:
  		break;
***************
*** 1441,1447 ****
  	    break;
  	case SB_DAC_PORT:
  	    dip->type = AUDIO_MIXER_VALUE;
! 	    dip->mixer_class = SB_OUTPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
  	    dip->next = AUDIO_MIXER_LAST;
  	    strcpy(dip->label.name, AudioNdac);
--- 1510,1516 ----
  	    break;
  	case SB_DAC_PORT:
  	    dip->type = AUDIO_MIXER_VALUE;
! 	    dip->mixer_class = SB_INPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
  	    dip->next = AUDIO_MIXER_LAST;
  	    strcpy(dip->label.name, AudioNdac);
***************
*** 1459,1465 ****
  	    break;
  	case SB_FM_PORT:
  	    dip->type = AUDIO_MIXER_VALUE;
! 	    dip->mixer_class = SB_OUTPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
  	    dip->next = AUDIO_MIXER_LAST;
  	    strcpy(dip->label.name, AudioNfmsynth);
--- 1528,1534 ----
  	    break;
  	case SB_FM_PORT:
  	    dip->type = AUDIO_MIXER_VALUE;
! 	    dip->mixer_class = SB_INPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
  	    dip->next = AUDIO_MIXER_LAST;
  	    strcpy(dip->label.name, AudioNfmsynth);
***************
*** 1470,1494 ****
  	    dip->type = AUDIO_MIXER_VALUE;
  	    dip->mixer_class = SB_OUTPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
! 	    dip->next = /*TREBLE, BASS not handled, nor is SB_OUTPUT_MODE*/SB_RECORD_SOURCE;
  	    strcpy(dip->label.name, AudioNvolume);
  	    dip->un.v.num_channels = 2;
  	    strcpy(dip->un.v.units.name, AudioNvolume);
  	    break;
- #if 0
- 	case SB_OUTPUT_MODE:
- 	    dip->mixer_class = SB_OUTPUT_CLASS;
- 	    dip->type = AUDIO_MIXER_ENUM;
- 	    dip->prev = SB_MASTER_VOL;
- 	    dip->next = AUDIO_MIXER_LAST;
- 	    strcpy(dip->label.name, AudioNmode);
- 	    dip->un.e.num_mem = 2;
- 	    strcpy(dip->un.e.member[0].label.name, AudioNmono);
- 	    dip->un.e.member[0].ord = 1; /* nchans */
- 	    strcpy(dip->un.e.member[1].label.name, AudioNstereo);
- 	    dip->un.e.member[1].ord = 2; /* nchans */
- 	    break;
- #endif
  	case SB_RECORD_SOURCE:
  	    dip->mixer_class = SB_RECORD_CLASS;
  	    dip->type = AUDIO_MIXER_ENUM;
--- 1539,1549 ----
  	    dip->type = AUDIO_MIXER_VALUE;
  	    dip->mixer_class = SB_OUTPUT_CLASS;
  	    dip->prev = AUDIO_MIXER_LAST;
! 	    dip->next = AUDIO_MIXER_LAST;
  	    strcpy(dip->label.name, AudioNvolume);
  	    dip->un.v.num_channels = 2;
  	    strcpy(dip->un.v.units.name, AudioNvolume);
  	    break;
  	case SB_RECORD_SOURCE:
  	    dip->mixer_class = SB_RECORD_CLASS;
  	    dip->type = AUDIO_MIXER_ENUM;
***************
*** 1504,1510 ****
--- 1559,1595 ----
  	    dip->un.e.member[2].ord = SB_LINE_IN_PORT;
  	    break;
  	case SB_BASS:
+ 	    dip->type = AUDIO_MIXER_ENUM;
+ 	    dip->mixer_class = SB_INPUT_CLASS;
+ 	    dip->prev = AUDIO_MIXER_LAST;
+ 	    dip->next = AUDIO_MIXER_LAST;
+ 	    strcpy(dip->label.name, AudioNtreble);
+ 	    dip->un.e.num_mem = 2;
+ 	    strcpy(dip->un.e.member[0].label.name, AudioNoff);
+ 	    dip->un.e.member[0].ord = 0;
+ 	    strcpy(dip->un.e.member[1].label.name, AudioNon);
+ 	    dip->un.e.member[1].ord = 1;
+ 	    break;
  	case SB_TREBLE:
+ 	    dip->type = AUDIO_MIXER_ENUM;
+ 	    dip->mixer_class = SB_INPUT_CLASS;
+ 	    dip->prev = AUDIO_MIXER_LAST;
+ 	    dip->next = AUDIO_MIXER_LAST;
+ 	    strcpy(dip->label.name, AudioNbass);
+ 	    dip->un.e.num_mem = 2;
+ 	    strcpy(dip->un.e.member[0].label.name, AudioNoff);
+ 	    dip->un.e.member[0].ord = 0;
+ 	    strcpy(dip->un.e.member[1].label.name, AudioNon);
+ 	    dip->un.e.member[1].ord = 1;
+ 	    break;
+ 
+ 	case SB_RECORD_CLASS:			/* record source class */
+ 	    dip->type = AUDIO_MIXER_CLASS;
+ 	    dip->mixer_class = SB_RECORD_CLASS;
+ 	    dip->next = dip->prev = AUDIO_MIXER_LAST;
+ 	    strcpy(dip->label.name, AudioCRecord);
+ 	    break;
+ 
  	default:
  	    return ENXIO;
  	    /*NOTREACHED*/
===================================================================
RCS file: RCS/sbdspvar.h,v
retrieving revision 1.1
diff -c -r1.1 sbdspvar.h
*** sbdspvar.h	1995/10/24 23:38:25	1.1
--- sbdspvar.h	1995/10/25 02:00:07
***************
*** 36,59 ****
  
  #define SB_MIC_PORT	0
  #define SB_SPEAKER	1
! #define SB_LINE_IN_PORT	2
! #define SB_DAC_PORT	3
! #define SB_FM_PORT	4
! #define SB_CD_PORT	5
! #define SB_MASTER_VOL	6
! #define SB_TREBLE	7
! #define SB_BASS		8
! #define SB_NDEVS	9
  
! #define SB_OUTPUT_MODE	9
  #define 	SB_SPKR_MONO	0
! #define 	SB_SPKR_STEREO	1
  
! #define	SB_RECORD_SOURCE 10
  
! #define SB_INPUT_CLASS	11
! #define SB_OUTPUT_CLASS	12
! #define SB_RECORD_CLASS	13
  
  
  /*
--- 36,61 ----
  
  #define SB_MIC_PORT	0
  #define SB_SPEAKER	1
! #define SB_INPUT_CLASS	2
! #define SB_OUTPUT_CLASS	3
! #define SB_LINE_IN_PORT	4
! #define SB_DAC_PORT	5
! #define SB_FM_PORT	6
! #define SB_CD_PORT	7
! #define SB_MASTER_VOL	8
! #define SB_TREBLE	9
! #define SB_BASS		10
! #define SB_NDEVS	11		/* XXX include classes above for
! 					   contiguous number space on
! 					   original SB */
  
! /*#define SB_OUTPUT_MODE	9
  #define 	SB_SPKR_MONO	0
! #define 	SB_SPKR_STEREO	1*/
  
! #define	SB_RECORD_SOURCE 11
  
! #define SB_RECORD_CLASS	12
  
  
  /*
***************
*** 93,98 ****
--- 95,101 ----
  
  	u_int	out_port;		/* output port */
  	u_int	in_port;		/* input port */
+ 	u_int	in_filter;		/* one of SB_TREBLE_EQ, SB_BASS_EQ, 0 */
  
  	u_int	spkr_state;		/* non-null is on */
  	
***************
*** 156,161 ****
--- 159,166 ----
  int	sbdsp_get_precision __P((void *));
  int	sbdsp_set_channels __P((void *, int));
  int	sbdsp_get_channels __P((void *));
+ int	sbdsp_set_ifilter __P((void *, int));
+ int	sbdsp_get_ifilter __P((void *));
  int	sbdsp_round_blocksize __P((void *, int));
  int	sbdsp_set_out_port __P((void *, int));
  int	sbdsp_get_out_port __P((void *));

>Audit-Trail:
>Unformatted: