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
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.
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
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.5 2007/04/22 22:04:20 corecode Exp $
40 * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
41 * <lyndon@orthanc.com>
45 #include <sys/soundcard.h>
65 static const struct morsetab mtab[] = {
116 {'=', "-...-"}, /* BT */
119 {'(', "-.--."}, /* KN */
122 {'+', ".-.-."}, /* AR */
124 /* prosigns without already assigned values */
126 {'#', ".-..."}, /* AS */
127 {'@', "...-.-"}, /* SK */
128 {'*', "...-."}, /* VE */
129 {'%', "-...-.-"}, /* BK */
135 static const struct morsetab iso8859tab[] = {
150 static const struct morsetab koi8rtab[] = {
152 * the cyrillic alphabet; you'll need a KOI8R font in order
153 * to see the actual characters
156 {'Â', "-..."}, /* be */
157 {'×', ".--"}, /* ve */
158 {'Ç', "--."}, /* ge */
159 {'Ä', "-.."}, /* de */
161 {'£', "."}, /* yo, the same as ye */
162 {'Ö', "...-"}, /* she */
163 {'Ú', "--.."}, /* ze */
165 {'Ê', ".---"}, /* i kratkoye */
166 {'Ë', "-.-"}, /* ka */
167 {'Ì', ".-.."}, /* el */
168 {'Í', "--"}, /* em */
169 {'Î', "-."}, /* en */
170 {'Ï', "---"}, /* o */
171 {'Ð', ".--."}, /* pe */
172 {'Ò', ".-."}, /* er */
173 {'Ó', "..."}, /* es */
175 {'Õ', "..-"}, /* u */
176 {'Æ', "..-."}, /* ef */
177 {'È', "...."}, /* kha */
178 {'Ã', "-.-."}, /* ce */
179 {'Þ', "---."}, /* che */
180 {'Û', "----"}, /* sha */
181 {'Ý', "--.-"}, /* shcha */
182 {'Ù', "-.--"}, /* yi */
183 {'Ø', "-..-"}, /* myakhkij znak */
184 {'Ü', "..-.."}, /* ae */
185 {'À', "..--"}, /* yu */
186 {'Ñ', ".-.-"}, /* ya */
196 void alloc_soundbuf(struct tone_data *, double, int);
197 void show(const char *), play(const char *), morse(char);
198 void ttyout(const char *);
199 void sighandler(int);
201 #define GETOPTOPTS "d:ef:opP:sw:"
203 "usage: morse [-s] [-e] [-p | -o] [-P device] [-d device] [-w speed] [-f frequency] [string ...]\n"
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 */
211 static struct tone_data tone_dot, tone_dash, tone_silence;
212 #define DSP_RATE 44100
213 static const char *snddev = NULL;
217 #define WORD_SPACE (7 - CHAR_SPACE)
218 static float dot_clock;
220 struct termios otty, ntty;
223 static const struct morsetab *hightab;
226 main(int argc, char **argv)
231 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
238 setvbuf(stdout, 0, _IONBF, 0);
260 fputs(USAGE, stderr);
263 if (pflag + !!device + sflag > 1) {
264 fputs("morse: only one of -o, -p, -d and -s allowed\n", stderr);
267 if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
268 fputs("morse: insane speed\n", stderr);
271 if ((pflag || device) && (freq == 0))
273 if (pflag || device) {
275 * A note on how to get to this magic 2.4:
276 * x WPM = 50*x dits per minute (norm word "PARIS").
277 * dits per sec = dits per minute / 60, thus
278 * dits per sec = 50 * x / 60 = x / (60 / 50) = x / 2.4
280 dot_clock = wpm / 2.4; /* dots/sec */
281 dot_clock = 1 / dot_clock; /* duration of a dot */
282 dot_clock = dot_clock / 2; /* dot_clock runs at twice */
285 if (snddev == NULL) {
288 else /* only pflag */
293 snd_chan_param param;
295 if (oflag && strcmp(snddev, "-") == 0)
296 spkr = STDOUT_FILENO;
298 spkr = open(snddev, O_WRONLY, 0);
300 err(1, "%s", snddev);
301 param.play_rate = DSP_RATE;
302 param.play_format = AFMT_S16_NE;
304 param.rec_format = 0;
305 if (!oflag && ioctl(spkr, AIOSFMT, ¶m) != 0)
306 err(1, "%s: set format", snddev);
307 alloc_soundbuf(&tone_dot, dot_clock, 1);
308 alloc_soundbuf(&tone_dash, DASH_LEN * dot_clock, 1);
309 alloc_soundbuf(&tone_silence, dot_clock, 0);
312 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
313 perror("open tty line");
316 if (tcgetattr(line, &otty) == -1) {
317 perror("tcgetattr() failed");
321 ntty.c_cflag |= CLOCAL;
322 tcsetattr(line, TCSANOW, &ntty);
323 lflags = fcntl(line, F_GETFL);
324 lflags &= ~O_NONBLOCK;
325 fcntl(line, F_SETFL, &lflags);
326 ioctl(line, TIOCMGET, &lflags);
327 lflags &= ~TIOCM_RTS;
329 ioctl(line, TIOCMSET, &lflags);
330 (void)signal(SIGHUP, sighandler);
331 (void)signal(SIGINT, sighandler);
332 (void)signal(SIGQUIT, sighandler);
333 (void)signal(SIGTERM, sighandler);
339 if (setlocale(LC_CTYPE, "") != NULL &&
340 *(codeset = nl_langinfo(CODESET)) != '\0') {
341 if (strcmp(codeset, "KOI8-R") == 0)
343 else if (strcmp(codeset, "ISO8859-1") == 0 ||
344 strcmp(codeset, "ISO8859-15") == 0)
345 hightab = iso8859tab;
350 for (p = *argv; *p; ++p) {
360 while ((ch = getchar()) != EOF) {
367 tcsetattr(line, TCSANOW, &otty);
372 alloc_soundbuf(struct tone_data *tone, double len, int on)
376 samples = DSP_RATE * len;
377 tone->len = samples * sizeof(*tone->data);
378 tone->data = malloc(tone->len);
379 if (tone->data == NULL)
382 bzero(tone->data, tone->len);
387 * We create a sinus with the specified frequency and smooth
388 * the edges to reduce key clicks.
390 for (i = 0; i < samples; i++) {
393 #define FILTER_SAMPLES 100
394 if (i < FILTER_SAMPLES || i > samples - FILTER_SAMPLES) {
401 if (i > FILTER_SAMPLES)
404 pow((double)(fi - FILTER_SAMPLES) /
405 (0.4 * FILTER_SAMPLES), 2));
410 if (i < FILTER_SAMPLES)
411 filter = (double)i / FILTER_SAMPLES;
413 filter = (double)(samples - i) / FILTER_SAMPLES;
416 tone->data[i] = 32767 * sin((double)i / samples * len * freq * 2 * M_PI) *
424 const struct morsetab *m;
426 if (isalpha((unsigned char)c))
427 c = tolower((unsigned char)c);
428 if ((c == '\r') || (c == '\n'))
442 for (m = ((unsigned char)c < 0x80? mtab: hightab);
443 m != NULL && m->inchar != '\0';
445 if (m->inchar == c) {
463 printf(" %s", *s == '.' ? "dit" : "dah");
472 struct tone_data *tone;
475 * We don't need to usleep() here, as the sound device blocks.
477 for (c = s; *c != '\0'; c++) {
488 duration = WORD_SPACE;
489 tone = &tone_silence;
492 errx(1, "invalid morse digit");
494 while (duration-- > 0)
495 write(spkr, tone->data, tone->len);
496 write(spkr, tone_silence.data, tone_silence.len);
498 duration = CHAR_SPACE - 1; /* we already waited 1 after the last symbol */
499 while (duration-- > 0)
500 write(spkr, tone_silence.data, tone_silence.len);
504 ttyout(const char *s)
507 int duration, on, lflags;
509 for (c = s; *c != '\0'; c++) {
513 duration = dot_clock;
517 duration = dot_clock * DASH_LEN;
521 duration = dot_clock * WORD_SPACE;
528 ioctl(line, TIOCMGET, &lflags);
530 ioctl(line, TIOCMSET, &lflags);
535 ioctl(line, TIOCMGET, &lflags);
536 lflags &= ~TIOCM_RTS;
537 ioctl(line, TIOCMSET, &lflags);
538 duration = dot_clock * 1000000;
541 duration = dot_clock * CHAR_SPACE * 1000000;
546 sighandler(int signo)
549 ioctl(line, TIOCMSET, &olflags);
550 tcsetattr(line, TCSANOW, &otty);
552 signal(signo, SIG_DFL);
553 (void)kill(getpid(), signo);