Module Name:    src
Committed By:   isaki
Date:           Sat Jul  6 12:58:58 UTC 2019

Modified Files:
        src/sys/dev/audio: audio.c audiodef.h

Log Message:
Implement auto recovery of the mixing volume.


To generate a diff of this commit:
cvs rdiff -u -r1.22 -r1.23 src/sys/dev/audio/audio.c
cvs rdiff -u -r1.6 -r1.7 src/sys/dev/audio/audiodef.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/audio/audio.c
diff -u src/sys/dev/audio/audio.c:1.22 src/sys/dev/audio/audio.c:1.23
--- src/sys/dev/audio/audio.c:1.22	Wed Jun 26 07:47:25 2019
+++ src/sys/dev/audio/audio.c	Sat Jul  6 12:58:58 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.22 2019/06/26 07:47:25 isaki Exp $	*/
+/*	$NetBSD: audio.c,v 1.23 2019/07/06 12:58:58 isaki Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -142,7 +142,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.22 2019/06/26 07:47:25 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.23 2019/07/06 12:58:58 isaki Exp $");
 
 #ifdef _KERNEL_OPT
 #include "audio.h"
@@ -593,6 +593,7 @@ static int audio_mixer_init(struct audio
 static void audio_mixer_destroy(struct audio_softc *, audio_trackmixer_t *);
 static void audio_pmixer_start(struct audio_softc *, bool);
 static void audio_pmixer_process(struct audio_softc *);
+static void audio_pmixer_agc(audio_trackmixer_t *, int);
 static int  audio_pmixer_mix_track(audio_trackmixer_t *, audio_track_t *, int);
 static void audio_pmixer_output(struct audio_softc *);
 static int  audio_pmixer_halt(struct audio_softc *);
@@ -2155,6 +2156,7 @@ audio_close(struct audio_softc *sc, audi
 		if (sc->sc_popens == 0) {
 			mutex_enter(sc->sc_intr_lock);
 			sc->sc_pmixer->volume = 256;
+			sc->sc_pmixer->voltimer = 0;
 			mutex_exit(sc->sc_intr_lock);
 		}
 	}
@@ -4972,60 +4974,33 @@ audio_pmixer_process(struct audio_softc 
 		memset(mixer->mixsample, 0,
 		    frametobyte(&mixer->mixfmt, frame_count));
 	} else {
-		aint2_t ovf_plus;
-		aint2_t ovf_minus;
-		int vol;
-
-		/* Overflow detection */
-		ovf_plus = AINT_T_MAX;
-		ovf_minus = AINT_T_MIN;
-		m = mixer->mixsample;
-		for (i = 0; i < sample_count; i++) {
-			aint2_t val;
-
-			val = *m++;
-			if (val > ovf_plus)
-				ovf_plus = val;
-			else if (val < ovf_minus)
-				ovf_minus = val;
-		}
-
-		/* Master Volume Auto Adjust */
-		vol = mixer->volume;
-		if (ovf_plus > (aint2_t)AINT_T_MAX
-		 || ovf_minus < (aint2_t)AINT_T_MIN) {
-			aint2_t ovf;
-			int vol2;
-
-			/* XXX TODO: Check AINT2_T_MIN ? */
-			ovf = ovf_plus;
-			if (ovf < -ovf_minus)
-				ovf = -ovf_minus;
-
-			/* Turn down the volume if overflow occured. */
-			vol2 = (int)((aint2_t)AINT_T_MAX * 256 / ovf);
-			if (vol2 < vol)
-				vol = vol2;
-
-			if (vol < mixer->volume) {
-				/* Turn down gradually to 128. */
-				if (mixer->volume > 128) {
-					mixer->volume =
-					    (mixer->volume * 95) / 100;
-					TRACE(2,
-					    "auto volume adjust: volume %d",
-					    mixer->volume);
-				}
-			}
+		if (mixed > 1) {
+			/* If there are multiple tracks, do auto gain control */
+			audio_pmixer_agc(mixer, sample_count);
 		}
 
-		/* Apply Master Volume. */
-		if (vol != 256) {
+		/* Apply master volume */
+		if (mixer->volume < 256) {
 			m = mixer->mixsample;
 			for (i = 0; i < sample_count; i++) {
-				*m = AUDIO_SCALEDOWN(*m * vol, 8);
+				*m = AUDIO_SCALEDOWN(*m * mixer->volume, 8);
 				m++;
 			}
+
+			/*
+			 * Recover the volume gradually at the pace of
+			 * several times per second.  If it's too fast, you
+			 * can recognize that the volume changes up and down
+			 * quickly and it's not so comfortable.
+			 */
+			mixer->voltimer += mixer->blktime_n;
+			if (mixer->voltimer * 4 >= mixer->blktime_d) {
+				mixer->volume++;
+				mixer->voltimer = 0;
+#if defined(AUDIO_DEBUG_AGC)
+				TRACE(1, "volume recover: %d", mixer->volume);
+#endif
+			}
 		}
 	}
 
@@ -5069,6 +5044,62 @@ audio_pmixer_process(struct audio_softc 
 }
 
 /*
+ * Do auto gain control.
+ * Must be called sc_intr_lock held.
+ */
+static void
+audio_pmixer_agc(audio_trackmixer_t *mixer, int sample_count)
+{
+	struct audio_softc *sc __unused;
+	aint2_t val;
+	aint2_t maxval;
+	aint2_t minval;
+	aint2_t over_plus;
+	aint2_t over_minus;
+	aint2_t *m;
+	int newvol;
+	int i;
+
+	sc = mixer->sc;
+
+	/* Overflow detection */
+	maxval = AINT_T_MAX;
+	minval = AINT_T_MIN;
+	m = mixer->mixsample;
+	for (i = 0; i < sample_count; i++) {
+		val = *m++;
+		if (val > maxval)
+			maxval = val;
+		else if (val < minval)
+			minval = val;
+	}
+
+	/* Absolute value of overflowed amount */
+	over_plus = maxval - AINT_T_MAX;
+	over_minus = AINT_T_MIN - minval;
+
+	if (over_plus > 0 || over_minus > 0) {
+		if (over_plus > over_minus) {
+			newvol = (int)((aint2_t)AINT_T_MAX * 256 / maxval);
+		} else {
+			newvol = (int)((aint2_t)AINT_T_MIN * 256 / minval);
+		}
+
+		/*
+		 * Change the volume only if new one is smaller.
+		 * Reset the timer even if the volume isn't changed.
+		 */
+		if (newvol <= mixer->volume) {
+			mixer->volume = newvol;
+			mixer->voltimer = 0;
+#if defined(AUDIO_DEBUG_AGC)
+			TRACE(1, "auto volume adjust: %d", mixer->volume);
+#endif
+		}
+	}
+}
+
+/*
  * Mix one track.
  * 'mixed' specifies the number of tracks mixed so far.
  * It returns the number of tracks mixed.  In other words, it returns

Index: src/sys/dev/audio/audiodef.h
diff -u src/sys/dev/audio/audiodef.h:1.6 src/sys/dev/audio/audiodef.h:1.7
--- src/sys/dev/audio/audiodef.h:1.6	Wed Jun 26 06:57:45 2019
+++ src/sys/dev/audio/audiodef.h	Sat Jul  6 12:58:58 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiodef.h,v 1.6 2019/06/26 06:57:45 isaki Exp $	*/
+/*	$NetBSD: audiodef.h,v 1.7 2019/07/06 12:58:58 isaki Exp $	*/
 
 /*
  * Copyright (C) 2017 Tetsuya Isaki. All rights reserved.
@@ -214,6 +214,11 @@ struct audio_trackmixer {
 	 * Must be protected by sc_intr_lock.
 	 */
 	u_int		volume;
+	/*
+	 * Volume recovery timer in auto gain control.
+	 * Must be protected by sc_intr_lock.
+	 */
+	int		voltimer;
 
 	audio_format2_t	mixfmt;
 	void		*mixsample;	/* mixing buf in double-sized int */

Reply via email to