-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b99ddaa
commit 0dc87d5
Showing
8 changed files
with
604 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
".-.-.-": ".", | ||
"--..--": ",", | ||
"..--..": "?", | ||
".----.": "'", | ||
"-.-.--": "!", | ||
"-..-.": "/", | ||
"-.--.": "(", | ||
"-.--.-": ")", | ||
".-...": "&", | ||
"---...": ":", | ||
"-.-.-.": ";", | ||
"-...-": "=", | ||
".-.-.": "+", | ||
"-....-": "-", | ||
"..--.-": "_", | ||
".-..-.": "\"", | ||
"..-...": "^", | ||
"...-..-": "$", | ||
".--.-.": "@" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.