2

I have created a morse code generator that converts English sentences into morse code. It also converts this text based morse code into an audio file. If the character is a dot, I append a dot.wave file to the output wave file followed by a dash.wav file if the next character is a dash.

I now want to open this wave file and read its content to figure out the order in which these dashes and dots are placed.

I have tried the following code:

waveFile = wave.open(r"C:\Users\Gaurav Keswani\Documents\Eclipse\Morse Code    Converter\src\resources\sound\morse.wav", 'r')
x =waveFile.readframes(20)
print (struct.unpack("<40H", x))

This gives me the output as:

(65089, 65089, 3093, 3093, 11895, 11895, 18629, 18629, 25196, 25196, 29325, 29325, 31986, 31986, 32767, 32767, 31265, 31265, 27532, 27532, 22485, 22485, 15762, 15762, 7895, 7895, 103, 103, 57228, 57228, 49571, 49571, 42790, 42790, 37667, 37667, 34362, 34362, 32776, 32776)

I don't know what to make of this output. Can anyone help?

sk11
  • 1,779
  • 1
  • 17
  • 29
Gaurav Keswani
  • 431
  • 4
  • 14
  • 1
    Imagine plotting it and you’ll see a wave. Sounds are waves. You probably want to see where the amplitude of the wave changes and record how much time passes between each significant change in amplitude and base whether it was a dot or dash on that. – icktoofay Sep 23 '14 at 19:04
  • Hey! Thanks for the reply. I am really a newbie when it comes to this thing. Could you explain in a little more detail? – Gaurav Keswani Sep 23 '14 at 19:07
  • 1
    @GauravKeswani: If you don't get the concept, don't just imagine plotting it, actually plot it. If you don't know how to use something like `matplotlib`, just export the frames to a giant one-column CSV file, open it in Excel/LibreOffice/Numbers/whatever, and graph it. Then you can see visually what dots, dashes, and spaces look like, and start thinking about an algorithm to distinguish them. – abarnert Sep 23 '14 at 19:09
  • Also, you might want to search for existing morse-code libraries (or open source apps with code you can borrow, if there aren't any). I don't know if there are any in Python, but I'll bet there's at least one in C that you could either read to get the concept, or just access via `ctypes`… – abarnert Sep 23 '14 at 19:10
  • 2
    http://stackoverflow.com/questions/18625085/how-to-plot-a-wav-file – tom10 Sep 23 '14 at 19:10
  • 1
    Actually, in your case, if you only want to detect exact copies of `dot.wav` and `dash.wav`, and you're not using any lossy compression, the algorithm should be a lot simpler: Just decode and read those two files into lists of numbers, then it's just a simple substring search (except that your "strings" are arrays of 16-bit numbers, not characters). – abarnert Sep 23 '14 at 19:12

1 Answers1

0

If you want a general solution to detecting Morse code, you are going to have to take a look at what it looks like as a waveform (tom10's link to this question should help here if you can install numpy and matplotlib; if not, you can use the stdlib's csv module to export a file that you can use in your favorite spreadsheet program); work out how you as a human can distinguish dots, dashes, and spaces; turn that into an algorithm (a series of steps that even a literal-minded moron can follow); then turn that algorithm into code. Or you may be able to find a library that's already done this for you.

But for your specific case, you only need to detect exact copies of the contents of dot.wav and dash.wav within your larger file. (At least assuming you're not using any lossy compression, which usually you aren't in .wav files.) So, this is really just a substring search.

Think about how you'd detect the strings 'dot' and 'dash' within a string like 'dash dash dash dash dash dot dash dot dot dot dot dot '. For such a simple problem, you could use a stupid brute-force algorithm, and it would be fine:

def find(haystack, needle, start):
    for i in range(start, len(haystack)):
        if haystack[i:i+len(needle)] == needle:
            return i
    return len(haystack)

def decode_morse(morse):
    i = 0
    while i < len(morse):
        next_dot = find(morse, 'dot', i)
        next_dash = find(morse, 'dash', i)
        if next_dot < next_dash:
            if next_dot < len(morse):
                yield '.'
            i = next_dot
        else:
            if next_dash < len(morse):
                yield '-'
            i = next_dash

Now, if you're searching a list of numbers instead of a string, how does this have to change? Barely at all; you can slice a list, compare two lists, etc. just like you can with strings.

The only real problem you'll run into is that you don't have the whole list in memory at once, just 20 frames at a time. What happens if a dot starts in frame 19 and ends in frame 20? If your files aren't too big, this is easy to solve: just read all the frames into memory in one giant list, then search the whole thing. But otherwise, you have to do some buffering.

For example (ignoring error handling and dealing with the end of the file properly, and dealing only with dashes for simplicity—of course you have to do both of those properly in your real code):

buf = []
while True:
    while len(buf) < 2*len(dash):
        buf.extend(waveFile.readFrames(20))
    next_dash = find(buf, dot)
    if next_dash < len(buf):
        yield '.'
        buf = buf[next_dash:]
    else:
        buf = buf[-len(dash):]

We're making sure we always have at least two dash lengths in our buffer. And we always keep the leftover after the first dot or dash (if one was found) or a full dash length (if not) in the buffer, and add the next buffer to that. That's actually overkill; think it through and think through out exactly what you need to make sure we never miss a dash that falls between two buffers. But the point is, as long as you get that right, you can't miss any dots or dashes.

Community
  • 1
  • 1
abarnert
  • 354,177
  • 51
  • 601
  • 671