]> git.deb.at Git - pkg/beep.git/blob - beep.c
814a0b31f245ca22c83c4ce2bd4a5246b0358fb0
[pkg/beep.git] / beep.c
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 #include <linux/input.h>
30
31 /* I don't know where this number comes from, I admit that freely.  A 
32    wonderful human named Raine M. Ekman used it in a program that played
33    a tune at the console, and apparently, it's how the kernel likes its
34    sound requests to be phrased.  If you see Raine, thank him for me.  
35    
36    June 28, email from Peter Tirsek (peter at tirsek dot com):
37    
38    This number represents the fixed frequency of the original PC XT's
39    timer chip (the 8254 AFAIR), which is approximately 1.193 MHz. This
40    number is divided with the desired frequency to obtain a counter value,
41    that is subsequently fed into the timer chip, tied to the PC speaker.
42    The chip decreases this counter at every tick (1.193 MHz) and when it
43    reaches zero, it toggles the state of the speaker (on/off, or in/out),
44    resets the counter to the original value, and starts over. The end
45    result of this is a tone at approximately the desired frequency. :)
46 */
47 #ifndef CLOCK_TICK_RATE
48 #define CLOCK_TICK_RATE 1193180
49 #endif
50
51 #define VERSION_STRING "beep-1.2.2"
52 char *copyright = 
53 "Copyright (C) Johnathan Nightingale, 2002.  "
54 "Use and Distribution subject to GPL.  "
55 "For information: http://www.gnu.org/copyleft/.";
56
57 /* Meaningful Defaults */
58 #define DEFAULT_FREQ       440.0 /* Middle A */
59 #define DEFAULT_LENGTH     200   /* milliseconds */
60 #define DEFAULT_REPS       1
61 #define DEFAULT_DELAY      100   /* milliseconds */
62 #define DEFAULT_END_DELAY  NO_END_DELAY
63 #define DEFAULT_STDIN_BEEP NO_STDIN_BEEP
64
65 /* Other Constants */
66 #define NO_END_DELAY    0
67 #define YES_END_DELAY   1
68
69 #define NO_STDIN_BEEP   0
70 #define LINE_STDIN_BEEP 1
71 #define CHAR_STDIN_BEEP 2
72
73 typedef struct beep_parms_t {
74   float freq;     /* tone frequency (Hz)      */
75   int length;     /* tone length    (ms)      */
76   int reps;       /* # of repetitions         */
77   int delay;      /* delay between reps  (ms) */
78   int end_delay;  /* do we delay after last rep? */
79   int stdin_beep; /* are we using stdin triggers?  We have three options:
80                      - just beep and terminate (default)
81                      - beep after a line of input
82                      - beep after a character of input
83                      In the latter two cases, pass the text back out again,
84                      so that beep can be tucked appropriately into a text-
85                      processing pipe.
86                   */
87   int verbose;    /* verbose output?          */
88   struct beep_parms_t *next;  /* in case -n/--new is used. */
89 } beep_parms_t;
90
91 enum { BEEP_TYPE_CONSOLE, BEEP_TYPE_EVDEV };
92
93 /* Momma taught me never to use globals, but we need something the signal 
94    handlers can get at.*/
95 int console_fd = -1;
96 int console_type = BEEP_TYPE_CONSOLE;
97 char *console_device = NULL;
98
99
100 void do_beep(int freq) {
101   if (console_type == BEEP_TYPE_CONSOLE) {
102     if(ioctl(console_fd, KIOCSOUND, freq != 0
103       ? (int)(CLOCK_TICK_RATE/freq)
104       : freq) < 0) {
105       printf("\a");  /* Output the only beep we can, in an effort to fall back on usefulness */
106       perror("ioctl");
107     }
108   } else {
109      /* BEEP_TYPE_EVDEV */
110      struct input_event e;
111  
112      e.type = EV_SND;
113      e.code = SND_TONE;
114      e.value = freq;
115   
116      write(console_fd, &e, sizeof(struct input_event));
117   }
118 }
119
120
121 /* If we get interrupted, it would be nice to not leave the speaker beeping in
122    perpetuity. */
123 void handle_signal(int signum) {
124   
125   if(console_device)
126     free(console_device);
127     
128   switch(signum) {
129   case SIGINT:
130     if(console_fd >= 0) {
131       /* Kill the sound, quit gracefully */
132       do_beep(0);
133       close(console_fd);
134       exit(signum);
135     } else {
136       /* Just quit gracefully */
137       exit(signum);
138     }
139   }
140 }
141
142 /* print usage and exit */
143 void usage_bail(const char *executable_name) {
144   printf("Usage:\n%s [-f freq] [-l length] [-r reps] [-d delay] "
145          "[-D delay] [-s] [-c] [-e device]\n",
146          executable_name);
147   printf("%s [Options...] [-n] [--new] [Options...] ... \n", executable_name);
148   printf("%s [-h] [--help]\n", executable_name);
149   printf("%s [-v] [-V] [--version]\n", executable_name);
150   exit(1);
151 }
152
153
154 /* Parse the command line.  argv should be untampered, as passed to main.
155  * Beep parameters returned in result, subsequent parameters in argv will over-
156  * ride previous ones.
157  * 
158  * Currently valid parameters:
159  *  "-f <frequency in Hz>"
160  *  "-l <tone length in ms>"
161  *  "-r <repetitions>"
162  *  "-d <delay in ms>"
163  *  "-D <delay in ms>" (similar to -d, but delay after last repetition as well)
164  *  "-s" (beep after each line of input from stdin, echo line to stdout)
165  *  "-c" (beep after each char of input from stdin, echo char to stdout)
166  *  "--verbose/--debug"
167  *  "-h/--help"
168  *  "-v/-V/--version"
169  *  "-n/--new"
170  *
171  * March 29, 2002 - Daniel Eisenbud points out that c should be int, not char,
172  * for correctness on platforms with unsigned chars.
173  */
174 void parse_command_line(int argc, char **argv, beep_parms_t *result) {
175   int c;
176
177   struct option opt_list[7] = {{"help", 0, NULL, 'h'},
178                                {"version", 0, NULL, 'V'},
179                                {"new", 0, NULL, 'n'},
180                                {"verbose", 0, NULL, 'X'},
181                                {"debug", 0, NULL, 'X'},
182                                {"device", 1, NULL, 'e'},
183                                {0,0,0,0}};
184   while((c = getopt_long(argc, argv, "f:l:r:d:D:schvVne:", opt_list, NULL))
185         != EOF) {
186     int argval = -1;    /* handle parsed numbers for various arguments */
187     float argfreq = -1; 
188     switch(c) {      
189     case 'f':  /* freq */
190       if(!sscanf(optarg, "%f", &argfreq) || (argfreq >= 20000 /* ack! */) || 
191          (argfreq <= 0))
192         usage_bail(argv[0]);
193       else
194         if (result->freq != 0)
195           fprintf(stderr, "WARNING: multiple -f values given, only last "
196             "one is used.\n");
197         result->freq = argfreq;    
198       break;
199     case 'l' : /* length */
200       if(!sscanf(optarg, "%d", &argval) || (argval < 0))
201         usage_bail(argv[0]);
202       else
203         result->length = argval;
204       break;
205     case 'r' : /* repetitions */
206       if(!sscanf(optarg, "%d", &argval) || (argval < 0))
207         usage_bail(argv[0]);
208       else
209         result->reps = argval;
210       break;
211     case 'd' : /* delay between reps - WITHOUT delay after last beep*/
212       if(!sscanf(optarg, "%d", &argval) || (argval < 0))
213         usage_bail(argv[0]);
214       else {
215         result->delay = argval;
216         result->end_delay = NO_END_DELAY;
217       }
218       break;
219     case 'D' : /* delay between reps - WITH delay after last beep */
220       if(!sscanf(optarg, "%d", &argval) || (argval < 0))
221         usage_bail(argv[0]);
222       else {
223         result->delay = argval;
224         result->end_delay = YES_END_DELAY;
225       }
226       break;
227     case 's' :
228       result->stdin_beep = LINE_STDIN_BEEP;
229       break;
230     case 'c' :
231       result->stdin_beep = CHAR_STDIN_BEEP;
232       break;
233     case 'v' :
234     case 'V' : /* also --version */
235       printf("%s\n",VERSION_STRING);
236       exit(0);
237       break;
238     case 'n' : /* also --new - create another beep */
239       if (result->freq == 0)
240         result->freq = DEFAULT_FREQ;
241       result->next = (beep_parms_t *)malloc(sizeof(beep_parms_t));
242       result->next->freq       = 0;
243       result->next->length     = DEFAULT_LENGTH;
244       result->next->reps       = DEFAULT_REPS;
245       result->next->delay      = DEFAULT_DELAY;
246       result->next->end_delay  = DEFAULT_END_DELAY;
247       result->next->stdin_beep = DEFAULT_STDIN_BEEP;
248       result->next->verbose    = result->verbose;
249       result->next->next       = NULL;
250       result = result->next; /* yes, I meant to do that. */
251       break;
252     case 'X' : /* --debug / --verbose */
253       result->verbose = 1;
254       break;
255     case 'e' : /* also --device */
256       console_device = strdup(optarg);
257       break;
258     case 'h' : /* notice that this is also --help */
259     default :
260       usage_bail(argv[0]);
261     }
262   }
263   if (result->freq == 0)
264     result->freq = DEFAULT_FREQ;
265 }  
266
267 void play_beep(beep_parms_t parms) {
268   int i; /* loop counter */
269
270   if(parms.verbose == 1)
271       fprintf(stderr, "[DEBUG] %d times %d ms beeps (%d delay between, "
272         "%d delay after) @ %.2f Hz\n",
273         parms.reps, parms.length, parms.delay, parms.end_delay, parms.freq);
274
275   /* try to snag the console */
276   if(console_device)
277     console_fd = open(console_device, O_WRONLY);
278   else
279     if((console_fd = open("/dev/input/event0", O_WRONLY)) == -1)
280       if((console_fd = open("/dev/tty0", O_WRONLY)) == -1)
281         console_fd = open("/dev/vc/0", O_WRONLY);
282       
283   if(console_fd == -1) {
284     fprintf(stderr, "Could not open %s for writing\n",
285       console_device != NULL ? console_device : "/dev/tty0 or /dev/vc/0");
286     printf("\a");  /* Output the only beep we can, in an effort to fall back on usefulness */
287     perror("open");
288     exit(1);
289   }
290
291   if (ioctl(console_fd, EVIOCGSND(0)) != -1)
292     console_type = BEEP_TYPE_EVDEV;
293   else
294     console_type = BEEP_TYPE_CONSOLE;
295   
296   /* Beep */
297   for (i = 0; i < parms.reps; i++) {                    /* start beep */
298     do_beep(parms.freq);
299     /* Look ma, I'm not ansi C compatible! */
300     usleep(1000*parms.length);                          /* wait...    */
301     do_beep(0);
302     if(parms.end_delay || (i+1 < parms.reps))
303        usleep(1000*parms.delay);                        /* wait...    */
304   }                                                     /* repeat.    */
305
306   close(console_fd);
307 }
308
309
310
311 int main(int argc, char **argv) {
312   char sin[4096], *ptr;
313   
314   beep_parms_t *parms = (beep_parms_t *)malloc(sizeof(beep_parms_t));
315   parms->freq       = 0;
316   parms->length     = DEFAULT_LENGTH;
317   parms->reps       = DEFAULT_REPS;
318   parms->delay      = DEFAULT_DELAY;
319   parms->end_delay  = DEFAULT_END_DELAY;
320   parms->stdin_beep = DEFAULT_STDIN_BEEP;
321   parms->verbose    = 0;
322   parms->next       = NULL;
323
324   signal(SIGINT, handle_signal);
325   parse_command_line(argc, argv, parms);
326
327   /* this outermost while loop handles the possibility that -n/--new has been
328      used, i.e. that we have multiple beeps specified. Each iteration will
329      play, then free() one parms instance. */
330   while(parms) {
331     beep_parms_t *next = parms->next;
332
333     if(parms->stdin_beep) {
334       /* in this case, beep is probably part of a pipe, in which case POSIX 
335          says stdin and out should be fuly buffered.  This however means very 
336          laggy performance with beep just twiddling it's thumbs until a buffer
337          fills. Thus, kill the buffering.  In some situations, this too won't 
338          be enough, namely if we're in the middle of a long pipe, and the 
339          processes feeding us stdin are buffered, we'll have to wait for them,
340          not much to  be done about that. */
341       setvbuf(stdin, NULL, _IONBF, 0);
342       setvbuf(stdout, NULL, _IONBF, 0);
343       while(fgets(sin, 4096, stdin)) {
344         if(parms->stdin_beep==CHAR_STDIN_BEEP) {
345           for(ptr=sin;*ptr;ptr++) {
346             putchar(*ptr);
347             fflush(stdout);
348             play_beep(*parms);
349           }
350         } else {
351           fputs(sin, stdout);
352           play_beep(*parms);
353         }
354       }
355     } else {
356       play_beep(*parms);
357     }
358
359     /* Junk each parms struct after playing it */
360     free(parms);
361     parms = next;
362   }
363
364   if(console_device)
365     free(console_device);
366     
367   return EXIT_SUCCESS;
368 }