1/* Copyright (c) 2004-2005, Oracle and/or its affiliates. All rights reserved. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a 4 * copy of this software and associated documentation files (the "Software"), 5 * to deal in the Software without restriction, including without limitation 6 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 * and/or sell copies of the Software, and to permit persons to whom the 8 * Software is furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice (including the next 11 * paragraph) shall be included in all copies or substantial portions of the 12 * Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23#ifdef HAVE_XORG_CONFIG_H 24#include <xorg-config.h> 25#endif 26 27#include <sys/audio.h> 28#include <sys/uio.h> 29#include <limits.h> 30#include <math.h> 31#include <xserver_poll.h> 32 33#include "xf86.h" 34#include "xf86Priv.h" 35#include "xf86_OSlib.h" 36 37#define BELL_RATE 48000 /* Samples per second */ 38#define BELL_HZ 50 /* Fraction of a second i.e. 1/x */ 39#define BELL_MS (1000/BELL_HZ) /* MS */ 40#define BELL_SAMPLES (BELL_RATE / BELL_HZ) 41#define BELL_MIN 3 /* Min # of repeats */ 42 43#define AUDIO_DEVICE "/dev/audio" 44 45void 46xf86OSRingBell(int loudness, int pitch, int duration) 47{ 48 static short samples[BELL_SAMPLES]; 49 static short silence[BELL_SAMPLES]; /* "The Sound of Silence" */ 50 static int lastFreq; 51 int cnt; 52 int i; 53 int written; 54 int repeats; 55 int freq; 56 audio_info_t audioInfo; 57 struct iovec iov[IOV_MAX]; 58 int iovcnt; 59 double ampl, cyclen, phase; 60 int audioFD; 61 62 if ((loudness <= 0) || (pitch <= 0) || (duration <= 0)) { 63 return; 64 } 65 66 lastFreq = 0; 67 memset(silence, 0, sizeof(silence)); 68 69 audioFD = open(AUDIO_DEVICE, O_WRONLY | O_NONBLOCK); 70 if (audioFD == -1) { 71 xf86Msg(X_ERROR, "Bell: cannot open audio device \"%s\": %s\n", 72 AUDIO_DEVICE, strerror(errno)); 73 return; 74 } 75 76 freq = pitch; 77 freq = min(freq, (BELL_RATE / 2) - 1); 78 freq = max(freq, 2 * BELL_HZ); 79 80 /* 81 * Ensure full waves per buffer 82 */ 83 freq -= freq % BELL_HZ; 84 85 if (freq != lastFreq) { 86 lastFreq = freq; 87 ampl = 16384.0; 88 89 cyclen = (double) freq / (double) BELL_RATE; 90 phase = 0.0; 91 92 for (i = 0; i < BELL_SAMPLES; i++) { 93 samples[i] = (short) (ampl * sin(2.0 * M_PI * phase)); 94 phase += cyclen; 95 if (phase >= 1.0) 96 phase -= 1.0; 97 } 98 } 99 100 repeats = (duration + (BELL_MS / 2)) / BELL_MS; 101 repeats = max(repeats, BELL_MIN); 102 103 loudness = max(0, loudness); 104 loudness = min(loudness, 100); 105 106#ifdef DEBUG 107 ErrorF("BELL : freq %d volume %d duration %d repeats %d\n", 108 freq, loudness, duration, repeats); 109#endif 110 111 AUDIO_INITINFO(&audioInfo); 112 audioInfo.play.encoding = AUDIO_ENCODING_LINEAR; 113 audioInfo.play.sample_rate = BELL_RATE; 114 audioInfo.play.channels = 2; 115 audioInfo.play.precision = 16; 116 audioInfo.play.gain = min(AUDIO_MAX_GAIN, AUDIO_MAX_GAIN * loudness / 100); 117 118 if (ioctl(audioFD, AUDIO_SETINFO, &audioInfo) < 0) { 119 xf86Msg(X_ERROR, 120 "Bell: AUDIO_SETINFO failed on audio device \"%s\": %s\n", 121 AUDIO_DEVICE, strerror(errno)); 122 close(audioFD); 123 return; 124 } 125 126 iovcnt = 0; 127 128 for (cnt = 0; cnt <= repeats; cnt++) { 129 if (cnt == repeats) { 130 /* Insert a bit of silence so that multiple beeps are distinct and 131 * not compressed into a single tone. 132 */ 133 iov[iovcnt].iov_base = (char *) silence; 134 iov[iovcnt++].iov_len = sizeof(silence); 135 } 136 else { 137 iov[iovcnt].iov_base = (char *) samples; 138 iov[iovcnt++].iov_len = sizeof(samples); 139 } 140 if ((iovcnt >= IOV_MAX) || (cnt == repeats)) { 141 written = writev(audioFD, iov, iovcnt); 142 143 if ((written < ((int) (sizeof(samples) * iovcnt)))) { 144 /* audio buffer was full! */ 145 146 int naptime; 147 148 if (written == -1) { 149 if (errno != EAGAIN) { 150 xf86Msg(X_ERROR, 151 "Bell: writev failed on audio device \"%s\": %s\n", 152 AUDIO_DEVICE, strerror(errno)); 153 close(audioFD); 154 return; 155 } 156 i = iovcnt; 157 } 158 else { 159 i = ((sizeof(samples) * iovcnt) - written) 160 / sizeof(samples); 161 } 162 cnt -= i; 163 164 /* sleep a little to allow audio buffer to drain */ 165 naptime = BELL_MS * i; 166 xserver_poll(NULL, 0, naptime); 167 168 i = ((sizeof(samples) * iovcnt) - written) % sizeof(samples); 169 iovcnt = 0; 170 if ((written != -1) && (i > 0)) { 171 iov[iovcnt].iov_base = ((char *) samples) + i; 172 iov[iovcnt++].iov_len = sizeof(samples) - i; 173 } 174 } 175 else { 176 iovcnt = 0; 177 } 178 } 179 } 180 181 close(audioFD); 182 return; 183} 184