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