Commit | Line | Data |
---|---|---|
a09e091a JB |
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 <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 | ||
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; | |
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 | 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 | } |