Module Name:    src
Committed By:   isaki
Date:           Wed Jun 26 06:57:45 UTC 2019

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

Log Message:
Improve audiobell (and interfaces with audio).
- Generate pseudo sine wave if possible.  It may improve timbre.
  If it cannot represent a sine wave, it falls back to a triangular
  wave or a rectangular wave.
- Volume adjustment.
- Calculate playback frequency based on mixer frequency.
  Now audiobellopen() initializes playback parameters other than
  sample_rate, and new audiobellsetrate() sets sample_rate.


To generate a diff of this commit:
cvs rdiff -u -r1.20 -r1.21 src/sys/dev/audio/audio.c
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/audio/audiobell.c
cvs rdiff -u -r1.5 -r1.6 src/sys/dev/audio/audiodef.h
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/audio/audiovar.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.20 src/sys/dev/audio/audio.c:1.21
--- src/sys/dev/audio/audio.c:1.20	Tue Jun 25 13:07:48 2019
+++ src/sys/dev/audio/audio.c	Wed Jun 26 06:57:45 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.20 2019/06/25 13:07:48 isaki Exp $	*/
+/*	$NetBSD: audio.c,v 1.21 2019/06/26 06:57:45 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.20 2019/06/25 13:07:48 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.21 2019/06/26 06:57:45 isaki Exp $");
 
 #ifdef _KERNEL_OPT
 #include "audio.h"
@@ -458,28 +458,6 @@ audio_track_bufstat(audio_track_t *track
 #define SPECIFIED(x)	((x) != ~0)
 #define SPECIFIED_CH(x)	((x) != (u_char)~0)
 
-/*
- * AUDIO_SCALEDOWN()
- * This macro should be used for audio wave data only.
- *
- * The arithmetic shift right (ASR) (in other words, floor()) is good for
- * this purpose, and will be faster than division on the most platform.
- * The division (in other words, truncate()) is not so bad alternate for
- * this purpose, and will be fast enough.
- * (Using ASR is 1.9 times faster than division on my amd64, and 1.3 times
- * faster on my m68k.  -- isaki 201801.)
- *
- * However, the right shift operator ('>>') for negative integer is
- * "implementation defined" behavior in C (note that it's not "undefined"
- * behavior).  So only if implementation defines '>>' as ASR, we use it.
- */
-#if defined(__GNUC__)
-/* gcc defines '>>' as ASR. */
-#define AUDIO_SCALEDOWN(value, bits)	((value) >> (bits))
-#else
-#define AUDIO_SCALEDOWN(value, bits)	((value) / (1 << (bits)))
-#endif
-
 /* Device timeout in msec */
 #define AUDIO_TIMEOUT	(3000)
 
@@ -539,7 +517,7 @@ static void filt_audioread_detach(struct
 static int  filt_audioread_event(struct knote *, long);
 
 static int audio_open(dev_t, struct audio_softc *, int, int, struct lwp *,
-	struct audiobell_arg *);
+	audio_file_t **);
 static int audio_close(struct audio_softc *, audio_file_t *);
 static int audio_read(struct audio_softc *, struct uio *, int, audio_file_t *);
 static int audio_write(struct audio_softc *, struct uio *, int, audio_file_t *);
@@ -1782,14 +1760,11 @@ audiommap(struct file *fp, off_t *offp, 
 
 /*
  * Open for audiobell.
- * sample_rate, encoding, precision and channels in arg are in-parameter
- * and indicates input encoding.
- * Stores allocated file to arg->file.
- * Stores blocksize to arg->blocksize.
+ * It stores allocated file to *filep.
  * If successful returns 0, otherwise errno.
  */
 int
-audiobellopen(dev_t dev, struct audiobell_arg *arg)
+audiobellopen(dev_t dev, audio_file_t **filep)
 {
 	struct audio_softc *sc;
 	int error;
@@ -1804,7 +1779,7 @@ audiobellopen(dev_t dev, struct audiobel
 		return error;
 
 	device_active(sc->sc_dev, DVA_SYSTEM);
-	error = audio_open(dev, sc, FWRITE, 0, curlwp, arg);
+	error = audio_open(dev, sc, FWRITE, 0, curlwp, filep);
 
 	audio_exit_exclusive(sc);
 	return error;
@@ -1830,6 +1805,28 @@ audiobellclose(audio_file_t *file)
 	return error;
 }
 
+/* Set sample rate for audiobell */
+int
+audiobellsetrate(audio_file_t *file, u_int sample_rate)
+{
+	struct audio_softc *sc;
+	struct audio_info ai;
+	int error;
+
+	sc = file->sc;
+
+	AUDIO_INITINFO(&ai);
+	ai.play.sample_rate = sample_rate;
+
+	error = audio_enter_exclusive(sc);
+	if (error)
+		return error;
+	error = audio_file_setinfo(sc, file, &ai);
+	audio_exit_exclusive(sc);
+
+	return error;
+}
+
 /* Playback for audiobell */
 int
 audiobellwrite(audio_file_t *file, struct uio *uio)
@@ -1848,7 +1845,7 @@ audiobellwrite(audio_file_t *file, struc
  */
 int
 audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
-	struct lwp *l, struct audiobell_arg *bell)
+	struct lwp *l, audio_file_t **bellfile)
 {
 	struct audio_info ai;
 	struct file *fp;
@@ -1912,11 +1909,12 @@ audio_open(dev_t dev, struct audio_softc
 
 	/* Set parameters */
 	AUDIO_INITINFO(&ai);
-	if (bell) {
-		ai.play.sample_rate   = bell->sample_rate;
-		ai.play.encoding      = bell->encoding;
-		ai.play.channels      = bell->channels;
-		ai.play.precision     = bell->precision;
+	if (bellfile) {
+		/* If audiobell, only sample_rate will be set later. */
+		ai.play.sample_rate   = audio_default.sample_rate;
+		ai.play.encoding      = AUDIO_ENCODING_SLINEAR_NE;
+		ai.play.channels      = 1;
+		ai.play.precision     = 16;
 		ai.play.pause         = false;
 	} else if (ISDEVAUDIO(dev)) {
 		/* If /dev/audio, initialize everytime. */
@@ -2041,7 +2039,7 @@ audio_open(dev_t dev, struct audio_softc
 		}
 	}
 
-	if (bell == NULL) {
+	if (bellfile == NULL) {
 		error = fd_allocfile(&fp, &fd);
 		if (error)
 			goto bad3;
@@ -2059,8 +2057,8 @@ audio_open(dev_t dev, struct audio_softc
 	SLIST_INSERT_HEAD(&sc->sc_files, af, entry);
 	mutex_exit(sc->sc_intr_lock);
 
-	if (bell) {
-		bell->file = af;
+	if (bellfile) {
+		*bellfile = af;
 	} else {
 		error = fd_clone(fp, fd, flags, &audio_fileops, af);
 		KASSERT(error == EMOVEFD);

Index: src/sys/dev/audio/audiobell.c
diff -u src/sys/dev/audio/audiobell.c:1.2 src/sys/dev/audio/audiobell.c:1.3
--- src/sys/dev/audio/audiobell.c:1.2	Wed May  8 13:40:17 2019
+++ src/sys/dev/audio/audiobell.c	Wed Jun 26 06:57:45 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiobell.c,v 1.2 2019/05/08 13:40:17 isaki Exp $	*/
+/*	$NetBSD: audiobell.c,v 1.3 2019/06/26 06:57:45 isaki Exp $	*/
 
 /*
  * Copyright (c) 1999 Richard Earnshaw
@@ -31,7 +31,7 @@
  */
 
 #include <sys/types.h>
-__KERNEL_RCSID(0, "$NetBSD: audiobell.c,v 1.2 2019/05/08 13:40:17 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audiobell.c,v 1.3 2019/06/26 06:57:45 isaki Exp $");
 
 #include <sys/audioio.h>
 #include <sys/conf.h>
@@ -45,8 +45,40 @@ __KERNEL_RCSID(0, "$NetBSD: audiobell.c,
 #include <dev/audio/audiodef.h>
 #include <dev/audio/audiobellvar.h>
 
-/* 44.1 kHz should reduce hum at higher pitches. */
-#define BELL_SAMPLE_RATE	44100
+/*
+ * The hexadecagon is sufficiently close to a sine wave.
+ * Audiobell always outputs this 16 points data but changes its playback
+ * frequency.  In addition, audio layer does linear interpolation in the
+ * frequency conversion stage, so the waveform becomes smooth.
+ * When the playback frequency rises (or the device frequency is not enough
+ * high) and one wave cannot be expressed with 16 points, the data is thinned
+ * out by power of two, like 8 points -> 4 points (triangular wave)
+ * -> 2 points (rectangular wave).
+ */
+
+/* Amplitude.  Full scale amplitude is too loud. */
+#define A(x) ((x) * 0.6)
+
+/* (sin(2*pi * (x/16)) * 32767 / 100) << 16 */
+static const int32_t sinewave[] = {
+	A(        0),
+	A(  8217813),
+	A( 15184539),
+	A( 19839556),
+	A( 21474181),
+	A( 19839556),
+	A( 15184539),
+	A(  8217813),
+	A(        0),
+	A( -8217814),
+	A(-15184540),
+	A(-19839557),
+	A(-21474182),
+	A(-19839557),
+	A(-15184540),
+	A( -8217814),
+};
+#undef A
 
 /*
  * dev is a device_t for the audio device to use.
@@ -60,18 +92,22 @@ audiobell(void *dev, u_int pitch, u_int 
 {
 	dev_t audio;
 	int16_t *buf;
-	struct audiobell_arg bellarg;
 	audio_file_t *file;
 	audio_track_t *ptrack;
 	struct uio auio;
 	struct iovec aiov;
-	int i;
-	int remaincount;
-	int remainlen;
-	int wave1count;
-	int wave1len;
-	int len;
-	int16_t vol;
+	u_int i;
+	u_int j;
+	u_int remaincount;
+	u_int remainbytes;
+	u_int wave1count;
+	u_int wave1bytes;
+	u_int blkbytes;
+	u_int len;
+	u_int step;
+	u_int offset;
+	u_int play_sample_rate;
+	u_int mixer_sample_rate;
 
 	KASSERT(volume <= 100);
 
@@ -79,53 +115,69 @@ audiobell(void *dev, u_int pitch, u_int 
 	if (poll)
 		return;
 
-	/* Limit the pitch from 20Hz to Nyquist frequency. */
-	if (pitch > BELL_SAMPLE_RATE / 2)
-		pitch = BELL_SAMPLE_RATE;
-	if (pitch < 20)
-		pitch = 20;
-
 	buf = NULL;
 	audio = AUDIO_DEVICE | device_unit((device_t)dev);
 
-	memset(&bellarg, 0, sizeof(bellarg));
-	bellarg.encoding = AUDIO_ENCODING_SLINEAR_NE;
-	bellarg.precision = 16;
-	bellarg.channels = 1;
-	bellarg.sample_rate = BELL_SAMPLE_RATE;
-
 	/* If not configured, we can't beep. */
-	if (audiobellopen(audio, &bellarg) != 0)
+	if (audiobellopen(audio, &file) != 0)
 		return;
 
-	file = bellarg.file;
 	ptrack = file->ptrack;
+	mixer_sample_rate = ptrack->mixer->track_fmt.sample_rate;
 
-	/* msec to sample count. */
-	remaincount = period * BELL_SAMPLE_RATE / 1000;
-	remainlen = remaincount * sizeof(int16_t);
+	/* Limit pitch */
+	if (pitch < 20)
+		pitch = 20;
 
-	wave1count = BELL_SAMPLE_RATE / pitch;
-	wave1len = wave1count * sizeof(int16_t);
+	offset = 0;
+	if (pitch <= mixer_sample_rate / 16) {
+		/* 16-point sine wave */
+		step = 1;
+	} else if (pitch <= mixer_sample_rate / 8) {
+		/* 8-point sine wave */
+		step = 2;
+	} else if (pitch <= mixer_sample_rate / 4) {
+		/* 4-point sine wave, aka, triangular wave */
+		step = 4;
+	} else {
+		/* Rectangular wave */
+		if (pitch > mixer_sample_rate / 2)
+			pitch = mixer_sample_rate / 2;
+		step = 8;
+		offset = 4;
+	}
 
-	buf = malloc(wave1len, M_TEMP, M_WAITOK);
+	wave1count = __arraycount(sinewave) / step;
+	play_sample_rate = pitch * wave1count;
+	audiobellsetrate(file, play_sample_rate);
+
+	/* msec to sample count */
+	remaincount = play_sample_rate * period / 1000;
+	/* Roundup to full wave */
+	remaincount = roundup(remaincount, wave1count);
+	remainbytes = remaincount * sizeof(int16_t);
+	wave1bytes = wave1count * sizeof(int16_t);
+
+	blkbytes = ptrack->usrbuf_blksize;
+	blkbytes = rounddown(blkbytes, wave1bytes);
+	blkbytes = uimin(blkbytes, remainbytes);
+	buf = malloc(blkbytes, M_TEMP, M_WAITOK);
 	if (buf == NULL)
 		goto out;
 
-	/* Generate single square wave.  It's enough to beep. */
-	vol = 32767 * volume / 100;
-	for (i = 0; i < wave1count / 2; i++) {
-		buf[i] = vol;
-	}
-	vol = -vol;
-	for (; i < wave1count; i++) {
-		buf[i] = vol;
+	/* Generate sinewave with specified volume */
+	j = offset;
+	for (i = 0; i < blkbytes / sizeof(int16_t); i++) {
+		/* XXX audio already has track volume feature though #if 0 */
+		buf[i] = AUDIO_SCALEDOWN(sinewave[j] * (int)volume, 16);
+		j += step;
+		j %= __arraycount(sinewave);
 	}
 
-	/* Write while paused to avoid begin inserted silence. */
+	/* Write while paused to avoid inserting silence. */
 	ptrack->is_pause = true;
-	for (; remainlen > 0; remainlen -= wave1len) {
-		len = uimin(remainlen, wave1len);
+	for (; remainbytes > 0; remainbytes -= len) {
+		len = uimin(remainbytes, blkbytes);
 		aiov.iov_base = (void *)buf;
 		aiov.iov_len = len;
 		auio.uio_iov = &aiov;

Index: src/sys/dev/audio/audiodef.h
diff -u src/sys/dev/audio/audiodef.h:1.5 src/sys/dev/audio/audiodef.h:1.6
--- src/sys/dev/audio/audiodef.h:1.5	Tue Jun 25 13:07:48 2019
+++ src/sys/dev/audio/audiodef.h	Wed Jun 26 06:57:45 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiodef.h,v 1.5 2019/06/25 13:07:48 isaki Exp $	*/
+/*	$NetBSD: audiodef.h,v 1.6 2019/06/26 06:57:45 isaki Exp $	*/
 
 /*
  * Copyright (C) 2017 Tetsuya Isaki. All rights reserved.
@@ -63,6 +63,28 @@
  */
 /* #define AUDIO_SUPPORT_TRACK_VOLUME */
 
+/*
+ * AUDIO_SCALEDOWN()
+ * This macro should be used for audio wave data only.
+ *
+ * The arithmetic shift right (ASR) (in other words, floor()) is good for
+ * this purpose, and will be faster than division on the most platform.
+ * The division (in other words, truncate()) is not so bad alternate for
+ * this purpose, and will be fast enough.
+ * (Using ASR is 1.9 times faster than division on my amd64, and 1.3 times
+ * faster on my m68k.  -- isaki 201801.)
+ *
+ * However, the right shift operator ('>>') for negative integer is
+ * "implementation defined" behavior in C (note that it's not "undefined"
+ * behavior).  So only if implementation defines '>>' as ASR, we use it.
+ */
+#if defined(__GNUC__)
+/* gcc defines '>>' as ASR. */
+#define AUDIO_SCALEDOWN(value, bits)	((value) >> (bits))
+#else
+#define AUDIO_SCALEDOWN(value, bits)	((value) / (1 << (bits)))
+#endif
+
 /* conversion stage */
 typedef struct {
 	audio_ring_t srcbuf;

Index: src/sys/dev/audio/audiovar.h
diff -u src/sys/dev/audio/audiovar.h:1.3 src/sys/dev/audio/audiovar.h:1.4
--- src/sys/dev/audio/audiovar.h:1.3	Mon Jun 10 13:12:51 2019
+++ src/sys/dev/audio/audiovar.h	Wed Jun 26 06:57:45 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiovar.h,v 1.3 2019/06/10 13:12:51 isaki Exp $	*/
+/*	$NetBSD: audiovar.h,v 1.4 2019/06/26 06:57:45 isaki Exp $	*/
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -318,15 +318,8 @@ audio_format2_endian(const audio_format2
 }
 
 /* Interfaces for audiobell. */
-struct audiobell_arg {
-	u_int sample_rate;	/* IN */
-	u_int encoding;		/* IN */
-	u_int channels;		/* IN */
-	u_int precision;	/* IN */
-	u_int blocksize;	/* OUT */
-	audio_file_t *file;	/* OUT */
-};
-int audiobellopen(dev_t, struct audiobell_arg *);
+int audiobellopen(dev_t, audio_file_t **);
+int audiobellsetrate(audio_file_t *, u_int);
 int audiobellclose(audio_file_t *);
 int audiobellwrite(audio_file_t *, struct uio *);
 

Reply via email to