summaryrefslogtreecommitdiff
path: root/sys/dev/audio
diff options
context:
space:
mode:
authorisaki <isaki@NetBSD.org>2019-07-06 12:58:58 +0000
committerisaki <isaki@NetBSD.org>2019-07-06 12:58:58 +0000
commit764e87f03fbbb6bbacaea15036d4064a69e29092 (patch)
treedd7e4797aa181cd9410aa79d4650f6ebf6eb1b3f /sys/dev/audio
parent8766ece22421771db0b9d715343ac8f34ab5edb5 (diff)
Implement auto recovery of the mixing volume.
Diffstat (limited to 'sys/dev/audio')
-rw-r--r--sys/dev/audio/audio.c131
-rw-r--r--sys/dev/audio/audiodef.h7
2 files changed, 87 insertions, 51 deletions
diff --git a/sys/dev/audio/audio.c b/sys/dev/audio/audio.c
index 280e6e28410..3b2ecefe1b7 100644
--- a/sys/dev/audio/audio.c
+++ b/sys/dev/audio/audio.c
@@ -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_softc *, int,
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, audio_file_t *file)
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 *sc)
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 *sc)
}
/*
+ * 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
diff --git a/sys/dev/audio/audiodef.h b/sys/dev/audio/audiodef.h
index 53e9dcc0985..8765bf85353 100644
--- a/sys/dev/audio/audiodef.h
+++ b/sys/dev/audio/audiodef.h
@@ -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 */