Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
spaceymonk committed Aug 21, 2022
1 parent b99ddaa commit 0dc87d5
Show file tree
Hide file tree
Showing 8 changed files with 604 additions and 0 deletions.
92 changes: 92 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
ABOUT_TEXT = """Morse Analyzer v0.1.0
This program is written by Berktug Kaan Ozkan.
Please visit project's website for more information, how to use etc.:
My GitHub: https://github.com/spaceymonk
Project's Source: https://github.com/spaceymonk/morse-analyzer
Note: this program is lack of proper exception handling / input validation. Please make sure you have a valid inputs.
"""

DEFAULTS = {
'n_fft': '1024',
'win_length': '512',
'hop_length': '256',
'apply_threshold_db': False,
'apply_freq_band': False,
'apply_time_band': False,
'threshold_db': '-80',
'freq_band_min': '',
'freq_band_max': '',
'time_band_min': '',
'time_band_max': '',
'plot_time_min': '',
'plot_time_max': '',
'plot_freq_min': '',
'plot_freq_max': '',
'plot_grids': False,
'dot_length': '',
'dash_length': '',
'letter_spacing': '',
'word_spacing': ''
}

MORSE = {
"-----": "0",
".----": "1",
"..---": "2",
"...--": "3",
"....-": "4",
".....": "5",
"-....": "6",
"--...": "7",
"---..": "8",
"----.": "9",
".-": "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",
".-.-.-": ".",
"--..--": ",",
"..--..": "?",
".----.": "'",
"-.-.--": "!",
"-..-.": "/",
"-.--.": "(",
"-.--.-": ")",
".-...": "&",
"---...": ":",
"-.-.-.": ";",
"-...-": "=",
".-.-.": "+",
"-....-": "-",
"..--.-": "_",
".-..-.": "\"",
"..-...": "^",
"...-..-": "$",
".--.-.": "@"
}
150 changes: 150 additions & 0 deletions gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import os

import matplotlib.pyplot as plt
import PySimpleGUI as sg

from config import ABOUT_TEXT, DEFAULTS


# ---------------------------------------------------- Theme Setup --------------------------------------------------- #

sg.theme("DarkBlue")
plt.style.use('dark_background')
plt.rcParams['figure.facecolor'] = "#1a2835"
plt.rcParams['figure.figsize'] = (11, 5.5)
plt.rcParams['font.family'] = 'sans'
plt.rcParams['font.size'] = 10.0
SMALLFONT = ("Helvitica", 10)
APPFONT = ('Helvitica', 12)
MONOSPACEFONT = ('Consolas', 12)


# -------------------------------------------------------------------------------------------------------------------- #

def create_window(filename):
layout = generate_layout(filename)
window = sg.Window('Morse Analyzer', layout, grab_anywhere=True, finalize=True, location=(0, 0), resizable=False,
font=APPFONT)
return window


def show_about():
sg.popup_ok(ABOUT_TEXT, title='About', icon='info', keep_on_top=True, grab_anywhere=True, font=APPFONT)


def get_filename():
# Get the filename from the user
filename = sg.popup_get_file('Choose an audio file', keep_on_top=True, grab_anywhere=True, location=(0, 0),
font=APPFONT, file_types=(("Audio Files", "*.wav"),))
if filename is None:
return
while not os.path.exists(filename):
sg.popup_error('Please select a file!', keep_on_top=True)
filename = sg.popup_get_file('Choose an audio file', keep_on_top=True, grab_anywhere=True, location=(0, 0),
font=APPFONT)
if filename is None:
return
return filename


def generate_layout(filename):
plotting_frame = sg.Frame('Plotting', layout=[[
sg.Column(layout=[
[
sg.T('Time window limits:', s=25),
sg.Input(k='plot_time_min', s=10, default_text=DEFAULTS['plot_time_min']),
sg.T('-'),
sg.Input(k='plot_time_max', s=10, default_text=DEFAULTS['plot_time_max'])
],
[
sg.T('Frequency window limits:', s=25),
sg.Input(k='plot_freq_min', s=10, default_text=DEFAULTS['plot_freq_min']),
sg.T('-'),
sg.Input(k='plot_freq_max', s=10, default_text=DEFAULTS['plot_freq_max'])
],
[sg.CB('Show Grids', default=DEFAULTS['plot_grids'], k='plot_grids')]
]),
sg.Column(layout=[
[sg.T('', k='-COORDS-', s=20, justification='center')],
[sg.T('', k='-VALUE-', s=20, justification='center')],
[sg.Button('Plot', k='-PLOT-', expand_x=True)]
])
]])
output_layout = [
[sg.Canvas(k='-CANVAS-')],
[sg.Column([[plotting_frame]], expand_x=True, element_justification='center', p=((5, 5), (20, 10)))]
]
shortened = filename if len(filename) < 40 else '...' + filename[-37:]
input_layout = [
# ------------------------------------------ Filename And SR Display ----------------------------------------- #
[sg.Frame(title='File', expand_x=True, pad=((5, 5), (0, 20)), layout=[
[sg.T(shortened, s=40, justification='center', tooltip=filename)],
[
sg.T('Sample rate:'),
sg.T('', k='-SR-', expand_x=True, justification='end')
]
])],

# ------------------------------------------- Re-sampling Settings ------------------------------------------- #
[sg.Frame(title='Sampling', pad=((5, 5), (0, 20)), element_justification='center', expand_x=True, layout=[
[
sg.T('FFT bins:', expand_x=True),
sg.Input(k='n_fft', s=15, justification="end", default_text=DEFAULTS['n_fft'])
],
[
sg.T('FFT bin size:'),
sg.T('', k='-BINSIZE-', expand_x=True, justification='end')
],
[
sg.T('Window length:', expand_x=True),
sg.Input(k='win_length', s=15, justification="end", default_text=DEFAULTS['win_length'])
],
[
sg.T('Hop length:', expand_x=True),
sg.Input(k='hop_length', s=15, justification="end", default_text=DEFAULTS['hop_length'])
],
[sg.Button('Render', k='-RENDER-', s=10)]
])],

# ----------------------------------------- Audio Filtering Settings ----------------------------------------- #
[sg.Frame(title='Filtering', pad=((5, 5), (0, 20)), element_justification='center', expand_x=True, layout=[
[
sg.CB('Threshold (dB):', expand_x=True, default=DEFAULTS['apply_threshold_db'], k='apply_threshold_db'),
sg.Input(k='threshold_db', s=15, justification="end", default_text=DEFAULTS['threshold_db'])
],
[
sg.CB('Frequency band (Hz):', expand_x=True, default=DEFAULTS['apply_freq_band'], k='apply_freq_band'),
sg.Input(k='freq_band_min', s=10, justification="end", default_text=DEFAULTS['freq_band_min']),
sg.T('-'),
sg.Input(k='freq_band_max', s=10, justification="end", default_text=DEFAULTS['freq_band_max'])
],
[
sg.CB('Time band (s):', expand_x=True, default=DEFAULTS['apply_time_band'], k='apply_time_band'),
sg.Input(k='time_band_min', s=10, justification="end", default_text=DEFAULTS['time_band_min']),
sg.T('-'),
sg.Input(k='time_band_max', s=10, justification="end", default_text=DEFAULTS['time_band_max'])
],
[sg.Button('Apply', k='-APPLY-', s=10)]
])],

# ------------------------------------------- Morse Decode Display ------------------------------------------- #
[sg.Frame(title='Decoding', pad=((5, 5), (0, 10)), expand_x=True, element_justification='center', layout=[
[sg.Button('Solve', k='-SOLVE-', s=10)],
[sg.Multiline(s=(40, 5), k='-ENCODED-', font=MONOSPACEFONT)],
[sg.Button('Decode', k='-DECODE-', s=10)],
[sg.Multiline(s=(40, 4), k='-DECODED-', font=MONOSPACEFONT)]
])]
]

# ================================================================================================================ #
# LAYOUT #
# ================================================================================================================ #
layout = [
[sg.Menu([['&Help', ['&About::-ABOUT-']]], tearoff=False)],
[
sg.Column(output_layout, vertical_alignment='top', expand_y=True),
sg.Column(input_layout, vertical_alignment='top', expand_y=True)
],
[sg.StatusBar('Ready', k='-STATUS-', p=0, size=180, font=SMALLFONT)]
]
return layout
Binary file added icon.ico
Binary file not shown.
95 changes: 95 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import traceback

import matplotlib.pyplot as plt
import PySimpleGUI as sg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import morse
from config import DEFAULTS
from gui import create_window, get_filename, show_about
from plotting import plot
from util import bin_to_freq, frames_to_time, get_bin_size


# ------------------------------------------------- Helper Functions ------------------------------------------------- #

def draw_figure(canvas, figure):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg


def setup_interactions(figure, dft, sr, values, window):
def notify_mouse_move(event):
if event.xdata is not None and event.ydata is not None and int(event.ydata) < dft.shape[0]:
coords = f"{frames_to_time(event.xdata, sr, values):.4f} s {bin_to_freq(int(event.ydata), sr, values):.2f} Hz"
window['-COORDS-'].update(coords)
window['-VALUE-'].update(f"{dft[int(event.ydata)][int(event.xdata)]:.2f} dB")
figure.canvas.mpl_connect('motion_notify_event', notify_mouse_move)


# ---------------------------------------------------- Event Loop ---------------------------------------------------- #

def event_loop(window):
try:
# Load the audio file
y, sr = morse.load_file(filename)
window['-SR-'].update(f"{sr} Hz")
dft = morse.get_dft(y, sr, DEFAULTS)
bin_size = get_bin_size(sr, DEFAULTS)
window['-BINSIZE-'].update(f"{bin_size:.2f} Hz")
# Plot the spectrogram
fig, ax = plot(dft, sr, DEFAULTS)
fig_canvas_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig)
setup_interactions(fig, dft, sr, DEFAULTS, window)
# Wait for the user interaction
while True:
event, values = window.read()

if event == sg.WIN_CLOSED:
break
elif event == "-RENDER-":
window['-STATUS-'].update("Rendering...")
dft = morse.get_dft(y, sr, values)
bin_size = get_bin_size(sr, values)
window['-BINSIZE-'].update(f"{bin_size:.2f} Hz")
window['-STATUS-'].update('Rendered')
elif event == "-APPLY-":
window['-STATUS-'].update("Applying...")
dft = morse.tweak_dft(dft, sr, values)
window['-STATUS-'].update(value='Filters applied')
elif event == "-DECODE-":
window['-DECODED-'].update(morse.decode(values['-ENCODED-']))
elif event == "-SOLVE-":
window['-STATUS-'].update("Solving...")
code, status = morse.solve(dft, sr, values)
window['-STATUS-'].update(status)
window['-ENCODED-'].update(code)
window.refresh()
elif event == 'About::-ABOUT-':
show_about()

if event in ("-PLOT-", "-RENDER-", "-APPLY-"):
plt.close('all')
fig_canvas_agg.get_tk_widget().forget()
fig, ax = plot(dft, sr, values)
fig_canvas_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig)
setup_interactions(fig, dft, sr, values, window)
except Exception as e:
tb = traceback.format_exc()
print(tb)
sg.Print('An error occurred. Details:', e, tb, blocking=True)
finally:
window.close()


# ------------------------------------------------------ Driver ------------------------------------------------------ #

if __name__ == '__main__':
filename = get_filename()
if filename != None:
# Create the window
window = create_window(filename)
# Run the main loop
event_loop(window)
Loading

0 comments on commit 0dc87d5

Please sign in to comment.