|
|
|
|
|
|
|
|
import math |
|
|
import math |
|
|
import wave #bearbeiten von .wav-Dateien |
|
|
|
|
|
|
|
|
import wave # bearbeiten von .wav-Dateien |
|
|
import struct |
|
|
import struct |
|
|
import sounddevice as sd #zum abspielen des audio-arrays |
|
|
|
|
|
import random as random #für die Rauscherzeugung |
|
|
|
|
|
import time #nur für die Tests, um Zeit zum Anhören der Sounds einzubauen |
|
|
|
|
|
|
|
|
import sounddevice as sd # zum abspielen des audio-arrays |
|
|
|
|
|
import numpy as np |
|
|
|
|
|
import sys # für Fehlermeldungen |
|
|
|
|
|
|
|
|
#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 |
|
|
|
|
|
|
|
|
"""--------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
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=600, r_freq=600, l_amp=0, r_amp=0, l_rausch=0, r_rausch=0, ear=0): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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): |
|
|
|
|
|
self.vorname = "" |
|
|
|
|
|
self.nachname = "" |
|
|
|
|
|
self.kommentar = "" |
|
|
self.linksFrequenz = l_freq |
|
|
self.linksFrequenz = l_freq |
|
|
self.rechtsFrequenz = r_freq |
|
|
self.rechtsFrequenz = r_freq |
|
|
self.linksLautstaerke = l_amp |
|
|
self.linksLautstaerke = l_amp |
|
|
self.rechtsLautstaerke = r_amp |
|
|
self.rechtsLautstaerke = r_amp |
|
|
self.linksRauschenLautstaerke = l_rausch #float von 0-1 (0 ist aus) |
|
|
|
|
|
|
|
|
self.linksRauschenLautstaerke = l_rausch # float von 0-1 (0 ist aus) |
|
|
self.rechtsRauschenLautstaerke = r_rausch |
|
|
self.rechtsRauschenLautstaerke = r_rausch |
|
|
self.ear = ear # 0:both 1:left 2:right 3:links/rechts unterschiedlich |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
def speichern(self): #speichert die Nutzerdaten in eine .csv-Datei |
|
|
|
|
|
|
|
|
self.ohr = ohr # 0:both 1:left 2:right 3:links/rechts unterschiedlich |
|
|
|
|
|
|
|
|
|
|
|
def speichern(self): # speichert die Nutzerdaten in eine .csv-Datei |
|
|
datei = open("TinnitusDaten.csv", "w") |
|
|
datei = open("TinnitusDaten.csv", "w") |
|
|
|
|
|
|
|
|
daten = "linke Frequenz;" + str(self.linksFrequenz) + "\n" |
|
|
|
|
|
|
|
|
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 += "linke Lautstärke;" + str(self.linksLautstaerke) + "\n" |
|
|
daten += "linkes Rauschen;" + str(self.linksRauschenLautstaerke) + "\n" |
|
|
daten += "linkes Rauschen;" + str(self.linksRauschenLautstaerke) + "\n" |
|
|
daten += "rechte Frequenz;" + str(self.rechtsFrequenz) + "\n" |
|
|
daten += "rechte Frequenz;" + str(self.rechtsFrequenz) + "\n" |
|
|
|
|
|
|
|
|
datei.write(daten) |
|
|
datei.write(daten) |
|
|
datei.close() |
|
|
datei.close() |
|
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
#Die Klasse beinhaltet alle Werte, die zum Erstellen einer .wav-Datei benötigt werden und ein Tinnitus-Objekt, das beim Initialisieren übergeben werden muss. |
|
|
|
|
|
#Die Audiodaten werden in das Array "audio" geschrieben |
|
|
|
|
|
|
|
|
"""---------------------------------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 eine Tinnitus-Objekt übergeben werden |
|
|
|
|
|
---------------------------------------------------------------------------------------------------------------------""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Sound: |
|
|
class Sound: |
|
|
def __init__(self, tinnitus, name="sound.wav", audio=None, nchannels=1, sampwidth=2, framerate=44100, comptype="NONE", compname="not compressed", mute=True): |
|
|
|
|
|
|
|
|
def __init__(self, tinnitus, name="sound.wav", audio=None, nchannels=1, sampwidth=2, framerate=44100, |
|
|
|
|
|
comptype="NONE", compname="not compressed", mute=True): |
|
|
if audio is None: |
|
|
if audio is None: |
|
|
audio = [] |
|
|
audio = [] |
|
|
self.tinnitus = tinnitus |
|
|
self.tinnitus = tinnitus |
|
|
self.name = name |
|
|
self.name = name |
|
|
self.audio = audio #ein Array, in das die Sound-Werte geschrieben werden |
|
|
|
|
|
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.audio = audio # ein Array, in das die Sound-Werte geschrieben werden |
|
|
|
|
|
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.comptype = comptype |
|
|
self.compname = compname |
|
|
self.compname = compname |
|
|
self.mute = True # wenn der mute boolean auf true gesetzt ist, sollte kein Ton ausgegeben werden |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
wav_file = wave.open(self.name, "w") |
|
|
|
|
|
print("Sound wird als .wav-Datei gespeichert. Bitte warten...\nDer Vorgang kann ca. 30 Sekunden dauern") |
|
|
|
|
|
|
|
|
def neu_audio(self): #Schreibt Werte in das audio-Array. Soll später abhängig sein von den Tinnitus-Werten(Frequenz, Rauschen, ...) |
|
|
|
|
|
|
|
|
# zuerst muss ein Array mit Audiodaten gefüllt werden |
|
|
|
|
|
audio = [] |
|
|
freq = self.tinnitus.linksFrequenz |
|
|
freq = self.tinnitus.linksFrequenz |
|
|
dauer_ms = 100.0 |
|
|
|
|
|
|
|
|
dauer_ms = 10000.0 # 10 Sekunden |
|
|
amp = self.tinnitus.linksLautstaerke |
|
|
amp = self.tinnitus.linksLautstaerke |
|
|
rauschen = self.tinnitus.linksRauschenLautstaerke |
|
|
rauschen = self.tinnitus.linksRauschenLautstaerke |
|
|
|
|
|
|
|
|
self.audio.clear() |
|
|
|
|
|
num_samples = dauer_ms * (self.framerate / 1000.0) # framerate -pro Sekunde- umgerechnet in -pro Millisekunde- |
|
|
num_samples = dauer_ms * (self.framerate / 1000.0) # framerate -pro Sekunde- umgerechnet in -pro Millisekunde- |
|
|
|
|
|
|
|
|
# einen einfachen Sinus ins array schreiben |
|
|
|
|
|
for x in range(int(num_samples)): |
|
|
|
|
|
self.audio.append(amp * math.sin(2 * math.pi * freq * (x / self.framerate))) |
|
|
|
|
|
|
|
|
for x in range(int(num_samples)): # einen einfachen Sinus ins array schreiben |
|
|
|
|
|
audio.append(amp * math.sin(2 * math.pi * freq * (x / self.framerate))) |
|
|
|
|
|
|
|
|
#das Rauschen addieren |
|
|
|
|
|
if(self.tinnitus.linksRauschenLautstaerke != 0): |
|
|
|
|
|
|
|
|
if rauschen: # das Rauschen addieren |
|
|
for x in range(int(num_samples)): |
|
|
for x in range(int(num_samples)): |
|
|
self.audio[x] += random.random() * rauschen |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
def wav_speichern(self): #ezeugt/aktuallisiert die .wav-Datei |
|
|
|
|
|
self.neu_audio() |
|
|
|
|
|
#das 100ms audio-array strecken: |
|
|
|
|
|
for x in range(50): # entspricht 5 Sekunden |
|
|
|
|
|
for y in range(int(self.framerate/10)): |
|
|
|
|
|
self.audio.append(self.audio[y]) |
|
|
|
|
|
|
|
|
audio[x] += (np.random.rand() - 0.5) * rauschen |
|
|
|
|
|
|
|
|
wav_file = wave.open(self.name, "w") |
|
|
|
|
|
#erst werden die Rahmen-Parameter gesetzt |
|
|
|
|
|
self.nframes = len(self.audio) # Anpassen des nframes-Parameters |
|
|
|
|
|
|
|
|
# erst werden die Rahmen-Parameter gesetzt |
|
|
|
|
|
self.nframes = len(audio) # Anpassen des nframes-Parameters |
|
|
wav_file.setparams((self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname)) |
|
|
wav_file.setparams((self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname)) |
|
|
|
|
|
|
|
|
#dann wird das audio-Array an die Datei übertragen |
|
|
|
|
|
for sample in self.audio: |
|
|
|
|
|
|
|
|
# dann wird das audio-Array an die Datei übertragen |
|
|
|
|
|
for sample in audio: |
|
|
wav_file.writeframes(struct.pack('h', int(sample * 32767.0))) |
|
|
wav_file.writeframes(struct.pack('h', int(sample * 32767.0))) |
|
|
|
|
|
|
|
|
wav_file.close() |
|
|
wav_file.close() |
|
|
return |
|
|
|
|
|
|
|
|
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): |
|
|
def play(self): |
|
|
self.neu_audio() |
|
|
|
|
|
if not self.mute: # NEVER play sound when patient mutes GUI |
|
|
|
|
|
sd.play(self.audio, self.framerate, loop=1) |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
if not self.mute: # NEVER play sound when patient mutes GUI |
|
|
|
|
|
self.sound_obj.start() |
|
|
|
|
|
|
|
|
def stop(self): |
|
|
def stop(self): |
|
|
sd.stop() |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
# tinnitus1 = Tinnitus() |
|
|
|
|
|
# sound = Sound(tinnitus1) |
|
|
|
|
|
# |
|
|
|
|
|
# if(1): |
|
|
|
|
|
# print("======abnehmende Lautstärke==========") |
|
|
|
|
|
# for x in range(10): |
|
|
|
|
|
# print(" Lautstärke = ", tinnitus1.linksLautstaerke) |
|
|
|
|
|
# sound.play() |
|
|
|
|
|
# time.sleep(0.6) |
|
|
|
|
|
# tinnitus1.linksLautstaerke -= 0.1 |
|
|
|
|
|
# print("\n\n") |
|
|
|
|
|
# time.sleep(1) |
|
|
|
|
|
# tinnitus1.linksLautstaerke = 1 |
|
|
|
|
|
# |
|
|
|
|
|
# |
|
|
|
|
|
# |
|
|
|
|
|
# if(0): |
|
|
|
|
|
# print("=========RauschTest============") |
|
|
|
|
|
# for x in range(10): |
|
|
|
|
|
# tinnitus1.linksRauschenLautstaerke += 0.01 |
|
|
|
|
|
# print(" Rauschen = ", tinnitus1.linksRauschenLautstaerke) |
|
|
|
|
|
# sound.play() |
|
|
|
|
|
# time.sleep(1) |
|
|
|
|
|
# |
|
|
|
|
|
# if(0): |
|
|
|
|
|
# tinnitus1.speichern() |
|
|
|
|
|
# |
|
|
|
|
|
# sd.stop() |
|
|
|
|
|
# print("ENDE") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 """ |
|
|
|
|
|
|
|
|
|
|
|
# momentan nur Mono. Es werden die Werte des linken Ohrs genutzt |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
# Sinus ins Array schreiben: f(t) = A * sin(2 * pi * f * t) |
|
|
|
|
|
for x in range(frames): |
|
|
|
|
|
# links: |
|
|
|
|
|
if self.tinnitus.linksLautstaerke: |
|
|
|
|
|
outdata[x][0] = self.tinnitus.linksLautstaerke * np.sin(2 * np.pi * self.tinnitus.linksFrequenz * |
|
|
|
|
|
((x + self.start_idx) / self.framerate)) |
|
|
|
|
|
# rechts: |
|
|
|
|
|
if self.tinnitus.rechtsLautstaerke: |
|
|
|
|
|
outdata[x][1] = self.tinnitus.rechtsLautstaerke * np.sin(2 * np.pi * self.tinnitus.rechtsFrequenz * |
|
|
|
|
|
((x + self.start_idx) / self.framerate)) |
|
|
|
|
|
|
|
|
|
|
|
# Rauschen addieren |
|
|
|
|
|
for x in range(frames): |
|
|
|
|
|
rand = (np.random.rand() - 0.5) # Zufallszahl zwischen -0.5 und 0.5 |
|
|
|
|
|
#links: |
|
|
|
|
|
if self.tinnitus.linksRauschenLautstaerke: |
|
|
|
|
|
outdata[x][0] += rand * self.tinnitus.linksRauschenLautstaerke |
|
|
|
|
|
#rechts: |
|
|
|
|
|
if self.tinnitus.rechtsRauschenLautstaerke: |
|
|
|
|
|
outdata[x][1] += rand * self.tinnitus.rechtsRauschenLautstaerke |
|
|
|
|
|
|
|
|
|
|
|
self.start_idx += frames |