gdb - Local mods (compile)
[dragonfly.git] / games / morse / morse.c
1 /*-
2  * Copyright (c) 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1988, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)morse.c  8.1 (Berkeley) 5/31/93
31  * $FreeBSD: src/games/morse/morse.c,v 1.12.2.2 2002/03/12 17:45:15 phantom Exp $
32  */
33
34 /*
35  * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
36  * <lyndon@orthanc.com>
37  */
38
39 #include <sys/time.h>
40 #include <sys/soundcard.h>
41
42 #include <ctype.h>
43 #include <err.h>
44 #include <fcntl.h>
45 #include <langinfo.h>
46 #include <locale.h>
47 #include <math.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <termios.h>
53 #include <unistd.h>
54
55 struct morsetab {
56         char            inchar;
57         const char      *morse;
58 };
59
60 static const struct morsetab mtab[] = {
61
62         /* letters */
63
64         {'a', ".-"},
65         {'b', "-..."},
66         {'c', "-.-."},
67         {'d', "-.."},
68         {'e', "."},
69         {'f', "..-."},
70         {'g', "--."},
71         {'h', "...."},
72         {'i', ".."},
73         {'j', ".---"},
74         {'k', "-.-"},
75         {'l', ".-.."},
76         {'m', "--"},
77         {'n', "-."},
78         {'o', "---"},
79         {'p', ".--."},
80         {'q', "--.-"},
81         {'r', ".-."},
82         {'s', "..."},
83         {'t', "-"},
84         {'u', "..-"},
85         {'v', "...-"},
86         {'w', ".--"},
87         {'x', "-..-"},
88         {'y', "-.--"},
89         {'z', "--.."},
90
91         /* digits */
92
93         {'0', "-----"},
94         {'1', ".----"},
95         {'2', "..---"},
96         {'3', "...--"},
97         {'4', "....-"},
98         {'5', "....."},
99         {'6', "-...."},
100         {'7', "--..."},
101         {'8', "---.."},
102         {'9', "----."},
103
104         /* punctuation */
105
106         {',', "--..--"},
107         {'.', ".-.-.-"},
108         {'?', "..--.."},
109         {'!', "-.-.--"},        /* KW */
110         {'/', "-..-."},
111         {'-', "-....-"},
112         {'_', "..--.."},
113         {'=', "-...-"},         /* BT */
114         {':', "---..."},
115         {';', "-.-.-."},
116         {'(', "-.--."},         /* KN */
117         {')', "-.--.-"},
118         {'$', "...-..-"},
119         {'+', ".-.-."},         /* AR */
120         {'\'', ".----."},
121         {'"', ".-..-."},
122         {'@', ".--.-."},        /* AC */
123
124         {'\0', ""}
125 };
126
127
128 static const struct morsetab iso8859tab[] = {
129         {'á', ".--.-"},
130         {'à', ".--.-"},
131         {'â', ".--.-"},
132         {'ä', ".-.-"},
133         {'ç', "-.-.."},
134         {'é', "..-.."},
135         {'è', "..-.."},
136         {'ê', "-..-."},
137         {'ö', "---."},
138         {'ü', "..--"},
139
140         {'\0', ""}
141 };
142
143 static const struct morsetab koi8rtab[] = {
144         /*
145          * the cyrillic alphabet; you'll need a KOI8R font in order
146          * to see the actual characters
147          */
148         {'Á', ".-"},            /* a */
149         {'Â', "-..."},  /* be */
150         {'×', ".--"},   /* ve */
151         {'Ç', "--."},   /* ge */
152         {'Ä', "-.."},   /* de */
153         {'Å', "."},             /* ye */
154         {'£', "."},             /* yo, the same as ye */
155         {'Ö', "...-"},  /* she */
156         {'Ú', "--.."},  /* ze */
157         {'É', ".."},            /* i */
158         {'Ê', ".---"},  /* i kratkoye */
159         {'Ë', "-.-"},   /* ka */
160         {'Ì', ".-.."},  /* el */
161         {'Í', "--"},            /* em */
162         {'Î', "-."},            /* en */
163         {'Ï', "---"},   /* o */
164         {'Ð', ".--."},  /* pe */
165         {'Ò', ".-."},   /* er */
166         {'Ó', "..."},   /* es */
167         {'Ô', "-"},             /* te */
168         {'Õ', "..-"},   /* u */
169         {'Æ', "..-."},  /* ef */
170         {'È', "...."},  /* kha */
171         {'Ã', "-.-."},  /* ce */
172         {'Þ', "---."},  /* che */
173         {'Û', "----"},  /* sha */
174         {'Ý', "--.-"},  /* shcha */
175         {'Ù', "-.--"},  /* yi */
176         {'Ø', "-..-"},  /* myakhkij znak */
177         {'Ü', "..-.."}, /* ae */
178         {'À', "..--"},  /* yu */
179         {'Ñ', ".-.-"},  /* ya */
180
181         {'\0', ""}
182 };
183
184 struct tone_data {
185         int16_t *data;
186         size_t  len;
187 };
188
189 void            alloc_soundbuf(struct tone_data *, double, int);
190 void            morse(char, int);
191 void            show(const char *, int);
192 void            play(const char *, int);
193 void            ttyout(const char *, int);
194 void            sighandler(int);
195
196 #define GETOPTOPTS "d:ef:lopP:sw:W:"
197 #define USAGE \
198 "usage: morse [-els] [-p | -o] [-P device] [-d device] [-w speed] [-W speed] [-f frequency] [string ...]\n"
199
200 static int      lflag, oflag, pflag, sflag, eflag;
201 static int      wpm = 20;       /* words per minute */
202 static int      farnsworth = -1;
203 #define FREQUENCY 600
204 static int      freq = FREQUENCY;
205 static char     *device;        /* for tty-controlled generator */
206
207 static struct tone_data tone_dot, tone_dash, tone_silence, tone_letter_silence;
208 #define DSP_RATE 44100
209 static const char *snddev = NULL;
210
211 #define DASH_LEN 3
212 #define CHAR_SPACE 3
213 #define WORD_SPACE (7 - CHAR_SPACE)
214 static float    dot_clock, word_clock;
215 int             spkr, line;
216 struct termios  otty, ntty;
217 int             olflags;
218
219 static const struct morsetab *hightab;
220
221 int
222 main(int argc, char **argv)
223 {
224         int    ch, lflags;
225         int    prosign;
226         char  *p, *codeset;
227
228         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
229                 switch ((char) ch) {
230                 case 'd':
231                         device = optarg;
232                         break;
233                 case 'e':
234                         eflag = 1;
235                         setvbuf(stdout, 0, _IONBF, 0);
236                         break;
237                 case 'f':
238                         freq = atoi(optarg);
239                         break;
240                 case 'l':
241                         lflag = 1;
242                         break;
243                 case 'o':
244                         oflag = 1;
245                         /* FALLTHROUGH */
246                 case 'p':
247                         pflag = 1;
248                         break;
249                 case 'P':
250                         snddev = optarg;
251                         break;
252                 case 's':
253                         sflag = 1;
254                         break;
255                 case 'w':
256                         wpm = atoi(optarg);
257                         break;
258                 case 'W':
259                         farnsworth = atoi(optarg);
260                         break;
261                 case '?':
262                 default:
263                         fputs(USAGE, stderr);
264                         exit(1);
265                 }
266         if (sflag && lflag) {
267                 fputs("morse: only one of -l and -s allowed\n", stderr);
268                 exit(1);
269         }
270         if (pflag + !!device + sflag + lflag > 1) {
271                 fputs("morse: only one of -o, -p, -d and -l, -s allowed\n", stderr);
272                 exit(1);
273         }
274         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (farnsworth > 60))) {
275                 fputs("morse: insane speed\n", stderr);
276                 exit(1);
277         }
278         if ((pflag || device) && (freq == 0))
279                 freq = FREQUENCY;
280         if (pflag || device) {
281                 /*
282                  * A note on how to get to this magic 1.2:
283                  * x WPM = 50*x dits per minute (norm word "PARIS").
284                  * dits per sec = dits per minute / 60, thus
285                  * dits per sec = 50 * x / 60 = x / (60 / 50) = x / 1.2
286                  */
287                 dot_clock = wpm / 1.2;          /* dots/sec */
288                 dot_clock = 1 / dot_clock;      /* duration of a dot */
289
290                 word_clock = dot_clock;
291
292                 /*
293                  * This is how to get to this formula:
294                  * PARIS = 22 dit (symbols) + 9 symbol spaces = 31 symbol times
295                  *       + 19 space times.
296                  *
297                  * The symbol times are in dot_clock, so the spaces have to
298                  * make up to reach the farnsworth time.
299                  */
300                 if (farnsworth > 0)
301                         word_clock = (60.0 / farnsworth - 31 * dot_clock) / 19;
302         }
303         if (snddev == NULL) {
304                 if (oflag)
305                         snddev = "-";
306                 else /* only pflag */
307                         snddev = "/dev/dsp";
308         }
309
310         if (pflag) {
311                 snd_chan_param param;
312
313                 if (oflag && strcmp(snddev, "-") == 0)
314                         spkr = STDOUT_FILENO;
315                 else
316                         spkr = open(snddev, O_WRONLY, 0);
317                 if (spkr == -1)
318                         err(1, "%s", snddev);
319                 param.play_rate = DSP_RATE;
320                 param.play_format = AFMT_S16_NE;
321                 param.rec_rate = 0;
322                 param.rec_format = 0;
323                 if (!oflag && ioctl(spkr, AIOSFMT, &param) != 0)
324                         err(1, "%s: set format", snddev);
325                 alloc_soundbuf(&tone_dot, dot_clock, 1);
326                 alloc_soundbuf(&tone_dash, DASH_LEN * dot_clock, 1);
327                 alloc_soundbuf(&tone_silence, dot_clock, 0);
328                 alloc_soundbuf(&tone_letter_silence, word_clock, 0);
329         } else
330         if (device) {
331                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
332                         perror("open tty line");
333                         exit(1);
334                 }
335                 if (tcgetattr(line, &otty) == -1) {
336                         perror("tcgetattr() failed");
337                         exit(1);
338                 }
339                 ntty = otty;
340                 ntty.c_cflag |= CLOCAL;
341                 tcsetattr(line, TCSANOW, &ntty);
342                 lflags = fcntl(line, F_GETFL);
343                 lflags &= ~O_NONBLOCK;
344                 fcntl(line, F_SETFL, &lflags);
345                 ioctl(line, TIOCMGET, &lflags);
346                 lflags &= ~TIOCM_RTS;
347                 olflags = lflags;
348                 ioctl(line, TIOCMSET, &lflags);
349                 (void)signal(SIGHUP, sighandler);
350                 (void)signal(SIGINT, sighandler);
351                 (void)signal(SIGQUIT, sighandler);
352                 (void)signal(SIGTERM, sighandler);
353         }
354
355         argc -= optind;
356         argv += optind;
357
358         if (setlocale(LC_CTYPE, "") != NULL &&
359             *(codeset = nl_langinfo(CODESET)) != '\0') {
360                 if (strcmp(codeset, "KOI8-R") == 0)
361                         hightab = koi8rtab;
362                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
363                          strcmp(codeset, "ISO8859-15") == 0)
364                         hightab = iso8859tab;
365         }
366
367         if (lflag)
368                 printf("m");
369         if (*argv) {
370                 do {
371                         prosign = 0;
372                         for (p = *argv; *p; ++p) {
373                                 if (eflag)
374                                         putchar(*p);
375                                 if (*p == '<' || *p == '>') {
376                                         prosign = *p == '<';
377                                         continue;
378                                 }
379                                 if (strchr("> \r\n", *(p + 1)) != NULL)
380                                         prosign = 0;
381                                 morse(*p, prosign);
382                         }
383                         if (eflag)
384                                 putchar(' ');
385                         morse(' ', 0);
386                 } while (*++argv);
387         } else {
388                 prosign = 0;
389                 while ((ch = getchar()) != EOF) {
390                         if (eflag)
391                                 putchar(ch);
392                         if (ch == '<') {
393                                 prosign = 1;
394                                 continue;
395                         }
396                         if (prosign) {
397                                 int tch;
398
399                                 tch = getchar();
400                                 if (strchr("> \r\n", tch) != NULL)
401                                         prosign = 0;
402                                 if (tch != '>')
403                                         ungetc(tch, stdin);
404                         }
405                         morse(ch, prosign);
406                 }
407         }
408         if (device)
409                 tcsetattr(line, TCSANOW, &otty);
410         exit(0);
411 }
412
413 void
414 alloc_soundbuf(struct tone_data *tone, double len, int on)
415 {
416         int samples, i;
417
418         samples = DSP_RATE * len;
419         tone->len = samples * sizeof(*tone->data);
420         tone->data = malloc(tone->len);
421         if (tone->data == NULL)
422                 err(1, NULL);
423         if (!on) {
424                 bzero(tone->data, tone->len);
425                 return;
426         }
427
428         /*
429          * We create a sinus with the specified frequency and smooth
430          * the edges to reduce key clicks.
431          */
432         for (i = 0; i < samples; i++) {
433                 double filter = 1;
434
435 #define FILTER_SAMPLES (DSP_RATE * 8 / 1000)    /* 8 ms ramp time */
436                 if (i < FILTER_SAMPLES || i > samples - FILTER_SAMPLES) {
437                         int fi = i;
438
439                         if (i > FILTER_SAMPLES)
440                                 fi = samples - i;
441 #if defined(TRIANGLE_FILTER)
442                         /*
443                          * Triangle envelope
444                          */
445                         filter = (double)fi / FILTER_SAMPLES;
446 #elif defined(GAUSS_FILTER)
447                         /*
448                          * Gauss envelope
449                          */
450                         filter = exp(-4.0 *
451                                      pow((double)(FILTER_SAMPLES - fi) /
452                                          FILTER_SAMPLES, 2));
453 #else
454                         /*
455                          * Cosine envelope
456                          */
457                         filter = (1 + cos(M_PI * (FILTER_SAMPLES - fi) / FILTER_SAMPLES)) / 2;
458 #endif
459                 }
460                 tone->data[i] = 32767 * sin((double)i / samples * len * freq * 2 * M_PI) *
461                     filter;
462         }
463 }
464
465 void
466 morse(char c, int prosign)
467 {
468         const struct morsetab *m;
469
470         if (isalpha((unsigned char)c))
471                 c = tolower((unsigned char)c);
472         if ((c == '\r') || (c == '\n'))
473                 c = ' ';
474         if (c == ' ') {
475                 if (pflag) {
476                         play(" ", 0);
477                         return;
478                 } else if (device) {
479                         ttyout(" ", 0);
480                         return;
481                 } else if (lflag) {
482                         printf("\n");
483                 } else {
484                         show("", 0);
485                         return;
486                 }
487         }
488         for (m = ((unsigned char)c < 0x80? mtab: hightab);
489              m != NULL && m->inchar != '\0';
490              m++) {
491                 if (m->inchar == c) {
492                         if (pflag) {
493                                 play(m->morse, prosign);
494                         } else if (device) {
495                                 ttyout(m->morse, prosign);
496                         } else
497                                 show(m->morse, prosign);
498                 }
499         }
500 }
501
502 void
503 show(const char *s, int prosign)
504 {
505         if (lflag) {
506                 printf("%s ", s);
507                 return;
508         } else if (sflag)
509                 printf(" %s", s);
510         else
511                 for (; *s; ++s)
512                         printf(" %s", *s == '.' ? "dit" : "dah");
513         if (!prosign)
514                 printf("\n");
515 }
516
517 void
518 play(const char *s, int prosign)
519 {
520         const char *c;
521         int duration;
522         struct tone_data *tone;
523
524         /*
525          * We don't need to usleep() here, as the sound device blocks.
526          */
527         for (c = s; *c != '\0'; c++) {
528                 switch (*c) {
529                 case '.':
530                         duration = 1;
531                         tone = &tone_dot;
532                         break;
533                 case '-':
534                         duration = 1;
535                         tone = &tone_dash;
536                         break;
537                 case ' ':
538                         duration = WORD_SPACE;
539                         tone = &tone_letter_silence;
540                         break;
541                 default:
542                         errx(1, "invalid morse digit");
543                 }
544                 while (duration-- > 0)
545                         write(spkr, tone->data, tone->len);
546                 /* Only space within a symbol */
547                 if (c[1] != '\0' || prosign)
548                         write(spkr, tone_silence.data, tone_silence.len);
549         }
550         if (prosign)
551                 return;
552         duration = CHAR_SPACE;
553         while (duration-- > 0)
554                 write(spkr, tone_letter_silence.data, tone_letter_silence.len);
555
556         /* Sync out the audio data with other output */
557         if (!oflag)
558                 ioctl(spkr, SNDCTL_DSP_SYNC, NULL);
559 }
560
561 void
562 ttyout(const char *s, int prosign)
563 {
564         const char *c;
565         int duration, on, lflags;
566
567         for (c = s; *c != '\0'; c++) {
568                 switch (*c) {
569                 case '.':
570                         on = 1;
571                         duration = dot_clock;
572                         break;
573                 case '-':
574                         on = 1;
575                         duration = dot_clock * DASH_LEN;
576                         break;
577                 case ' ':
578                         on = 0;
579                         duration = word_clock * WORD_SPACE;
580                         break;
581                 default:
582                         on = 0;
583                         duration = 0;
584                 }
585                 if (on) {
586                         ioctl(line, TIOCMGET, &lflags);
587                         lflags |= TIOCM_RTS;
588                         ioctl(line, TIOCMSET, &lflags);
589                 }
590                 duration *= 1000000;
591                 if (duration)
592                         usleep(duration);
593                 ioctl(line, TIOCMGET, &lflags);
594                 lflags &= ~TIOCM_RTS;
595                 ioctl(line, TIOCMSET, &lflags);
596                 duration = dot_clock * 1000000;
597                 /* Only space within a symbol */
598                 if (c[1] != '\0' || prosign)
599                         usleep(duration);
600         }
601         if (!prosign) {
602                 duration = word_clock * CHAR_SPACE * 1000000;
603                 usleep(duration);
604         }
605 }
606
607 void
608 sighandler(int signo)
609 {
610
611         ioctl(line, TIOCMSET, &olflags);
612         tcsetattr(line, TCSANOW, &otty);
613
614         signal(signo, SIG_DFL);
615         (void)kill(getpid(), signo);
616 }