diff --git a/TinnitusAnalyse/SoundGenerator.py b/TinnitusAnalyse/SoundGenerator.py index 0a50aa8..e65ce21 100644 --- a/TinnitusAnalyse/SoundGenerator.py +++ b/TinnitusAnalyse/SoundGenerator.py @@ -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)) diff --git a/TinnitusAnalyse/TinnitusAnalyse_GUI.py b/TinnitusAnalyse/TinnitusAnalyse_GUI.py index 5286d1a..d3a5fc1 100644 --- a/TinnitusAnalyse/TinnitusAnalyse_GUI.py +++ b/TinnitusAnalyse/TinnitusAnalyse_GUI.py @@ -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() \ No newline at end of file