blob: 5aacaa1e782c9d50ec5026131653e990fe6cfcff [file] [log] [blame]
Georg Brandl22fff432009-10-27 20:19:02 +00001#! /usr/bin/env python
2
3# DAH should be three DOTs.
4# Space between DOTs and DAHs should be one DOT.
5# Space between two letters should be one DAH.
6# Space between two words should be DOT DAH DAH.
7
8import sys, math, aifc
9from contextlib import closing
10
11DOT = 30
12DAH = 3 * DOT
13OCTAVE = 2 # 1 == 441 Hz, 2 == 882 Hz, ...
14
15morsetab = {
16 'A': '.-', 'a': '.-',
17 'B': '-...', 'b': '-...',
18 'C': '-.-.', 'c': '-.-.',
19 'D': '-..', 'd': '-..',
20 'E': '.', 'e': '.',
21 'F': '..-.', 'f': '..-.',
22 'G': '--.', 'g': '--.',
23 'H': '....', 'h': '....',
24 'I': '..', 'i': '..',
25 'J': '.---', 'j': '.---',
26 'K': '-.-', 'k': '-.-',
27 'L': '.-..', 'l': '.-..',
28 'M': '--', 'm': '--',
29 'N': '-.', 'n': '-.',
30 'O': '---', 'o': '---',
31 'P': '.--.', 'p': '.--.',
32 'Q': '--.-', 'q': '--.-',
33 'R': '.-.', 'r': '.-.',
34 'S': '...', 's': '...',
35 'T': '-', 't': '-',
36 'U': '..-', 'u': '..-',
37 'V': '...-', 'v': '...-',
38 'W': '.--', 'w': '.--',
39 'X': '-..-', 'x': '-..-',
40 'Y': '-.--', 'y': '-.--',
41 'Z': '--..', 'z': '--..',
42 '0': '-----', ',': '--..--',
43 '1': '.----', '.': '.-.-.-',
44 '2': '..---', '?': '..--..',
45 '3': '...--', ';': '-.-.-.',
46 '4': '....-', ':': '---...',
47 '5': '.....', "'": '.----.',
48 '6': '-....', '-': '-....-',
49 '7': '--...', '/': '-..-.',
50 '8': '---..', '(': '-.--.-',
51 '9': '----.', ')': '-.--.-',
52 ' ': ' ', '_': '..--.-',
53}
54
55nowave = b'\0' * 200
56
57# If we play at 44.1 kHz (which we do), then if we produce one sine
58# wave in 100 samples, we get a tone of 441 Hz. If we produce two
59# sine waves in these 100 samples, we get a tone of 882 Hz. 882 Hz
60# appears to be a nice one for playing morse code.
61def mkwave(octave):
62 sinewave = bytearray()
63 for i in range(100):
64 val = int(math.sin(math.pi * i * octave / 50.0) * 30000)
65 sinewave.extend([(val >> 8) & 255, val & 255])
66 return bytes(sinewave)
67
68defaultwave = mkwave(OCTAVE)
69
70def main():
71 import getopt
72 try:
73 opts, args = getopt.getopt(sys.argv[1:], 'o:p:')
74 except getopt.error:
75 sys.stderr.write('Usage ' + sys.argv[0] +
76 ' [ -o outfile ] [ -p octave ] [ words ] ...\n')
77 sys.exit(1)
78 wave = defaultwave
79 outfile = 'morse.aifc'
80 for o, a in opts:
81 if o == '-o':
82 outfile = a
83 if o == '-p':
84 wave = mkwave(int(a))
85 with closing(aifc.open(outfile, 'w')) as fp:
86 fp.setframerate(44100)
87 fp.setsampwidth(2)
88 fp.setnchannels(1)
89 if args:
90 source = [' '.join(args)]
91 else:
92 source = iter(sys.stdin.readline, '')
93 for line in source:
94 mline = morse(line)
95 play(mline, fp, wave)
96
97# Convert a string to morse code with \001 between the characters in
98# the string.
99def morse(line):
100 res = ''
101 for c in line:
102 try:
103 res += morsetab[c] + '\001'
104 except KeyError:
105 pass
106 return res
107
108# Play a line of morse code.
109def play(line, fp, wave):
110 for c in line:
111 if c == '.':
112 sine(fp, DOT, wave)
113 elif c == '-':
114 sine(fp, DAH, wave)
115 else: # space
116 pause(fp, DAH + DOT)
117 pause(fp, DOT)
118
119def sine(fp, length, wave):
120 for i in range(length):
121 fp.writeframesraw(wave)
122
123def pause(fp, length):
124 for i in range(length):
125 fp.writeframesraw(nowave)
126
127if __name__ == '__main__':
128 main()