games: Remove (void) casts.
[dragonfly.git] / games / morse / morse.c
blob1a8c5a2364a86862eed6970a0be7596c9cc7ee93
1 /*-
2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
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.
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.
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 $
35 * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
36 * <lyndon@orthanc.com>
39 #include <sys/time.h>
40 #include <sys/soundcard.h>
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>
55 struct morsetab {
56 char inchar;
57 const char *morse;
60 static const struct morsetab mtab[] = {
62 /* letters */
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', "--.."},
91 /* digits */
93 {'0', "-----"},
94 {'1', ".----"},
95 {'2', "..---"},
96 {'3', "...--"},
97 {'4', "....-"},
98 {'5', "....."},
99 {'6', "-...."},
100 {'7', "--..."},
101 {'8', "---.."},
102 {'9', "----."},
104 /* punctuation */
106 {',', "--..--"},
107 {'.', ".-.-.-"},
108 {'?', "..--.."},
109 {'!', "-.-.--"}, /* KW */
110 {'/', "-..-."},
111 {'-', "-....-"},
112 {'_', "..--.."},
113 {'=', "-...-"}, /* BT */
114 {':', "---..."},
115 {';', "-.-.-."},
116 {'(', "-.--."}, /* KN */
117 {')', "-.--.-"},
118 {'$', "...-..-"},
119 {'+', ".-.-."}, /* AR */
120 {'\'', ".----."},
121 {'"', ".-..-."},
122 {'@', ".--.-."}, /* AC */
124 {'\0', ""}
128 static const struct morsetab iso8859tab[] = {
129 {'á', ".--.-"},
130 {'à', ".--.-"},
131 {'â', ".--.-"},
132 {'ä', ".-.-"},
133 {'ç', "-.-.."},
134 {'é', "..-.."},
135 {'è', "..-.."},
136 {'ê', "-..-."},
137 {'ö', "---."},
138 {'ü', "..--"},
140 {'\0', ""}
143 static const struct morsetab koi8rtab[] = {
145 * the cyrillic alphabet; you'll need a KOI8R font in order
146 * to see the actual characters
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 */
181 {'\0', ""}
184 struct tone_data {
185 int16_t *data;
186 size_t len;
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);
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"
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 */
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;
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;
219 static const struct morsetab *hightab;
222 main(int argc, char **argv)
224 int ch, lflags;
225 int prosign;
226 char *p, *codeset;
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);
266 if (sflag && lflag) {
267 fputs("morse: only one of -l and -s allowed\n", stderr);
268 exit(1);
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);
274 if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (farnsworth > 60))) {
275 fputs("morse: insane speed\n", stderr);
276 exit(1);
278 if ((pflag || device) && (freq == 0))
279 freq = FREQUENCY;
280 if (pflag || device) {
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
287 dot_clock = wpm / 1.2; /* dots/sec */
288 dot_clock = 1 / dot_clock; /* duration of a dot */
290 word_clock = dot_clock;
293 * This is how to get to this formula:
294 * PARIS = 22 dit (symbols) + 9 symbol spaces = 31 symbol times
295 * + 19 space times.
297 * The symbol times are in dot_clock, so the spaces have to
298 * make up to reach the farnsworth time.
300 if (farnsworth > 0)
301 word_clock = (60.0 / farnsworth - 31 * dot_clock) / 19;
303 if (snddev == NULL) {
304 if (oflag)
305 snddev = "-";
306 else /* only pflag */
307 snddev = "/dev/dsp";
310 if (pflag) {
311 snd_chan_param param;
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);
335 if (tcgetattr(line, &otty) == -1) {
336 perror("tcgetattr() failed");
337 exit(1);
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 signal(SIGHUP, sighandler);
350 signal(SIGINT, sighandler);
351 signal(SIGQUIT, sighandler);
352 signal(SIGTERM, sighandler);
355 argc -= optind;
356 argv += optind;
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;
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;
379 if (strchr("> \r\n", *(p + 1)) != NULL)
380 prosign = 0;
381 morse(*p, prosign);
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;
396 if (prosign) {
397 int tch;
399 tch = getchar();
400 if (strchr("> \r\n", tch) != NULL)
401 prosign = 0;
402 if (tch != '>')
403 ungetc(tch, stdin);
405 morse(ch, prosign);
408 if (device)
409 tcsetattr(line, TCSANOW, &otty);
410 exit(0);
413 void
414 alloc_soundbuf(struct tone_data *tone, double len, int on)
416 int samples, i;
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;
429 * We create a sinus with the specified frequency and smooth
430 * the edges to reduce key clicks.
432 for (i = 0; i < samples; i++) {
433 double filter = 1;
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;
439 if (i > FILTER_SAMPLES)
440 fi = samples - i;
441 #if defined(TRIANGLE_FILTER)
443 * Triangle envelope
445 filter = (double)fi / FILTER_SAMPLES;
446 #elif defined(GAUSS_FILTER)
448 * Gauss envelope
450 filter = exp(-4.0 *
451 pow((double)(FILTER_SAMPLES - fi) /
452 FILTER_SAMPLES, 2));
453 #else
455 * Cosine envelope
457 filter = (1 + cos(M_PI * (FILTER_SAMPLES - fi) / FILTER_SAMPLES)) / 2;
458 #endif
460 tone->data[i] = 32767 * sin((double)i / samples * len * freq * 2 * M_PI) *
461 filter;
465 void
466 morse(char c, int prosign)
468 const struct morsetab *m;
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;
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);
502 void
503 show(const char *s, int prosign)
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");
517 void
518 play(const char *s, int prosign)
520 const char *c;
521 int duration;
522 struct tone_data *tone;
525 * We don't need to usleep() here, as the sound device blocks.
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");
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);
550 if (prosign)
551 return;
552 duration = CHAR_SPACE;
553 while (duration-- > 0)
554 write(spkr, tone_letter_silence.data, tone_letter_silence.len);
556 /* Sync out the audio data with other output */
557 if (!oflag)
558 ioctl(spkr, SNDCTL_DSP_SYNC, NULL);
561 void
562 ttyout(const char *s, int prosign)
564 const char *c;
565 int duration, on, lflags;
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;
585 if (on) {
586 ioctl(line, TIOCMGET, &lflags);
587 lflags |= TIOCM_RTS;
588 ioctl(line, TIOCMSET, &lflags);
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);
601 if (!prosign) {
602 duration = word_clock * CHAR_SPACE * 1000000;
603 usleep(duration);
607 void
608 sighandler(int signo)
611 ioctl(line, TIOCMSET, &olflags);
612 tcsetattr(line, TCSANOW, &otty);
614 signal(signo, SIG_DFL);
615 kill(getpid(), signo);