1 | /* beep - just what it sounds like, makes the console beep - but with
|
2 | * precision control. See the man page for details.
|
3 | *
|
4 | * Try beep -h for command line args
|
5 | *
|
6 | * This code is copyright (C) Johnathan Nightingale, 2000.
|
7 | *
|
8 | * This code may distributed only under the terms of the GNU Public License
|
9 | * which can be found at http://www.gnu.org/copyleft or in the file COPYING
|
10 | * supplied with this code.
|
11 | *
|
12 | * This code is not distributed with warranties of any kind, including implied
|
13 | * warranties of merchantability or fitness for a particular use or ability to
|
14 | * breed pandas in captivity, it just can't be done.
|
15 | *
|
16 | * Bug me, I like it: http://johnath.com/ or johnath@johnath.com
|
17 | */
|
18 |
|
19 | #include <fcntl.h>
|
20 | #include <getopt.h>
|
21 | #include <signal.h>
|
22 | #include <stdio.h>
|
23 | #include <stdlib.h>
|
24 | #include <string.h>
|
25 | #include <unistd.h>
|
26 | #include <sys/ioctl.h>
|
27 | #include <sys/types.h>
|
28 | #include <linux/kd.h>
|
29 |
|
30 | /* I don't know where this number comes from, I admit that freely. A
|
31 | wonderful human named Raine M. Ekman used it in a program that played
|
32 | a tune at the console, and apparently, it's how the kernel likes its
|
33 | sound requests to be phrased. If you see Raine, thank him for me.
|
34 |
|
35 | June 28, email from Peter Tirsek (peter at tirsek dot com):
|
36 |
|
37 | This number represents the fixed frequency of the original PC XT's
|
38 | timer chip (the 8254 AFAIR), which is approximately 1.193 MHz. This
|
39 | number is divided with the desired frequency to obtain a counter value,
|
40 | that is subsequently fed into the timer chip, tied to the PC speaker.
|
41 | The chip decreases this counter at every tick (1.193 MHz) and when it
|
42 | reaches zero, it toggles the state of the speaker (on/off, or in/out),
|
43 | resets the counter to the original value, and starts over. The end
|
44 | result of this is a tone at approximately the desired frequency. :)
|
45 | */
|
46 | #ifndef CLOCK_TICK_RATE
|
47 | #define CLOCK_TICK_RATE 1193180
|
48 | #endif
|
49 |
|
50 | #define VERSION_STRING "beep-1.2.2"
|
51 | char *copyright =
|
52 | "Copyright (C) Johnathan Nightingale, 2002. "
|
53 | "Use and Distribution subject to GPL. "
|
54 | "For information: http://www.gnu.org/copyleft/.";
|
55 |
|
56 | /* Meaningful Defaults */
|
57 | #define DEFAULT_FREQ 440.0 /* Middle A */
|
58 | #define DEFAULT_LENGTH 200 /* milliseconds */
|
59 | #define DEFAULT_REPS 1
|
60 | #define DEFAULT_DELAY 100 /* milliseconds */
|
61 | #define DEFAULT_END_DELAY NO_END_DELAY
|
62 | #define DEFAULT_STDIN_BEEP NO_STDIN_BEEP
|
63 |
|
64 | /* Other Constants */
|
65 | #define NO_END_DELAY 0
|
66 | #define YES_END_DELAY 1
|
67 |
|
68 | #define NO_STDIN_BEEP 0
|
69 | #define LINE_STDIN_BEEP 1
|
70 | #define CHAR_STDIN_BEEP 2
|
71 |
|
72 | typedef struct beep_parms_t {
|
73 | float freq; /* tone frequency (Hz) */
|
74 | int length; /* tone length (ms) */
|
75 | int reps; /* # of repetitions */
|
76 | int delay; /* delay between reps (ms) */
|
77 | int end_delay; /* do we delay after last rep? */
|
78 | int stdin_beep; /* are we using stdin triggers? We have three options:
|
79 | - just beep and terminate (default)
|
80 | - beep after a line of input
|
81 | - beep after a character of input
|
82 | In the latter two cases, pass the text back out again,
|
83 | so that beep can be tucked appropriately into a text-
|
84 | processing pipe.
|
85 | */
|
86 | struct beep_parms_t *next; /* in case -n/--new is used. */
|
87 | } beep_parms_t;
|
88 |
|
89 | /* Momma taught me never to use globals, but we need something the signal
|
90 | handlers can get at.*/
|
91 | int console_fd = -1;
|
92 |
|
93 | /* If we get interrupted, it would be nice to not leave the speaker beeping in
|
94 | perpetuity. */
|
95 | void handle_signal(int signum) {
|
96 | switch(signum) {
|
97 | case SIGINT:
|
98 | if(console_fd >= 0) {
|
99 | /* Kill the sound, quit gracefully */
|
100 | ioctl(console_fd, KIOCSOUND, 0);
|
101 | close(console_fd);
|
102 | exit(signum);
|
103 | } else {
|
104 | /* Just quit gracefully */
|
105 | exit(signum);
|
106 | }
|
107 | }
|
108 | }
|
109 |
|
110 | /* print usage and exit */
|
111 | void usage_bail(const char *executable_name) {
|
112 | printf("Usage:\n%s [-f freq] [-l length] [-r reps] [-d delay] "
|
113 | "[-D delay] [-s] [-c]\n",
|
114 | executable_name);
|
115 | printf("%s [Options...] [-n] [--new] [Options...] ... \n", executable_name);
|
116 | printf("%s [-h] [--help]\n", executable_name);
|
117 | printf("%s [-v] [-V] [--version]\n", executable_name);
|
118 | exit(1);
|
119 | }
|
120 |
|
121 |
|
122 | /* Parse the command line. argv should be untampered, as passed to main.
|
123 | * Beep parameters returned in result, subsequent parameters in argv will over-
|
124 | * ride previous ones.
|
125 | *
|
126 | * Currently valid parameters:
|
127 | * "-f <frequency in Hz>"
|
128 | * "-l <tone length in ms>"
|
129 | * "-r <repetitions>"
|
130 | * "-d <delay in ms>"
|
131 | * "-D <delay in ms>" (similar to -d, but delay after last repetition as well)
|
132 | * "-s" (beep after each line of input from stdin, echo line to stdout)
|
133 | * "-c" (beep after each char of input from stdin, echo char to stdout)
|
134 | * "-h/--help"
|
135 | * "-v/-V/--version"
|
136 | * "-n/--new"
|
137 | *
|
138 | * March 29, 2002 - Daniel Eisenbud points out that c should be int, not char,
|
139 | * for correctness on platforms with unsigned chars.
|
140 | */
|
141 | void parse_command_line(int argc, char **argv, beep_parms_t *result) {
|
142 | int c;
|
143 |
|
144 | struct option opt_list[4] = {{"help", 0, NULL, 'h'},
|
145 | {"version", 0, NULL, 'V'},
|
146 | {"new", 0, NULL, 'n'},
|
147 | {0,0,0,0}};
|
148 | while((c = getopt_long(argc, argv, "f:l:r:d:D:schvVn", opt_list, NULL))
|
149 | != EOF) {
|
150 | int argval = -1; /* handle parsed numbers for various arguments */
|
151 | float argfreq = -1;
|
152 | switch(c) {
|
153 | case 'f': /* freq */
|
154 | if(!sscanf(optarg, "%f", &argfreq) || (argfreq >= 20000 /* ack! */) ||
|
155 | (argfreq <= 0))
|
156 | usage_bail(argv[0]);
|
157 | else
|
158 | result->freq = argfreq;
|
159 | break;
|
160 | case 'l' : /* length */
|
161 | if(!sscanf(optarg, "%d", &argval) || (argval < 0))
|
162 | usage_bail(argv[0]);
|
163 | else
|
164 | result->length = argval;
|
165 | break;
|
166 | case 'r' : /* repetitions */
|
167 | if(!sscanf(optarg, "%d", &argval) || (argval < 0))
|
168 | usage_bail(argv[0]);
|
169 | else
|
170 | result->reps = argval;
|
171 | break;
|
172 | case 'd' : /* delay between reps - WITHOUT delay after last beep*/
|
173 | if(!sscanf(optarg, "%d", &argval) || (argval < 0))
|
174 | usage_bail(argv[0]);
|
175 | else {
|
176 | result->delay = argval;
|
177 | result->end_delay = NO_END_DELAY;
|
178 | }
|
179 | break;
|
180 | case 'D' : /* delay between reps - WITH delay after last beep */
|
181 | if(!sscanf(optarg, "%d", &argval) || (argval < 0))
|
182 | usage_bail(argv[0]);
|
183 | else {
|
184 | result->delay = argval;
|
185 | result->end_delay = YES_END_DELAY;
|
186 | }
|
187 | break;
|
188 | case 's' :
|
189 | result->stdin_beep = LINE_STDIN_BEEP;
|
190 | break;
|
191 | case 'c' :
|
192 | result->stdin_beep = CHAR_STDIN_BEEP;
|
193 | break;
|
194 | case 'v' :
|
195 | case 'V' : /* also --version */
|
196 | printf("%s\n",VERSION_STRING);
|
197 | exit(0);
|
198 | break;
|
199 | case 'n' : /* also --new - create another beep */
|
200 | result->next = (beep_parms_t *)malloc(sizeof(beep_parms_t));
|
201 | result->next->freq = DEFAULT_FREQ;
|
202 | result->next->length = DEFAULT_LENGTH;
|
203 | result->next->reps = DEFAULT_REPS;
|
204 | result->next->delay = DEFAULT_DELAY;
|
205 | result->next->end_delay = DEFAULT_END_DELAY;
|
206 | result->next->stdin_beep = DEFAULT_STDIN_BEEP;
|
207 | result->next->next = NULL;
|
208 | result = result->next; /* yes, I meant to do that. */
|
209 | break;
|
210 | case 'h' : /* notice that this is also --help */
|
211 | default :
|
212 | usage_bail(argv[0]);
|
213 | }
|
214 | }
|
215 | }
|
216 |
|
217 | void play_beep(beep_parms_t parms) {
|
218 | int i; /* loop counter */
|
219 |
|
220 | /* try to snag the console */
|
221 | if((console_fd = open("/dev/console", O_WRONLY)) == -1) {
|
222 | fprintf(stderr, "Could not open /dev/console for writing.\n");
|
223 | printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */
|
224 | perror("open");
|
225 | exit(1);
|
226 | }
|
227 |
|
228 | /* Beep */
|
229 | for (i = 0; i < parms.reps; i++) { /* start beep */
|
230 | if(ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/parms.freq)) < 0) {
|
231 | printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */
|
232 | perror("ioctl");
|
233 | }
|
234 | /* Look ma, I'm not ansi C compatible! */
|
235 | usleep(1000*parms.length); /* wait... */
|
236 | ioctl(console_fd, KIOCSOUND, 0); /* stop beep */
|
237 | if(parms.end_delay || (i+1 < parms.reps))
|
238 | usleep(1000*parms.delay); /* wait... */
|
239 | } /* repeat. */
|
240 |
|
241 | close(console_fd);
|
242 | }
|
243 |
|
244 |
|
245 |
|
246 | int main(int argc, char **argv) {
|
247 | char sin[4096], *ptr;
|
248 |
|
249 | beep_parms_t *parms = (beep_parms_t *)malloc(sizeof(beep_parms_t));
|
250 | parms->freq = DEFAULT_FREQ;
|
251 | parms->length = DEFAULT_LENGTH;
|
252 | parms->reps = DEFAULT_REPS;
|
253 | parms->delay = DEFAULT_DELAY;
|
254 | parms->end_delay = DEFAULT_END_DELAY;
|
255 | parms->stdin_beep = DEFAULT_STDIN_BEEP;
|
256 | parms->next = NULL;
|
257 |
|
258 | signal(SIGINT, handle_signal);
|
259 | parse_command_line(argc, argv, parms);
|
260 |
|
261 | /* this outermost while loop handles the possibility that -n/--new has been
|
262 | used, i.e. that we have multiple beeps specified. Each iteration will
|
263 | play, then free() one parms instance. */
|
264 | while(parms) {
|
265 | beep_parms_t *next = parms->next;
|
266 |
|
267 | if(parms->stdin_beep) {
|
268 | /* in this case, beep is probably part of a pipe, in which case POSIX
|
269 | says stdin and out should be fuly buffered. This however means very
|
270 | laggy performance with beep just twiddling it's thumbs until a buffer
|
271 | fills. Thus, kill the buffering. In some situations, this too won't
|
272 | be enough, namely if we're in the middle of a long pipe, and the
|
273 | processes feeding us stdin are buffered, we'll have to wait for them,
|
274 | not much to be done about that. */
|
275 | setvbuf(stdin, NULL, _IONBF, 0);
|
276 | setvbuf(stdout, NULL, _IONBF, 0);
|
277 | while(fgets(sin, 4096, stdin)) {
|
278 | if(parms->stdin_beep==CHAR_STDIN_BEEP) {
|
279 | for(ptr=sin;*ptr;ptr++) {
|
280 | putchar(*ptr);
|
281 | fflush(stdout);
|
282 | play_beep(*parms);
|
283 | }
|
284 | } else {
|
285 | fputs(sin, stdout);
|
286 | play_beep(*parms);
|
287 | }
|
288 | }
|
289 | } else {
|
290 | play_beep(*parms);
|
291 | }
|
292 |
|
293 | /* Junk each parms struct after playing it */
|
294 | free(parms);
|
295 | parms = next;
|
296 | }
|
297 |
|
298 | return EXIT_SUCCESS;
|
299 | }
|