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