3 min read

Morse Code with Python

Morse code is an awesome tool. I have always been amazed by it. I remember my mom and dad buying me a walkie-talkie that had the morse code on a sticker on the side. I would click on the beep button on the walkie-talkie to make the sound. It always reminded me of the starting theme of the old(1941) Superman cartoons. Although not from that era had an old VHS tape that I certainly watched more than a couple of times.

When a software engineer looks at the code it will be apparent to them that the code was not written by a person with the knowledge of computer coding. The code was actually developed by Samuel F. B. Morse in 1836 who was an artist.

Telegraph

Don’t get me wrong. I really like how a lot of times science gets combined with art. But I do wish it was more… binary. Yeah… That is what I was looking for.

So you may ask why  the hell is John talking about Morse code. Well the reason is that I am working on a small weekend project (one of several).

I do welcome any ideas or help with this nice little project. It will be using some DSP (Digital Signal Processing) later. Which was always my favorite subject.

The general idea is to encode text to Morse code (sound) send it via some network means to another computer which will than translate (decode) Morse sound to text.

Text to Morse Code Conversion

Of course first we need to encode our text to Morse code. A small sample of the Morse code can be found below;

The small cool script below basically turns text to sound. It is important to note the rules 3, 4 and 5 in the image above which I didn’t know about before. In the code I denoted * as the space between two letters while | as the space between two words.

import pygame
import sys

# global constants
FREQ = 44100   # same as audio CD
BITSIZE = -16  # unsigned 16 bit
CHANNELS = 2   # 1 == mono, 2 == stereo
BUFFER = 1024  # audio buffer size in no. of samples
FRAMERATE = 60 # how often to check if playback has finished

morsetab = {
    'a': '.- ',     'b': '-... ',
    'c': '-.-. ',   'd': '-.. ',
    'e': '. ',      'f': '..-. ',
    'g': '--. ',    'h': '.... ',
    'i': '.. ',     'j': '.--- ',
    'k': '-.- ',    'l': '.-.. ',
    'm': '-- ',     'n': '-. ',
    'o': '--- ',    'p': '.--. ',
    'q': '--.- ',   'r': '.-. ',
    's': '... ',    't': '- ',
    'u': '..- ',    'v': '...- ',
    'w': '.-- ',    'x': '-..- ',
    'y': '-.-- ',   'z': '--.. ',
    '0': '----- ',  ',': '--..-- ',
    '1': '.---- ',  '.': '.-.-.- ',
    '2': '..--- ',  '?': '..--.. ',
    '3': '...-- ',  ';': '-.-.-. ',
    '4': '....- ',  ':': '---... ',
    '5': '..... ',  "'": '.----. ',
    '6': '-.... ',  '-': '-....- ',
    '7': '--... ',  '/': '-..-. ',
    '8': '---.. ',  '(': '-.--.- ',
    '9': '----. ',  ')': '-.--.- ',
    ' ': '|',       '_': '..--.- ',
}

morse_sound = {
    '.': 'dot.ogg',
    '-': 'dash.ogg',
    ' ': 'short_silence.ogg',
    '*': 'very_short_silence.ogg',
    '|': 'long_silence.ogg',
}

pygame.init()
pygame.mixer.init(FREQ, BITSIZE, CHANNELS, BUFFER)


def main(argv):
    convert_string = argv[0].lower()

    play_morse_sound(code_to_sound_code(string_to_code(convert_string)))
    print(string_to_code(convert_string).replace('|', '  '))


def playsound(soundfile):
    """Play sound through default mixer channel in blocking manner.
    This will load the whole sound into memory before playback
    """
    sound = pygame.mixer.Sound(soundfile)
    clock = pygame.time.Clock()
    sound.play()
    while pygame.mixer.get_busy():
        clock.tick(FRAMERATE)


def play_morse_sound(code):
    for channel_id, dip in enumerate(code):
        try:
            sound = pygame.mixer.Sound(morse_sound[dip])
        except KeyError:
            sound = pygame.mixer.Sound(morse_sound[' '])
        playsound(sound)


def code_to_sound_code(code):
    res = code.replace('..', '.*.') 
              .replace('--', '-*-') 
              .replace('.-', '.*-') 
              .replace('-.', '-*.') 
              .replace('..', '.*.') 
              .replace('--', '-*-') 
              .replace('.-', '.*-') 
              .replace('-.', '-*.')
    return res


def string_to_code(convert_string):
    res = ''
    for c in convert_string:
        try:
            res += morsetab[c]
        except KeyError:
            pass
    return res


if __name__ == '__main__':
    main(sys.argv[1:])

In the next part I will be using some cool Python code to record and sample the sound created by this script. And in the final part I hope to decode the Morse code.

You can find the code for this post here: https://github.com/JohnRoach/morse

For now,

Have fun!