ommerthe69431 4e63ebc564 Code aufgeraeumt
Kommentare ueberarbeitet
unnoetigen Code geloescht/zusammengefasst
2020-06-26 13:24:25 +02:00

165 lines
8.9 KiB
Python

import wave # bearbeiten von .wav-Dateien
import struct
import sounddevice as sd
import numpy as np
import sys # für Fehlermeldungen
from scipy import signal
"""---------------------------------------------------------------------------------------------------------------------
In .wav-Dateien wird der Ton in absoluten Werte eingetragen. Die Standart-framerate ist 44100
das heißt für jede Sekunde an Ton gibt es 44100 Werte, die die Tonwelle über die Zeit beschreiben
---------------------------------------------------------------------------------------------------------------------"""
class Tinnitus: # beinhaltet alle Werte, die vom Nutzer eingestellt werden
def __init__(self, l_freq=0, r_freq=0, l_amp=0, r_amp=0, l_rausch=0, r_rausch=0, ohr=0, l_rausch_ug=10, r_rausch_ug=10, l_rausch_og=20000, r_rausch_og=20000):
self.vorname = ""
self.nachname = ""
self.kommentar = ""
# [Alle Frequenzangaben in Hz, alle Amplitudenangaben von 0-1 (float)]
self.linksFrequenz = l_freq
self.rechtsFrequenz = r_freq
self.linksLautstaerke = l_amp
self.rechtsLautstaerke = r_amp
self.linksRauschenLautstaerke = l_rausch
self.rechtsRauschenLautstaerke = r_rausch
self.linksRauschenUntereGrenzfrequenz = l_rausch_ug
self.rechtsRauschenUntereGrenzfrequenz = r_rausch_ug
self.linksRauschenObereGrenzfrequenz = l_rausch_og
self.rechtsRauschenObereGrenzfrequenz = r_rausch_og
def speichern(self): # speichert die Nutzerdaten in eine .csv-Datei
datei = open("TinnitusDaten.csv", "w")
daten = "Vorname;" + self.vorname + "\n"
daten += "Nachname;" + self.nachname + "\n"
daten += "linke Frequenz;" + str(self.linksFrequenz) + "\n"
daten += "linke Lautstärke;" + str(self.linksLautstaerke) + "\n"
daten += "linkes Rauschen Lautstärke;" + str(self.linksRauschenLautstaerke) + "\n"
daten += "linkes Rauschen untere Grenzfrequenz;" + str(self.linksRauschenUntereGrenzfrequenz) + "\n"
daten += "linkes Rauschen obere Grenzfrequenz;" + str(self.linksRauschenObereGrenzfrequenz) + "\n"
daten += "rechte Frequenz;" + str(self.rechtsFrequenz) + "\n"
daten += "rechte Lautstärke;" + str(self.rechtsLautstaerke) + "\n"
daten += "rechtes Rauschen Lautstärke;" + str(self.rechtsRauschenLautstaerke) + "\n"
daten += "rechtes Rauschen untere Grenzfrequenz;" + str(self.rechtsRauschenUntereGrenzfrequenz) + "\n"
daten += "rechtes Rauschen obere Grenzfrequenz;" + str(self.rechtsRauschenObereGrenzfrequenz) + "\n"
daten += "Kommentar;" + str(self.kommentar) + "\n"
datei.write(daten)
datei.close()
"""---------------------------------KLASSE: SOUND-----------------------------------------------------------------------
Sound beinhaltet alle Variablen, die zum erstellen einer .wav-Datei benötigt werden (siehe soun.wav_speichern())
Das 'sound_obj' ist für das dynamische abspielen zuständig (siehe sound.play())
Beim Initialisieren muss ein Tinnitus-Objekt übergeben werden
---------------------------------------------------------------------------------------------------------------------"""
class Sound:
def __init__(self, tinnitus, wav_name="MeinTinnitus.wav", audio=None, nchannels=2, sampwidth=2, framerate=44100,
comptype="NONE", compname="not compressed", mute=True):
if audio is None:
audio = []
self.tinnitus = tinnitus
self.wav_name = wav_name #Der Dateiname
self.audio = audio # ein Array, in das die Sound-Werte geschrieben werden (von -1, bis +1)
self.nchannels = nchannels # Zahl der audio channels (1:mono 2:stereo)
self.sampwidth = sampwidth # Größe eines einzelnen Sound-Werts (in bytes)
self.framerate = framerate # Abtastrate
self.nframes = len(audio) # Anzahl der Sound-Werte -> Muss bei jeder audio-Änderung aktuallisiert werden
self.comptype = comptype
self.compname = compname
self.mute = mute # wenn der mute boolean auf true gesetzt ist, sollte kein Ton ausgegeben werden
self.sound_obj = sd.OutputStream(channels=2, callback=self.callback,
samplerate=self.framerate) # Objekt fürs Abspielen (siehe sound.play())
self.start_idx = 0 # wird für sound_obj benötigt
def wav_speichern(self): # ezeugt/aktuallisiert die .wav-Datei
print("Sound wird als .wav-Datei gespeichert. Bitte warten...\nDer Vorgang kann ca. 10 Sekunden dauern")
wav_obj = wave.open(self.wav_name, "w")
# Die Callback-Funktion aufrufen, um die Audiodaten zu bekommen
frames = self.framerate * 3 # entspricht 3 Sekunden
status = "" # für den Funktionsaufruf benötigt, sonst keine Funktion
audio = np.ones((frames, 2))
audio = self.callback(audio, frames, self.sound_obj.time, status)
# Rahmenparameter für die .wav-Datei setzen
self.nframes = len(audio)
wav_obj.setparams((self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname))
#Die Audiosamples schreiben
for x in range(frames):
#Die Audiodaten müssen von float in einen passenden int-Wert umgerechnet werden
wav_obj.writeframes(struct.pack('h', int(audio[x][0] * 32767.0))) # Werte für links und rechts werden bei
wav_obj.writeframes(struct.pack('h', int(audio[x][1] * 32767.0))) # wav abwechselnd eingetragen
wav_obj.close()
print("Speichern beendet.")
"""Die Objekt-Funktion 'start()' startet die asynchrone Soundwiedergabe. Sie ruft dabei immer wieder die Funktion
sound.callback() auf. Daher können die dort genutzten Variablen dynamisch geändert werden. """
def play(self):
if not self.mute: # Nie abspielen, wenn die GUI auf stumm geschaltet ist
self.sound_obj.start() # öffnet thread der immer wieder callback funktion aufruft und diese daten abspielt
def stop(self):
self.sound_obj.stop() # beendet die asynchrone Soundwiedergabe
"""Die Funktion callback() erzeugt bei jedem Aufruf die Audiodaten, abhängig von den aktuellen Tinnitus-Variablen.
Die errechneten Werte werden in das NumPy-Array 'outdata' geschrieben """
def callback(self, outdata, frames, time, status):
if status: # Warnungen, wenn das Soundobj. auf Fehler stößt (hauptsächlich over/underflow wg. Timingproblemen)
print(status, file=sys.stderr)
# Whitenoise erzeugen
for x in range(len(outdata)):
rand = (np.random.rand() - 0.5) # Zufallszahl zwischen -0.5 und 0.5
# links:
outdata[x][0] = rand * self.tinnitus.linksRauschenLautstaerke
# rechts:
outdata[x][1] = rand * self.tinnitus.rechtsRauschenLautstaerke
# Whitenoise durch Bandpass laufen lassen
if self.tinnitus.linksRauschenLautstaerke:
# (-3dB Grenzen) bzw was der Bandpass durchlässt
fGrenz = [self.tinnitus.linksRauschenUntereGrenzfrequenz,
self.tinnitus.linksRauschenObereGrenzfrequenz]
# sos (=second order sections = Filter 2. Ordnung) ist ein Array der Länge (filterOrder) und beinhaltet
# die Koeffizienten der IIR Filter 2. Ordnung (b0, b1, b2 & a0, a1, a2)
sos = signal.butter(5, fGrenz, 'bandpass', fs=self.framerate, output='sos')
# sosfilt filtert das Signal mittels mehrerer 'second order sections' (= Filter 2. Ordnung) die über sos definiert sind
outdata[:, 0] = signal.sosfilt(sos, outdata[:, 0])
if self.tinnitus.rechtsRauschenLautstaerke:
# (-3dB Grenzen) bzw was der Bandpass durchlässt
fGrenz = [self.tinnitus.rechtsRauschenUntereGrenzfrequenz,
self.tinnitus.rechtsRauschenObereGrenzfrequenz]
# sos (=second order sections = Filter 2. Ordnung) ist ein Array der Länge (filterOrder) und beinhaltet
# die Koeffizienten der IIR Filter 2. Ordnung (b0, b1, b2 & a0, a1, a2)
sos = signal.butter(5, fGrenz, 'bandpass', fs=self.framerate, output='sos')
# sosfilt filtert das Signal mittels mehrerer 'second order sections' (= Filter 2. Ordnung) die über sos definiert sind
outdata[:, 1] = signal.sosfilt(sos, outdata[:, 1])
# Sinus addieren: f(t) = A * sin(2 * pi * f * t)
for x in range(len(outdata)):
# links:
outdata[x][0] += self.tinnitus.linksLautstaerke * np.sin(2 * np.pi * self.tinnitus.linksFrequenz *
((x + self.start_idx) / self.framerate))
# rechts:
outdata[x][1] += self.tinnitus.rechtsLautstaerke * np.sin(2 * np.pi * self.tinnitus.rechtsFrequenz *
((x + self.start_idx) / self.framerate))
self.start_idx += frames
return outdata