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 */
23 #ifdef HAVE_XORG_CONFIG_H
24 #include <xorg-config.h>
25 #endif
27 #include <sys/audio.h>
28 #include <sys/uio.h>
29 #include <limits.h>
30 #include <math.h>
31 #include <poll.h>
33 #include "xf86.h"
34 #include "xf86Priv.h"
35 #include "xf86_OSlib.h"
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 */
43 #define AUDIO_DEVICE "/dev/audio"
45 void
46 xf86OSRingBell(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;
62 if ((loudness <= 0) || (pitch <= 0) || (duration <= 0)) {
63 return;
64 }
66 lastFreq = 0;
67 memset(silence, 0, sizeof(silence));
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 }
76 freq = pitch;
77 freq = min(freq, (BELL_RATE / 2) - 1);
78 freq = max(freq, 2 * BELL_HZ);
80 /*
81 * Ensure full waves per buffer
82 */
83 freq -= freq % BELL_HZ;
85 if (freq != lastFreq) {
86 lastFreq = freq;
87 ampl = 16384.0;
89 cyclen = (double) freq / (double) BELL_RATE;
90 phase = 0.0;
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 }
100 repeats = (duration + (BELL_MS / 2)) / BELL_MS;
101 repeats = max(repeats, BELL_MIN);
103 loudness = max(0, loudness);
104 loudness = min(loudness, 100);
106 #ifdef DEBUG
107 ErrorF("BELL : freq %d volume %d duration %d repeats %d\n",
108 freq, loudness, duration, repeats);
109 #endif
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);
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 }
126 iovcnt = 0;
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 } else {
136 iov[iovcnt].iov_base = (char *) samples;
137 iov[iovcnt++].iov_len = sizeof(samples);
138 }
139 if ((iovcnt >= IOV_MAX) || (cnt == repeats)) {
140 written = writev(audioFD, iov, iovcnt);
142 if ((written < ((int)(sizeof(samples) * iovcnt)))) {
143 /* audio buffer was full! */
145 int naptime;
147 if (written == -1) {
148 if (errno != EAGAIN) {
149 xf86Msg(X_ERROR,
150 "Bell: writev failed on audio device \"%s\": %s\n",
151 AUDIO_DEVICE, strerror(errno));
152 close(audioFD);
153 return;
154 }
155 i = iovcnt;
156 } else {
157 i = ((sizeof(samples) * iovcnt) - written)
158 / sizeof(samples);
159 }
160 cnt -= i;
162 /* sleep a little to allow audio buffer to drain */
163 naptime = BELL_MS * i;
164 poll(NULL, 0, naptime);
166 i = ((sizeof(samples) * iovcnt) - written) % sizeof(samples);
167 iovcnt = 0;
168 if ((written != -1) && (i > 0)) {
169 iov[iovcnt].iov_base = ((char *) samples) + i;
170 iov[iovcnt++].iov_len = sizeof(samples) - i;
171 }
172 } else {
173 iovcnt = 0;
174 }
175 }
176 }
178 close(audioFD);
179 return;
180 }