]> git.deb.at Git - pkg/beep.git/blob - beep.c
Bump version to 1.3, update CHANGELOG and README
[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.3"
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] [--verbose | --debug] [-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/tty0", O_WRONLY)) == -1)
280       console_fd = open("/dev/vc/0", O_WRONLY);
281
282   if(console_fd == -1) {
283     fprintf(stderr, "Could not open %s for writing\n",
284       console_device != NULL ? console_device : "/dev/tty0 or /dev/vc/0");
285     printf("\a");  /* Output the only beep we can, in an effort to fall back on usefulness */
286     perror("open");
287     exit(1);
288   }
289
290   if (ioctl(console_fd, EVIOCGSND(0)) != -1)
291     console_type = BEEP_TYPE_EVDEV;
292   else
293     console_type = BEEP_TYPE_CONSOLE;
294   
295   /* Beep */
296   for (i = 0; i < parms.reps; i++) {                    /* start beep */
297     do_beep(parms.freq);
298     /* Look ma, I'm not ansi C compatible! */
299     usleep(1000*parms.length);                          /* wait...    */
300     do_beep(0);                                         /* stop beep  */
301     if(parms.end_delay || (i+1 < parms.reps))
302        usleep(1000*parms.delay);                        /* wait...    */
303   }                                                     /* repeat.    */
304
305   close(console_fd);
306 }
307
308
309
310 int main(int argc, char **argv) {
311   char sin[4096], *ptr;
312   
313   beep_parms_t *parms = (beep_parms_t *)malloc(sizeof(beep_parms_t));
314   parms->freq       = 0;
315   parms->length     = DEFAULT_LENGTH;
316   parms->reps       = DEFAULT_REPS;
317   parms->delay      = DEFAULT_DELAY;
318   parms->end_delay  = DEFAULT_END_DELAY;
319   parms->stdin_beep = DEFAULT_STDIN_BEEP;
320   parms->verbose    = 0;
321   parms->next       = NULL;
322
323   signal(SIGINT, handle_signal);
324   parse_command_line(argc, argv, parms);
325
326   /* this outermost while loop handles the possibility that -n/--new has been
327      used, i.e. that we have multiple beeps specified. Each iteration will
328      play, then free() one parms instance. */
329   while(parms) {
330     beep_parms_t *next = parms->next;
331
332     if(parms->stdin_beep) {
333       /* in this case, beep is probably part of a pipe, in which case POSIX 
334          says stdin and out should be fuly buffered.  This however means very 
335          laggy performance with beep just twiddling it's thumbs until a buffer
336          fills. Thus, kill the buffering.  In some situations, this too won't 
337          be enough, namely if we're in the middle of a long pipe, and the 
338          processes feeding us stdin are buffered, we'll have to wait for them,
339          not much to  be done about that. */
340       setvbuf(stdin, NULL, _IONBF, 0);
341       setvbuf(stdout, NULL, _IONBF, 0);
342       while(fgets(sin, 4096, stdin)) {
343         if(parms->stdin_beep==CHAR_STDIN_BEEP) {
344           for(ptr=sin;*ptr;ptr++) {
345             putchar(*ptr);
346             fflush(stdout);
347             play_beep(*parms);
348           }
349         } else {
350           fputs(sin, stdout);
351           play_beep(*parms);
352         }
353       }
354     } else {
355       play_beep(*parms);
356     }
357
358     /* Junk each parms struct after playing it */
359     free(parms);
360     parms = next;
361   }
362
363   if(console_device)
364     free(console_device);
365
366   return EXIT_SUCCESS;
367 }