I've seen people do this using an Arduino with USB libraries... talk about overkill. Every computer has the ability to do this without any external processing, which I interpret as, without any additional expense. You can send on/off signals by touching the DTR/DSR lines together (or, the RTS/CTS lines together) of an ordinary serial port. I believe this is quite a well known trick.
A USB-to-serial cable can be had for under £2. Assuming you already have a Morse key, that's all the hardware you need. If you don't want to sacrifice the cable, you can indulge in a 9-pin plug to connect the two together. DTR/DSR are pins 4 and 6, RTS/CTS are pins 7 and 8.
To use it as a keyboard, I wrote a little Python script.
import serial
import pyaudio
import numpy
import ctypes
user32 = ctypes.windll.user32
key = serial.Serial(9)# COM port
ditlength=12# determines character speed
spacelength=60# ~5*ditlength, longer for farnsworth pause
a=1.99# ~700Hz, a = 2*cos(2*pi* Pitch / 44100)# A-Z 0-9 .,/?=+
lookup={".-":65,"-...":66,"-.-.":67,"-..":68,".":69,"..-.":70,"--.":71,"....":72,"..":73,".---":74,"-.-":75,".-..":76,"--":77,"-.":78,"---":79,".--.":80,"--.-":81,".-.":82,"...":83,"-":84,"..-":85,"...-":86,".--":87,"-..-":88,"-.--":89,"--..":90,".----":49,"..---":50,"...--":51,"....-":52,".....":53,"-....":54,"--...":55,"---..":56,"----.":57,"-----":48,".-.-.-":190,"--..--":188,"-..-.":191,"..--..":191+256,"-...-":187,".-.-.":187+256,"........":8}deftypechar(keycode):if keycode>255:
user32.keybd_event(16,0,0,0)# hold shift
user32.keybd_event(keycode-256,0,0,0)
user32.keybd_event(keycode-256,0,2,0)
user32.keybd_event(16,0,2,0)else:
user32.keybd_event(keycode,0,0,0)
user32.keybd_event(keycode,0,2,0)defprocess(in_data, frame_count, time_info, status):global s1, s2, a, vol, down, up, stack
v=key.getDSR()if v:
down+=1
up=0elif down:
stack+=("-"if down>ditlength else".")
down=0else:
up+=1if up==1+ditlength:if stack in lookup: typechar(lookup[stack])if len(stack)>7: up+=spacelength
stack=''if up==spacelength:
typechar(32)
v=float(v)
data=[]for i in range(frame_count):if v!=vol:if i<30: vol+=(v-vol)/20else: vol=v
output = a * s1 - s2;
s2 = s1;
s1 = output;
data.append(numpy.uint8(vol*output*128))return(bytes(data), pyaudio.paContinue)
s1=s2=a*0.2
vol=0
down=0
up=1+spacelength
stack=''
pyaudio.PyAudio().open(
format=pyaudio.paInt8,
channels=1,
rate=44100,
frames_per_buffer=512,
output=True,
stream_callback=process
).start_stream()
input()
Pretty straightforward really. It uses my favourite recurrence relation to make a sine wave, and adds a little lowpass/envelope to the audio to prevent clicking. Those speed parameters work well for typing at around 15 to 20wpm. They are in units of buffersize divided by samplerate, so if you change either you'll need to readjust your speeds. The character set is quite limited but nothing's stopping you adding to it or even inventing your own codes for other keys (see keycode references). Typing the error prosign (eight dots) acts as a backspace.
I suspect that controlling every aspect of a computer via Morse code is something that's been done before. There may even be a standard for it. I, however, just wanted a fun way to practice.