MorseCode / app.py
nightfury's picture
Update app.py
10adbb1 verified
raw
history blame
11.8 kB
import gradio as gr
import numpy as np
import io
from scipy.io import wavfile
import speech_recognition as sr
import soundfile as sf
# Morse code dictionary (ITU standard)
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()}
# Morse code timing (in seconds)
DIT_DURATION = 0.1 # 100ms for dit
DAH_DURATION = 3 * DIT_DURATION # 300ms for dah
SPACE_DURATION = 7 * DIT_DURATION # 700ms for word space
CHAR_SPACE = DIT_DURATION # Space between characters
SAMPLE_RATE = 44100 # Standard audio sample rate
# Decode Morse from audio
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
morse_code, decoded_text = "", ""
i = 0
while i < len(signal) - int(SAMPLE_RATE * DIT_DURATION):
if signal[i]:
start = i
while i < len(signal) and signal[i]:
i += 1
duration = (i - start) / sample_rate
morse_code += "-" if duration >= DAH_DURATION else "."
else:
start = i
while i < len(signal) and not signal[i]:
i += 1
pause = (i - start) / sample_rate
if pause >= SPACE_DURATION and morse_code:
decoded_text += " "
morse_code = ""
elif pause >= DIT_DURATION 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()
# Convert text to Morse code
def text_to_morse(text):
text = text.upper()
morse = " ".join(MORSE_CODE_DICT.get(char, "?") for char in text if char in MORSE_CODE_DICT)
return morse
# Generate Morse code audio
def generate_morse_audio(morse):
audio = []
frequency = 750 # Hz for Morse tone
for symbol in morse.split():
if symbol == "/":
audio.extend([0] * int(SAMPLE_RATE * SPACE_DURATION))
else:
for char in symbol:
duration = DAH_DURATION if char == "-" else DIT_DURATION
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), False)
tone = 0.5 * np.sin(2 * np.pi * frequency * t)
audio.extend(tone)
audio.extend([0] * int(SAMPLE_RATE * DIT_DURATION)) # Space between dits/dahs
audio.extend([0] * int(SAMPLE_RATE * CHAR_SPACE)) # Space between characters
audio = np.array(audio, dtype=np.float32)
buffer = io.BytesIO()
sf.write(buffer, audio, SAMPLE_RATE, format="wav")
buffer.seek(0)
return buffer
# Speech to text
def speech_to_text(audio_path):
recognizer = sr.Recognizer()
with sr.AudioFile(audio_path) as source:
audio_data = recognizer.record(source)
try:
return recognizer.recognize_google(audio_data)
except sr.UnknownValueError:
return "Could not understand audio"
except sr.RequestError:
return "Speech recognition service unavailable"
# Highlight alphabet in UI
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
# Combined processing function
def process_input(text=None, speech=None, audio=None):
morse, decoded_text, audio_output = "", "", None
if text: # Text input
morse = text_to_morse(text)
decoded_text = text
audio_output = generate_morse_audio(morse)
elif speech: # Speech input
text = speech_to_text(speech)
morse = text_to_morse(text)
decoded_text = text
audio_output = generate_morse_audio(morse)
elif audio: # Live audio input
morse, decoded_text = decode_morse_from_audio(audio)
alphabet_html = generate_alphabet_html(decoded_text)
return morse, decoded_text, alphabet_html, audio_output
# Gradio UI with Blocks
with gr.Blocks(title="Morse Code Decoder & Generator") as demo:
gr.Markdown("# Morse Code Decoder & Generator")
gr.Markdown("Decode live Morse audio, or generate Morse from text/speech!")
with gr.Tab("Decode Live Audio"):
audio_input = gr.Audio(type="numpy", label="Live Audio Input (Use Microphone)", streaming=True)
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)
alphabet_display = gr.HTML(label="Morse Alphabet (Highlighted)")
audio_input.stream(
fn=process_input,
inputs=[audio_input],
outputs=[morse_output, text_output, alphabet_display, gr.Audio(visible=False)],
)
with gr.Tab("Generate from Text"):
text_input = gr.Textbox(label="Enter Text", placeholder="Type here...")
generate_btn = gr.Button("Generate Morse")
with gr.Row():
with gr.Column():
morse_gen_output = gr.Textbox(label="Morse Code", interactive=False)
text_gen_output = gr.Textbox(label="Original Text", interactive=False)
gen_alphabet_display = gr.HTML(label="Morse Alphabet (Highlighted)")
audio_playback = gr.Audio(label="Morse Audio Playback", interactive=False)
generate_btn.click(
fn=process_input,
inputs=[text_input],
outputs=[morse_gen_output, text_gen_output, gen_alphabet_display, audio_playback]
)
with gr.Tab("Generate from Speech"):
speech_input = gr.Audio(type="filepath", label="Speak Your Text (Record Audio)")
speech_btn = gr.Button("Convert Speech to Morse")
with gr.Row():
with gr.Column():
morse_speech_output = gr.Textbox(label="Morse Code", interactive=False)
text_speech_output = gr.Textbox(label="Transcribed Text", interactive=False)
speech_alphabet_display = gr.HTML(label="Morse Alphabet (Highlighted)")
speech_audio_playback = gr.Audio(label="Morse Audio Playback", interactive=False)
speech_btn.click(
fn=process_input,
inputs=[speech_input],
outputs=[morse_speech_output, text_speech_output, speech_alphabet_display, speech_audio_playback]
)
# Launch the app
demo.launch()
'''
import gradio as gr
import numpy as np
import time
from scipy.io import wavfile
import io
# Morse code dictionary (ITU standard)
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': '-----', ' ': ' '
}
# Reverse dictionary for decoding
MORSE_TO_CHAR = {v: k for k, v in MORSE_CODE_DICT.items()}
# Function to process audio and detect Morse code (simplified)
def decode_morse_from_audio(audio_data):
if audio_data is None:
return "", ""
# Audio comes as (sample_rate, data) from Gradio
sample_rate, data = audio_data
if len(data.shape) > 1: # Convert stereo to mono if needed
data = data.mean(axis=1)
# Normalize audio data
data = data / np.max(np.abs(data))
# Threshold for detecting signal (tweak as needed)
threshold = 0.1
signal = data > threshold
# Timing parameters (in samples, adjust based on sample_rate)
dit_length = int(sample_rate * 0.1) # 100ms for a dit
dah_length = dit_length * 3 # 300ms for a dah
space_length = dit_length * 7 # 700ms for word space
morse_code = ""
decoded_text = ""
i = 0
while i < len(signal) - dit_length:
if signal[i]:
# Measure signal duration
start = i
while i < len(signal) and signal[i]:
i += 1
duration = i - start
# Classify as dit or dah
if duration >= dah_length:
morse_code += "-"
elif duration >= dit_length:
morse_code += "."
# Check for spaces (pauses)
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
# Handle any remaining code
if morse_code:
decoded_text += MORSE_TO_CHAR.get(morse_code, "?")
return morse_code, decoded_text.strip()
# Function to generate highlighted alphabet UI
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
# Gradio streaming function
def stream_morse_decoder(audio):
morse, text = decode_morse_from_audio(audio)
alphabet_html = generate_alphabet_html(text)
return morse, text, alphabet_html
# Gradio UI with Blocks
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)")
# Event listener for streaming audio
audio_input.stream(
fn=stream_morse_decoder,
inputs=[audio_input],
outputs=[morse_output, text_output, alphabet_display],
_js="() => { return [navigator.mediaDevices.getUserMedia({ audio: true })];"
)
# Launch the app
demo.launch()
'''