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 *);