Home | History | Annotate | Line # | Download | only in audio
audiobell.c revision 1.2.2.3
      1  1.2.2.3    martin /*	$NetBSD: audiobell.c,v 1.2.2.3 2020/04/13 08:04:18 martin Exp $	*/
      2  1.2.2.2  christos 
      3  1.2.2.2  christos /*
      4  1.2.2.2  christos  * Copyright (c) 1999 Richard Earnshaw
      5  1.2.2.2  christos  * Copyright (c) 2004 Ben Harris
      6  1.2.2.2  christos  *
      7  1.2.2.2  christos  * Redistribution and use in source and binary forms, with or without
      8  1.2.2.2  christos  * modification, are permitted provided that the following conditions
      9  1.2.2.2  christos  * are met:
     10  1.2.2.2  christos  * 1. Redistributions of source code must retain the above copyright
     11  1.2.2.2  christos  *    notice, this list of conditions and the following disclaimer.
     12  1.2.2.2  christos  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.2.2.2  christos  *    notice, this list of conditions and the following disclaimer in the
     14  1.2.2.2  christos  *    documentation and/or other materials provided with the distribution.
     15  1.2.2.2  christos  * 3. All advertising materials mentioning features or use of this software
     16  1.2.2.2  christos  *    must display the following acknowledgement:
     17  1.2.2.2  christos  *	This product includes software developed by the RiscBSD team.
     18  1.2.2.2  christos  * 4. The name of the author may not be used to endorse or promote products
     19  1.2.2.2  christos  *    derived from this software without specific prior written permission.
     20  1.2.2.2  christos  *
     21  1.2.2.2  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  1.2.2.2  christos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  1.2.2.2  christos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  1.2.2.2  christos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  1.2.2.2  christos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  1.2.2.2  christos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  1.2.2.2  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  1.2.2.2  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  1.2.2.2  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  1.2.2.2  christos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  1.2.2.2  christos  */
     32  1.2.2.2  christos 
     33  1.2.2.2  christos #include <sys/types.h>
     34  1.2.2.3    martin __KERNEL_RCSID(0, "$NetBSD: audiobell.c,v 1.2.2.3 2020/04/13 08:04:18 martin Exp $");
     35  1.2.2.2  christos 
     36  1.2.2.2  christos #include <sys/audioio.h>
     37  1.2.2.2  christos #include <sys/conf.h>
     38  1.2.2.2  christos #include <sys/device.h>
     39  1.2.2.2  christos #include <sys/malloc.h>
     40  1.2.2.2  christos #include <sys/systm.h>
     41  1.2.2.2  christos #include <sys/uio.h>
     42  1.2.2.2  christos 
     43  1.2.2.2  christos #include <dev/audio/audio_if.h>
     44  1.2.2.2  christos #include <dev/audio/audiovar.h>
     45  1.2.2.2  christos #include <dev/audio/audiodef.h>
     46  1.2.2.2  christos #include <dev/audio/audiobellvar.h>
     47  1.2.2.2  christos 
     48  1.2.2.3    martin /*
     49  1.2.2.3    martin  * The hexadecagon is sufficiently close to a sine wave.
     50  1.2.2.3    martin  * Audiobell always outputs this 16 points data but changes its playback
     51  1.2.2.3    martin  * frequency.  In addition, audio layer does linear interpolation in the
     52  1.2.2.3    martin  * frequency conversion stage, so the waveform becomes smooth.
     53  1.2.2.3    martin  * When the playback frequency rises (or the device frequency is not enough
     54  1.2.2.3    martin  * high) and one wave cannot be expressed with 16 points, the data is thinned
     55  1.2.2.3    martin  * out by power of two, like 8 points -> 4 points (triangular wave)
     56  1.2.2.3    martin  * -> 2 points (rectangular wave).
     57  1.2.2.3    martin  */
     58  1.2.2.3    martin 
     59  1.2.2.3    martin /* Amplitude.  Full scale amplitude is too loud. */
     60  1.2.2.3    martin #define A(x) ((x) * 0.6)
     61  1.2.2.3    martin 
     62  1.2.2.3    martin /* (sin(2*pi * (x/16)) * 32767 / 100) << 16 */
     63  1.2.2.3    martin static const int32_t sinewave[] = {
     64  1.2.2.3    martin 	A(        0),
     65  1.2.2.3    martin 	A(  8217813),
     66  1.2.2.3    martin 	A( 15184539),
     67  1.2.2.3    martin 	A( 19839556),
     68  1.2.2.3    martin 	A( 21474181),
     69  1.2.2.3    martin 	A( 19839556),
     70  1.2.2.3    martin 	A( 15184539),
     71  1.2.2.3    martin 	A(  8217813),
     72  1.2.2.3    martin 	A(        0),
     73  1.2.2.3    martin 	A( -8217814),
     74  1.2.2.3    martin 	A(-15184540),
     75  1.2.2.3    martin 	A(-19839557),
     76  1.2.2.3    martin 	A(-21474182),
     77  1.2.2.3    martin 	A(-19839557),
     78  1.2.2.3    martin 	A(-15184540),
     79  1.2.2.3    martin 	A( -8217814),
     80  1.2.2.3    martin };
     81  1.2.2.3    martin #undef A
     82  1.2.2.2  christos 
     83  1.2.2.2  christos /*
     84  1.2.2.2  christos  * dev is a device_t for the audio device to use.
     85  1.2.2.2  christos  * pitch is the pitch of the bell in Hz,
     86  1.2.2.2  christos  * period is the length in ms,
     87  1.2.2.2  christos  * volume is the amplitude in % of max,
     88  1.2.2.2  christos  * poll is no longer used.
     89  1.2.2.2  christos  */
     90  1.2.2.2  christos void
     91  1.2.2.2  christos audiobell(void *dev, u_int pitch, u_int period, u_int volume, int poll)
     92  1.2.2.2  christos {
     93  1.2.2.2  christos 	dev_t audio;
     94  1.2.2.2  christos 	int16_t *buf;
     95  1.2.2.2  christos 	audio_file_t *file;
     96  1.2.2.2  christos 	audio_track_t *ptrack;
     97  1.2.2.2  christos 	struct uio auio;
     98  1.2.2.2  christos 	struct iovec aiov;
     99  1.2.2.3    martin 	u_int i;
    100  1.2.2.3    martin 	u_int j;
    101  1.2.2.3    martin 	u_int remaincount;
    102  1.2.2.3    martin 	u_int remainbytes;
    103  1.2.2.3    martin 	u_int wave1count;
    104  1.2.2.3    martin 	u_int wave1bytes;
    105  1.2.2.3    martin 	u_int blkbytes;
    106  1.2.2.3    martin 	u_int len;
    107  1.2.2.3    martin 	u_int step;
    108  1.2.2.3    martin 	u_int offset;
    109  1.2.2.3    martin 	u_int play_sample_rate;
    110  1.2.2.3    martin 	u_int mixer_sample_rate;
    111  1.2.2.2  christos 
    112  1.2.2.2  christos 	KASSERT(volume <= 100);
    113  1.2.2.2  christos 
    114  1.2.2.2  christos 	/* The audio system isn't built for polling. */
    115  1.2.2.2  christos 	if (poll)
    116  1.2.2.2  christos 		return;
    117  1.2.2.2  christos 
    118  1.2.2.2  christos 	buf = NULL;
    119  1.2.2.2  christos 	audio = AUDIO_DEVICE | device_unit((device_t)dev);
    120  1.2.2.2  christos 
    121  1.2.2.2  christos 	/* If not configured, we can't beep. */
    122  1.2.2.3    martin 	if (audiobellopen(audio, &file) != 0)
    123  1.2.2.2  christos 		return;
    124  1.2.2.2  christos 
    125  1.2.2.2  christos 	ptrack = file->ptrack;
    126  1.2.2.3    martin 	mixer_sample_rate = ptrack->mixer->track_fmt.sample_rate;
    127  1.2.2.2  christos 
    128  1.2.2.3    martin 	/* Limit pitch */
    129  1.2.2.3    martin 	if (pitch < 20)
    130  1.2.2.3    martin 		pitch = 20;
    131  1.2.2.2  christos 
    132  1.2.2.3    martin 	offset = 0;
    133  1.2.2.3    martin 	if (pitch <= mixer_sample_rate / 16) {
    134  1.2.2.3    martin 		/* 16-point sine wave */
    135  1.2.2.3    martin 		step = 1;
    136  1.2.2.3    martin 	} else if (pitch <= mixer_sample_rate / 8) {
    137  1.2.2.3    martin 		/* 8-point sine wave */
    138  1.2.2.3    martin 		step = 2;
    139  1.2.2.3    martin 	} else if (pitch <= mixer_sample_rate / 4) {
    140  1.2.2.3    martin 		/* 4-point sine wave, aka, triangular wave */
    141  1.2.2.3    martin 		step = 4;
    142  1.2.2.3    martin 	} else {
    143  1.2.2.3    martin 		/* Rectangular wave */
    144  1.2.2.3    martin 		if (pitch > mixer_sample_rate / 2)
    145  1.2.2.3    martin 			pitch = mixer_sample_rate / 2;
    146  1.2.2.3    martin 		step = 8;
    147  1.2.2.3    martin 		offset = 4;
    148  1.2.2.3    martin 	}
    149  1.2.2.2  christos 
    150  1.2.2.3    martin 	wave1count = __arraycount(sinewave) / step;
    151  1.2.2.3    martin 	play_sample_rate = pitch * wave1count;
    152  1.2.2.3    martin 	audiobellsetrate(file, play_sample_rate);
    153  1.2.2.3    martin 
    154  1.2.2.3    martin 	/* msec to sample count */
    155  1.2.2.3    martin 	remaincount = play_sample_rate * period / 1000;
    156  1.2.2.3    martin 	/* Roundup to full wave */
    157  1.2.2.3    martin 	remaincount = roundup(remaincount, wave1count);
    158  1.2.2.3    martin 	remainbytes = remaincount * sizeof(int16_t);
    159  1.2.2.3    martin 	wave1bytes = wave1count * sizeof(int16_t);
    160  1.2.2.3    martin 
    161  1.2.2.3    martin 	blkbytes = ptrack->usrbuf_blksize;
    162  1.2.2.3    martin 	blkbytes = rounddown(blkbytes, wave1bytes);
    163  1.2.2.3    martin 	blkbytes = uimin(blkbytes, remainbytes);
    164  1.2.2.3    martin 	buf = malloc(blkbytes, M_TEMP, M_WAITOK);
    165  1.2.2.2  christos 	if (buf == NULL)
    166  1.2.2.2  christos 		goto out;
    167  1.2.2.2  christos 
    168  1.2.2.3    martin 	/* Generate sinewave with specified volume */
    169  1.2.2.3    martin 	j = offset;
    170  1.2.2.3    martin 	for (i = 0; i < blkbytes / sizeof(int16_t); i++) {
    171  1.2.2.3    martin 		/* XXX audio already has track volume feature though #if 0 */
    172  1.2.2.3    martin 		buf[i] = AUDIO_SCALEDOWN(sinewave[j] * (int)volume, 16);
    173  1.2.2.3    martin 		j += step;
    174  1.2.2.3    martin 		j %= __arraycount(sinewave);
    175  1.2.2.2  christos 	}
    176  1.2.2.2  christos 
    177  1.2.2.3    martin 	/* Write while paused to avoid inserting silence. */
    178  1.2.2.2  christos 	ptrack->is_pause = true;
    179  1.2.2.3    martin 	for (; remainbytes > 0; remainbytes -= len) {
    180  1.2.2.3    martin 		len = uimin(remainbytes, blkbytes);
    181  1.2.2.2  christos 		aiov.iov_base = (void *)buf;
    182  1.2.2.2  christos 		aiov.iov_len = len;
    183  1.2.2.2  christos 		auio.uio_iov = &aiov;
    184  1.2.2.2  christos 		auio.uio_iovcnt = 1;
    185  1.2.2.2  christos 		auio.uio_offset = 0;
    186  1.2.2.2  christos 		auio.uio_resid = len;
    187  1.2.2.2  christos 		auio.uio_rw = UIO_WRITE;
    188  1.2.2.2  christos 		UIO_SETUP_SYSSPACE(&auio);
    189  1.2.2.2  christos 		if (audiobellwrite(file, &auio) != 0)
    190  1.2.2.2  christos 			goto out;
    191  1.2.2.2  christos 
    192  1.2.2.2  christos 		if (ptrack->usrbuf.used >= ptrack->usrbuf_blksize * NBLKHW)
    193  1.2.2.2  christos 			ptrack->is_pause = false;
    194  1.2.2.2  christos 	}
    195  1.2.2.2  christos 	/* Here we go! */
    196  1.2.2.2  christos 	ptrack->is_pause = false;
    197  1.2.2.2  christos out:
    198  1.2.2.2  christos 	if (buf != NULL)
    199  1.2.2.2  christos 		free(buf, M_TEMP);
    200  1.2.2.2  christos 	audiobellclose(file);
    201  1.2.2.2  christos }
    202