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.7 2008/03/30 16:45:04 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
[] = {
114 {'!', "-.-.--"}, /* KW */
118 {'=', "-...-"}, /* BT */
121 {'(', "-.--."}, /* KN */
124 {'+', ".-.-."}, /* AR */
127 {'@', ".--.-."}, /* AC */
133 static const struct morsetab iso8859tab
[] = {
148 static const struct morsetab koi8rtab
[] = {
150 * the cyrillic alphabet; you'll need a KOI8R font in order
151 * to see the actual characters
154 {'Â', "-..."}, /* be */
155 {'×', ".--"}, /* ve */
156 {'Ç', "--."}, /* ge */
157 {'Ä', "-.."}, /* de */
159 {'£', "."}, /* yo, the same as ye */
160 {'Ö', "...-"}, /* she */
161 {'Ú', "--.."}, /* ze */
163 {'Ê', ".---"}, /* i kratkoye */
164 {'Ë', "-.-"}, /* ka */
165 {'Ì', ".-.."}, /* el */
166 {'Í', "--"}, /* em */
167 {'Î', "-."}, /* en */
168 {'Ï', "---"}, /* o */
169 {'Ð', ".--."}, /* pe */
170 {'Ò', ".-."}, /* er */
171 {'Ó', "..."}, /* es */
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 */
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);
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
)
232 while ((ch
= getopt(argc
, argv
, GETOPTOPTS
)) != -1)
239 setvbuf(stdout
, 0, _IONBF
, 0);
261 fputs(USAGE
, stderr
);
264 if (pflag
+ !!device
+ sflag
> 1) {
265 fputs("morse: only one of -o, -p, -d and -s allowed\n", stderr
);
268 if ((pflag
|| device
) && ((wpm
< 1) || (wpm
> 60))) {
269 fputs("morse: insane speed\n", stderr
);
272 if ((pflag
|| device
) && (freq
== 0))
274 if (pflag
|| device
) {
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
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 */
286 if (snddev
== NULL
) {
289 else /* only pflag */
294 snd_chan_param param
;
296 if (oflag
&& strcmp(snddev
, "-") == 0)
297 spkr
= STDOUT_FILENO
;
299 spkr
= open(snddev
, O_WRONLY
, 0);
301 err(1, "%s", snddev
);
302 param
.play_rate
= DSP_RATE
;
303 param
.play_format
= AFMT_S16_NE
;
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);
313 if ((line
= open(device
, O_WRONLY
| O_NONBLOCK
)) == -1) {
314 perror("open tty line");
317 if (tcgetattr(line
, &otty
) == -1) {
318 perror("tcgetattr() failed");
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
;
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
);
340 if (setlocale(LC_CTYPE
, "") != NULL
&&
341 *(codeset
= nl_langinfo(CODESET
)) != '\0') {
342 if (strcmp(codeset
, "KOI8-R") == 0)
344 else if (strcmp(codeset
, "ISO8859-1") == 0 ||
345 strcmp(codeset
, "ISO8859-15") == 0)
346 hightab
= iso8859tab
;
352 for (p
= *argv
; *p
; ++p
) {
355 if (*p
== '<' || *p
== '>') {
359 if (strchr("> \r\n", *(p
+ 1)) != NULL
)
369 while ((ch
= getchar()) != EOF
) {
380 if (strchr("> \r\n", tch
) != NULL
)
389 tcsetattr(line
, TCSANOW
, &otty
);
394 alloc_soundbuf(struct tone_data
*tone
, double len
, int on
)
398 samples
= DSP_RATE
* len
;
399 tone
->len
= samples
* sizeof(*tone
->data
);
400 tone
->data
= malloc(tone
->len
);
401 if (tone
->data
== NULL
)
404 bzero(tone
->data
, tone
->len
);
409 * We create a sinus with the specified frequency and smooth
410 * the edges to reduce key clicks.
412 for (i
= 0; i
< samples
; i
++) {
415 #define FILTER_SAMPLES 100
416 if (i
< FILTER_SAMPLES
|| i
> samples
- FILTER_SAMPLES
) {
423 if (i
> FILTER_SAMPLES
)
426 pow((double)(fi
- FILTER_SAMPLES
) /
427 (0.4 * FILTER_SAMPLES
), 2));
432 if (i
< FILTER_SAMPLES
)
433 filter
= (double)i
/ FILTER_SAMPLES
;
435 filter
= (double)(samples
- i
) / FILTER_SAMPLES
;
438 tone
->data
[i
] = 32767 * sin((double)i
/ samples
* len
* freq
* 2 * M_PI
) *
444 morse(char c
, int prosign
)
446 const struct morsetab
*m
;
448 if (isalpha((unsigned char)c
))
449 c
= tolower((unsigned char)c
);
450 if ((c
== '\r') || (c
== '\n'))
464 for (m
= ((unsigned char)c
< 0x80? mtab
: hightab
);
465 m
!= NULL
&& m
->inchar
!= '\0';
467 if (m
->inchar
== c
) {
469 play(m
->morse
, prosign
);
471 ttyout(m
->morse
, prosign
);
473 show(m
->morse
, prosign
);
479 show(const char *s
, int prosign
)
485 printf(" %s", *s
== '.' ? "dit" : "dah");
491 play(const char *s
, int prosign
)
495 struct tone_data
*tone
;
498 * We don't need to usleep() here, as the sound device blocks.
500 for (c
= s
; *c
!= '\0'; c
++) {
511 duration
= WORD_SPACE
;
512 tone
= &tone_silence
;
515 errx(1, "invalid morse digit");
517 while (duration
-- > 0)
518 write(spkr
, tone
->data
, tone
->len
);
519 write(spkr
, tone_silence
.data
, tone_silence
.len
);
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
);
527 /* Sync out the audio data with other output */
529 ioctl(spkr
, SNDCTL_DSP_SYNC
, NULL
);
533 ttyout(const char *s
, int prosign
)
536 int duration
, on
, lflags
;
538 for (c
= s
; *c
!= '\0'; c
++) {
542 duration
= dot_clock
;
546 duration
= dot_clock
* DASH_LEN
;
550 duration
= dot_clock
* WORD_SPACE
;
557 ioctl(line
, TIOCMGET
, &lflags
);
559 ioctl(line
, TIOCMSET
, &lflags
);
564 ioctl(line
, TIOCMGET
, &lflags
);
565 lflags
&= ~TIOCM_RTS
;
566 ioctl(line
, TIOCMSET
, &lflags
);
567 duration
= dot_clock
* 1000000;
571 duration
= dot_clock
* (CHAR_SPACE
- 1) * 1000000;
577 sighandler(int signo
)
580 ioctl(line
, TIOCMSET
, &olflags
);
581 tcsetattr(line
, TCSANOW
, &otty
);
583 signal(signo
, SIG_DFL
);
584 (void)kill(getpid(), signo
);