Browse Source

kleine Aenderungen:

 -Die samples werden nach dem Filtern jetzt auf einen maximalen wert von 1 normiert
 -Layout der GUI verbessert
 -Feedback verbessert (v. a. Filterstatus)

ACHTUNG: Der Filter liefert bei niedrigen Filterfrequenzen unendlich hohe Werte, was das Filtern unmoeglich macht
Fehlt: Filter fuers Rauschen
master
Heiko Ommert 3 years ago
parent
commit
23784cc6f7
2 changed files with 92 additions and 37 deletions
  1. 47
    8
      TinnitusAnalyse/SoundGenerator.py
  2. 45
    29
      TinnitusAnalyse/TinnitusAnalyse_GUI.py

+ 47
- 8
TinnitusAnalyse/SoundGenerator.py View File

@@ -81,7 +81,7 @@ class Sound:
self.start_idx = 0 # wird für sound_obj benötigt
self.music_samplerate = 0 # die samplerate der ausgewählten Musikdatei
self.music_data = 0 # das Numpy Array der ausgewählten Musikdatei
self.filterfortschritt = 0 # siehe musik_filtern(), zwischen 0 und 100, wird für die feedback-fkt gebraucht
self.filterfortschritt = 0, 0. # für feedback-fkt, 1.Position: Abschnitt-Nmr, 2.Positon: Speicherfortschritt


def wav_speichern(self): # ezeugt/aktuallisiert die .wav-Datei
@@ -105,7 +105,7 @@ class Sound:
for x in range(self.nframes): # geht jeden Sample-Wert der Musikdatei einzeln durch
# Die Audiodaten müssen von float in einen passenden int-Wert umgerechnet werden
packedMusic.append(struct.pack('h', int(audio[x][0] * 32767.0)))
packedMusic.append(struct.pack('h', int(audio[x][0] * 32767.0)))
packedMusic.append(struct.pack('h', int(audio[x][1] * 32767.0)))

value_str = b"".join(packedMusic)
wav_obj.writeframes(value_str)
@@ -183,8 +183,10 @@ class Sound:
2. Die digitalen Filter erstellen und die Tinnitus Frequenz aus der Audiodatei "herausschneiden"
3. Die fertigen Audiodatei als .wav Datei speichern
"""
nframes = len(self.music_data) # Gesamtanzahl der Frames in der Musikdatei

# ------------1. Die nötigen Informationen über den Tinnitus aus der .csv Datei herausholen---------------------
self.filterfortschritt = 1, 0 # der erste schritt
csvstring = open("TinnitusDaten.csv").read()
tinnitus_data = csvstring.split("\n")

@@ -211,9 +213,10 @@ class Sound:
rl = float(rl[1])

# -------- 2. Die digitalen Filter erstellen und die Tinnitus Frequenz aus der Audiodatei "herausschneiden------
self.filterfortschritt = 2, 0 # der zweite schritt
start_time = time.time() # einen Timer laufen lassen um zu sehen wie lange Filterung dauert

self.music_data = self.music_data/32767 * 0.8 # convert array from int16 to float
self.music_data = self.music_data/32767 # convert array from int16 to float
""" OLD IIR Notch Filter 2nd Order----------------------------------------------------------------------
w0 = float(lf / (self.music_samplerate / 2)) # Frequency to remove from a signal. If fs is specified, this is
in the same units as fs. By default, it is a normalized scalar that must satisfy 0 < w0 < 1, with w0 = 1
@@ -258,11 +261,46 @@ class Sound:
endTimeFiltering = time.time()
print("benötigte Zeit zum Filtern rechts Ohr =", endTimeFiltering - start_time, "s")

# ------------------------- 3. Die fertigen Audiodatei als .wav Datei speichern --------------------------------
#----------------------- 3. Maxima finden und Samples auf 1 normieren ----------------------------
self.filterfortschritt = 3, 0 # der dritte schritt

#Maximum finden (Funktion max(...) ist minimal schneller, macht aber Probleme beim Feedback)
start_time = time.time()
max_ges = 0
for i in range(nframes):
if max_ges < abs(music_links[i]):
max_ges = abs(music_links[i])
if max_ges < abs(music_rechts[i]):
max_ges = abs(music_rechts[i])
if i % 50000 == 0:
fortschritt = i / nframes * 100
self.filterfortschritt = 3, round(fortschritt, 1)
print(" max: ", self.filterfortschritt[1], "%")
end_time = time.time()
print("Zeitaufwand Maxima-Suche: ", end_time - start_time)

#auf 4 Nachkommastellen aufrunden
start_time = time.time()
max_ges = int(max_ges * 10000)
max_ges += 1
max_ges /= 10000
end_time = time.time()
print("Zeitaufwand Maximum runden: ", end_time - start_time)

#Alle samples normieren
start_time = time.time()
music_links /= max_ges
music_rechts /= max_ges
end_time = time.time()
print("Zeitaufwand samples normieren: ", end_time - start_time)


# ------------------------- 4. Die fertigen Audiodatei als .wav Datei speichern --------------------------------
self.filterfortschritt = [4, 0] # der vierte Schritt
start_time = time.time()
wav_obj = wave.open("MyTinnitusFreeSong.wav", "w")

# Rahmenparameter für die .wav-Datei setzen
nframes = len(self.music_data) #Gesamtanzahl der Frames in der Musikdatei
wav_obj.setparams((self.nchannels, self.sampwidth, self.music_samplerate, nframes, self.comptype, self.compname))

"""The values are stored in a temporary list, and when the process is finished, they are joined together into
@@ -279,10 +317,10 @@ class Sound:

# wav_obj.writeframes(struct.pack('h', int(music_links[x] * 32767.0))) # Werte für links und rechts werden bei
# wav_obj.writeframes(struct.pack('h', int(musicRechts[x] * 32767.0))) # wav abwechselnd eingetragen
if tinnitus_data % 10000 == 0:
if tinnitus_data % 50000 == 0:
fortschritt = tinnitus_data/nframes*100
self.filterfortschritt = round(fortschritt, 1)
print(" ", self.filterfortschritt, "%")
self.filterfortschritt = 4, round(fortschritt, 1)
print(" samples: ", self.filterfortschritt[1], "%")

end_time = time.time()
print("Zeitaufwand für das packen der einzelnen Samples =", end_time - start_time, "s")
@@ -297,6 +335,7 @@ class Sound:
wav_obj.close()

print("Speichern beendet.")
self.filterfortschritt = 5, 0 #Nach erfolgreichem Filtern Fortschritt zur Bestätigung auf 5 setzen
# # Plot (hilfreich für Filterentwurf)
# freq, h = signal.freqz(b, a, fs=self.music_samplerate)
# fig, ax = plt.subplots(2, 1, figsize=(8, 6))

+ 45
- 29
TinnitusAnalyse/TinnitusAnalyse_GUI.py View File

@@ -126,11 +126,11 @@ def unten_button_speichern_press():
else:
try:
unten_button_stop_press() # Wiedergabe beenden, durch den Rechenaufwand gibt es sonst Wiedergabeprobleme
feedback("Speichere Sound als '.wav'-Datei. Bitte warten...")
tinnitus.vorname = untenEntryVorname.get()
tinnitus.nachname = untenEntryNachname.get()
tinnitus.kommentar = untenTextKommentar.get("1.0", END)
tinnitus.speichern()
feedback("Speichere Sound als '.wav'-Datei. Bitte warten...")
sound.wav_speichern()
feedback("Daten erfolgreich gespeichert. Siehe: " + sound.wav_name, "white", "green")
except:
@@ -156,7 +156,6 @@ def feedback(text, fontcolor="black", backgroundcolor="lightsteelblue"):
right of the GUI) in the text widget. The parameter color is also a string and defines the font color. Same with
background. Honestly this function is way too complicated, but Tkinter has no nicer/easier builtin way of doing the
coloring nicely """

feedback.lineCounter += 1 # in order to color the texts nicely we need to count the lines of text we add
untenFeedbackText.config(state=NORMAL) # activate text field (otherwise it is readonly)

@@ -199,23 +198,38 @@ def unten_button_musikdatei_laden_press():

def unten_button_filtere_tinnitus_aus_musik():
print("button filtere tinnitus aus musik pressed")
try:
print(untenTextMusikDatei.get('1.0', END))
if untenTextMusikDatei.get('1.0', END) == "Einen Song deiner Wahl hier auswählen\n":
feedback("Wähle zuerst eine Musikdatei aus", "white", "red")
else:
unten_button_stop_press() # Wiedergabe beenden, durch den Rechenaufwand gibt es sonst Wiedergabeprobleme
feedback("Starte Filtervorgang (dies kann etwas dauern)...", "blue")
# Filtern in extra thread, damit sich die GUI nicht aufhängt: (daemon beendet den Thread, wenn das Hauptprogramm beendet wird)
filter_thread = threading.Thread(target=sound.musik_filtern, daemon=True)
filter_thread.start()

while filter_thread.is_alive() == True:
fb = "Status: " + str(sound.filterfortschritt) + "%"
feedback(fb)
time.sleep(0.2)
print("-- filtern beendet --")
feedback("Filtervorgang erfolgreich abgeschlossen. \n"
"Audiodatei unter dem Namen MyTinnitusFreeSong.wav erstellt", "white", "green")
except:
feedback("Fehlgeschlagener Filterversuch. Drücke zuerst den Speichern Knopf"
"Stelle sicher, dass die Lautstärke mindestens einer Seite über 0"
"gestellt ist. Sonst gehen wir davon aus, dass auf diesem Ohr kein Tinnitus vorliegt.", "red", "white")

try:
# Filtern in extra thread, damit sich die GUI nicht aufhängt: (daemon beendet den Thread, wenn das Hauptprogramm beendet wird)
filter_thread = threading.Thread(target=sound.musik_filtern, daemon=True)
filter_thread.start()
time.sleep(3) # Zeit, damit man das Feedback lesen kann, bevor es gelöscht wird (siehe übernächste Zeile)

while filter_thread.is_alive():
feedback.lineCounter = 11 # "Workaround" um Zeilen überschreiben zu können
if sound.filterfortschritt[0] > 2: #Nur bei dem 3. und 4. Schritt wird der Fortschritt in Prozent angezeigt
fb = "Schritt " + str(sound.filterfortschritt[0]) + " von 4 (" + str(sound.filterfortschritt[1]) + "%)"
else:
fb = "Schritt " + str(sound.filterfortschritt[0]) + " von 4"
feedback(fb)
if sound.filterfortschritt[0] == 5: #ist 5, wenn erfolgreich gefiltert wurde
print("-- filtern beendet --")
feedback("Filtervorgang erfolgreich abgeschlossen. \n"
"Audiodatei unter dem Namen MyTinnitusFreeSong.wav erstellt", "white", "green")
else:
print("Fehler bei Filterfunktion. Siehe Compiler-Meldungen")
feedback("Fehlgeschlagener Filterversuch!", "red", "white")
except:
feedback("Fehlgeschlagener Filterversuch. Drücke zuerst den Speichern Knopf"
"Stelle sicher, dass die Lautstärke mindestens einer Seite über 0"
"gestellt ist. Sonst gehen wir davon aus, dass auf diesem Ohr kein Tinnitus vorliegt.", "red",
"white")


""" Initialisierungen """
@@ -349,17 +363,17 @@ untenLabelOhrenSynchro2 = Label(untererFrame, text=" für beide übernehmen")
untenLabelOhrenSynchro2.grid(column=2, row=0, sticky=(N + W + E + S))
untenButtonOhrenSynchro = Button(untererFrame, text="Bestätigen",
command=unten_button_ohren_synchro)
untenButtonOhrenSynchro.grid(column=3, row=0, sticky=(N + W + E + S))
untenButtonOhrenSynchro.grid(column=3, row=0, sticky=(N + S))

#----------- PLAY BUTTON
untenButtonPlay = Button(untererFrame, text="PLAY", font="bold", relief="raised", bg="green", fg="white",
command=unten_button_play_press)
untenButtonPlay.grid(column=8, row=0, sticky=(N + W + E + S))
untenButtonPlay.grid(column=7, row=0, sticky=(N + W + E + S))

#------------STOP BUTTON-------------
untenButtonStop = Button(untererFrame, text="STOP", font="bold", relief="raised", bg="red", fg="white",
command=unten_button_stop_press)
untenButtonStop.grid(column=9, row=0, sticky=(N + W + E + S))
untenButtonStop.grid(column=8, row=0, sticky=(N + W + E + S))

# ----------- ABTRENNSTRICHE ----------------
untenSeparator = Separator(untererFrame, orient="horizontal")
@@ -381,13 +395,15 @@ untenEntryVorname.grid(column=1, row=3, sticky=W)
# ------------ KOMMENTAR ----------------
untenLabelKommentar = Label(untererFrame, text="weitere Kommentare:")
untenLabelKommentar.grid(column=0, row=4, sticky=W)
untenTextKommentar = Text(untererFrame, height=10, width=50) # create a field where u can enter text
untenTextKommentar.grid(column=1, row=4, columnspan=3, rowspan=3)
untenLabelKommentar = Label(untererFrame, text="(optional)")
untenLabelKommentar.grid(column=0, row=5, sticky=N)
untenTextKommentar = Text(untererFrame, height=10, width=60) # create a field where u can enter text
untenTextKommentar.grid(column=1, row=4, sticky=W, columnspan=3, rowspan=3)

# ----------- SPEICHERN --------------------
untenButtonSpeichern = Button(untererFrame, text="Speichern", font="bold",
command=unten_button_speichern_press)
untenButtonSpeichern.grid(column=4, row=6, sticky=S)
untenButtonSpeichern.grid(column=4, row=6, sticky=(S+E+W))


"""--------------------------------------UNTERER RECHTER FRAME-------------------------------------------------------"""
@@ -405,20 +421,20 @@ untenFeedbackText.place(relx=.5, rely=.5, anchor="center") # the only time I us

# -------------- LOAD MUSIC FILE--------------
untenTextMusikDatei = Text(untererFrame, height=1, width=50)
untenTextMusikDatei.grid(column=0, row=7, sticky=(N+S+E+W), columnspan=6)
untenTextMusikDatei.grid(column=1, row=7, sticky=(N+S+E+W), columnspan=4)
untenTextMusikDatei.insert(INSERT, "Einen Song deiner Wahl hier auswählen") # insert selected file path to text widget
untenTextMusikDatei.config(state=DISABLED) # activate text field (otherwise it is readonly)
untenTextMusikDatei.config(state=DISABLED, font=("Arial", 8)) # activate text field (otherwise it is readonly)

untenButtonMusikDateiLaden = Button(untererFrame, text="Musikdatei auswählen",
command=unten_button_musikdatei_laden_press)
untenButtonMusikDateiLaden.grid(column=7, row=7, sticky=(N+S+E+W))
untenButtonMusikDateiLaden.grid(column=4, row=7, sticky=(N+S+E+W))


#------------BUTTON FILTERE TINNITUS AUS MUSIK-----------------
untenButtonFiltereTinnitusAusMusik = Button(untererFrame, text="Filtere Tinnitus Frequenzen aus Musik",
untenButtonFiltereTinnitusAusMusik = Button(untererFrame, text="Filtere Tinnitus-Frequenzen aus Musik",
command=unten_button_filtere_tinnitus_aus_musik, font="bold",
relief="raised", bg="blue", fg="white",)
untenButtonFiltereTinnitusAusMusik.grid(column=0, row=9, sticky=(N+S+E+W))
untenButtonFiltereTinnitusAusMusik.grid(column=3, row=9, sticky=(N+S+E+W), columnspan=3)


root.mainloop()

Loading…
Cancel
Save