|
import gradio as gr |
|
import numpy as np |
|
import time |
|
from scipy.io import wavfile |
|
import io |
|
|
|
|
|
MORSE_CODE_DICT = { |
|
'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': '-----', ' ': ' ' |
|
} |
|
|
|
|
|
MORSE_TO_CHAR = {v: k for k, v in MORSE_CODE_DICT.items()} |
|
|
|
|
|
def decode_morse_from_audio(audio_data): |
|
if audio_data is None: |
|
return "", "" |
|
|
|
|
|
sample_rate, data = audio_data |
|
if len(data.shape) > 1: |
|
data = data.mean(axis=1) |
|
|
|
|
|
data = data / np.max(np.abs(data)) |
|
|
|
|
|
threshold = 0.1 |
|
signal = data > threshold |
|
|
|
|
|
dit_length = int(sample_rate * 0.1) |
|
dah_length = dit_length * 3 |
|
space_length = dit_length * 7 |
|
|
|
morse_code = "" |
|
decoded_text = "" |
|
i = 0 |
|
|
|
while i < len(signal) - dit_length: |
|
if signal[i]: |
|
|
|
start = i |
|
while i < len(signal) and signal[i]: |
|
i += 1 |
|
duration = i - start |
|
|
|
|
|
if duration >= dah_length: |
|
morse_code += "-" |
|
elif duration >= dit_length: |
|
morse_code += "." |
|
|
|
|
|
else: |
|
start = i |
|
while i < len(signal) and not signal[i]: |
|
i += 1 |
|
pause = i - start |
|
|
|
if pause >= space_length: |
|
if morse_code: |
|
decoded_text += MORSE_TO_CHAR.get(morse_code, "?") |
|
decoded_text += " " |
|
morse_code = "" |
|
elif pause >= dit_length and morse_code: |
|
decoded_text += MORSE_TO_CHAR.get(morse_code, "?") |
|
morse_code = "" |
|
|
|
i += 1 |
|
|
|
|
|
if morse_code: |
|
decoded_text += MORSE_TO_CHAR.get(morse_code, "?") |
|
|
|
return morse_code, decoded_text.strip() |
|
|
|
|
|
def generate_alphabet_html(decoded_text): |
|
html = "<div style='font-family: monospace; font-size: 16px;'>" |
|
for char in MORSE_CODE_DICT.keys(): |
|
color = "red" if char in decoded_text.upper() else "black" |
|
html += f"<span style='color: {color}; margin: 5px;'>{char}: {MORSE_CODE_DICT[char]}</span>" |
|
if char in "AEIMQUZ": |
|
html += "<br>" |
|
html += "</div>" |
|
return html |
|
|
|
|
|
def stream_morse_decoder(audio): |
|
morse, text = decode_morse_from_audio(audio) |
|
alphabet_html = generate_alphabet_html(text) |
|
return morse, text, alphabet_html |
|
|
|
|
|
with gr.Blocks(title="Morse Code Decoder") as demo: |
|
gr.Markdown("# Morse Code Decoder") |
|
gr.Markdown("Speak or play Morse code into your microphone to decode it live!") |
|
|
|
with gr.Row(): |
|
audio_input = gr.Audio(source="microphone", type="numpy", streaming=True, label="Live Audio Input") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
morse_output = gr.Textbox(label="Detected Morse Code", interactive=False) |
|
text_output = gr.Textbox(label="Decoded Text", interactive=False) |
|
with gr.Column(): |
|
alphabet_display = gr.HTML(label="Morse Alphabet (Highlighted)") |
|
|
|
|
|
audio_input.stream( |
|
fn=stream_morse_decoder, |
|
inputs=[audio_input], |
|
outputs=[morse_output, text_output, alphabet_display], |
|
_js="() => { return [navigator.mediaDevices.getUserMedia({ audio: true })];" |
|
) |
|
|
|
|
|
demo.launch() |