Eren Biçer
Eren Biçer

Reputation: 171

Text to Morse Code

I recently found out that you can play sounds in python. I instantly wanted to make a text to morse code program but when I try to assign winsound.Beep(600,300) to "a" and launch, it just beeps once and when i type a it does nothing.

import winsound
def dot():
    winsound.Beep(600,100)
def line():
    winsound.Beep(600,100)
a = dot(), line()    #it doesn't work when i just do
                     #a = winsound.Beep(600,100) either
                     #and it beeps at the beginning which i don't want

can you tell me how i can assign winsound.Beep() to a varible?

Upvotes: 2

Views: 1982

Answers (5)

HackJob99
HackJob99

Reputation: 109

Here's a small modification of thebjorn's answer with a complete alphabet and more precise timing parameters:

import winsound
import time

alphabet = {'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': '--..',
            '1': '.----',
            '2': '..---',
            '3': '...--',
            '4': '....-',
            '5': '.....',
            '6': '-....',
            '7': '--...',
            '8': '---..',
            '9': '----.',
            '0': '-----',
            ', ': '--..--',
            '.': '.-.-.-',
            '?': '..--..',
            '/': '-..-.',
            '-': '-....-',
            '(': '-.--.',
            ')': '-.--.-'}

unit = 100  # ms
frequency = 3000  # Hz


def play_dot():
    winsound.Beep(frequency, 1*unit)


def play_dash():
    winsound.Beep(frequency, 3*unit)


def play_char(ch):
    if ch == ' ':
        time.sleep(7*unit/1000)
        return '<space>'

    morse_value = alphabet[ch]
    for d in morse_value:
        if d == '.':
            play_dot()
        else:
            play_dash()
        time.sleep(1*unit/1000)

    return morse_value


def txt2morse(txt):
    for ch in txt.upper():
        print(play_char(ch))


if __name__ == "__main__":
    txt2morse('SOS SOS')
    txt2morse('hello world')

Upvotes: 2

thebjorn
thebjorn

Reputation: 27321

Adding a layer of indirection is often helpful. Define the character values separately from the playing, then you can loop over any text:

import winsound
import time

# define the values for the alphabet (using easy to verify strings)
alphabet = dict(
    a='.-',
    b='-...',
    c='-.-.',
    d='-..',
    e='.',
    h='....',
    l='.-..',
    o='---',
    w='.--',
    r='.-.',
    # etc.
)

How to play (single) dots and dashes (the lines are referred to as dashes):

def play_dot():
    winsound.Beep(600, 100)

def play_dash():
    winsound.Beep(500, 200)  # a bit lower frequency, double length

Play one character by looking up the morse value for the character in the alphabet and then iterate over the dots/dashes:

def play_char(ch):
    if ch == ' ':             # space is a special case
        time.sleep(0.9)       # wait for 0.9 seconds before returning
        return '<space>'

    morseval = alphabet[ch]   # morseval is now a series of dots/dashes
    for d in morseval:        # loop over them
        if d == '.':          # if it's a dot, play a dot
            play_dot()
        else:
            play_dash()
        time.sleep(0.1)       # a small break (0.1 secs) makes it easier on the ears

    return morseval           # return the morse value so we can see what was played.. 

to play a text, assuming it's ascii, just iterate through the characters and play each of them in turn:

def txt2morse(txt):
    for ch in txt.lower():    # convert the text to lower case, just in case..
        print play_char(ch),  # Python 2.7'ism to keep printing on the same line
    print

then:

>>> txt2morse('hello world')
.... . .-.. .-.. --- <space> .-- --- .-. .-.. -..

Upvotes: 1

Hugh Bothwell
Hugh Bothwell

Reputation: 56654

You are mixing up "code that runs right now" with "code I want to run later".

Your line,

a = dot(), line()

runs dot() (which beeps and returns None) then runs line() (which beeps and returns None) then assigns the result (None, None) to the variable a.

Instead try

def a():
    dot()
    line()

which creates a function which you can run later by calling a()... but it will still sound like one continuous beep, because you need to add a short pause after each tone in the definitions of dot() and line(). You might find time.sleep() useful, or possibly winsound already provides something similar.

Edit:

You can use a dispatch table (a dict that translates characters to function calls) and an input loop, like so:

MORSE_CHARS = {
    "a": (dot, line),
    "b": (dot, dot, line),
    # define other characters and space here
}

def morse(s):
    """
    Turn string s into Morse beeps
    """
    for ch in s.lower():
        for fn in MORSE_CHARS[ch]:
            fn()

def main():
    while True:
        s = input("Enter text to play (or just hit Enter to quit): ")
        if s:
            morse(s)
        else:
            break

if __name__ == "__main__":
    main()

Upvotes: 3

Kendas
Kendas

Reputation: 2243

My solution to your problem will involve passing functions as parameters to other functions. If this is unfamiliar to you, I suggest you look into functional programming with Python a little bit.

import winsound
import time

# first - create the `dot` and `line` functions
def dot():
    winsound.Beep(600, 100)
    time.sleep(0.1)               # this is in seconds

def line():
    ....

def space():
    ....

def full_stop():
    ....

# now you need some way of mapping letters to sounds
mapping = {
    "a": (dot, line),
    "b": (line, dot, dot, dot),
    ....
    " ": (space,),                 # A one-tuple is defined like this, not like `(var)`
    ".": (full_stop,)
}

# And as a final step you need a function to play the sounds
def play_morse(message):
    for character in message:
        character = character.lower()
        if character in mapping:
            for func in mapping[character]:
                func()
        else:
            print("Unknown character: '{}'".format(character))

You would use the function like so:

>>> play_morse("Hello world!")

Upvotes: 1

Nurjan
Nurjan

Reputation: 6063

In python everything is an object. If you run this:

a = dot()

Execute dot() and assign the returned value to a - in your case it's None.

However, if you want to "assign" the function dot() to a and then call a you do this:

a = dot
a()

In this case the names a and dot refer to the same object. a is now a function which you call with parentheses only.

Upvotes: 0

Related Questions