Datenablage für Tinnitus Therapie Projektarbeit von Julian Seyffer und Heiko Ommert SS2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SoundGenerator.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import math
  2. import wave # bearbeiten von .wav-Dateien
  3. import struct
  4. import sounddevice as sd # zum abspielen des audio-arrays
  5. import numpy as np
  6. import sys # für Fehlermeldungen
  7. """---------------------------------------------------------------------------------------------------------------------
  8. In .wav-Dateien wird der Ton in absoluten Werte eingetragen. Die Standart-framerate ist 44100
  9. das heißt für jede Sekunde an Ton gibt es 44100 Werte, die die Tonwelle über die Zeit beschreiben
  10. ---------------------------------------------------------------------------------------------------------------------"""
  11. class Tinnitus: # beinhaltet alle Werte, die vom Nutzer eingestellt werden
  12. def __init__(self, l_freq=0, r_freq=0, l_amp=0, r_amp=0, l_rausch=0, r_rausch=0, ohr=0):
  13. self.vorname = ""
  14. self.nachname = ""
  15. self.kommentar = ""
  16. self.linksFrequenz = l_freq
  17. self.rechtsFrequenz = r_freq
  18. self.linksLautstaerke = l_amp
  19. self.rechtsLautstaerke = r_amp
  20. self.linksRauschenLautstaerke = l_rausch # float von 0-1 (0 ist aus)
  21. self.rechtsRauschenLautstaerke = r_rausch
  22. self.ohr = ohr # 0:both 1:left 2:right 3:links/rechts unterschiedlich
  23. def speichern(self): # speichert die Nutzerdaten in eine .csv-Datei
  24. datei = open("TinnitusDaten.csv", "w")
  25. daten = "Vorname;" + self.vorname + "\n"
  26. daten += "Nachname;" + self.nachname + "\n"
  27. daten += "linke Frequenz;" + str(self.linksFrequenz) + "\n"
  28. daten += "linke Lautstärke;" + str(self.linksLautstaerke) + "\n"
  29. daten += "linkes Rauschen;" + str(self.linksRauschenLautstaerke) + "\n"
  30. daten += "rechte Frequenz;" + str(self.rechtsFrequenz) + "\n"
  31. daten += "rechte Lautstärke;" + str(self.rechtsLautstaerke) + "\n"
  32. daten += "rechtes Rauschen;" + str(self.rechtsRauschenLautstaerke) + "\n"
  33. datei.write(daten)
  34. datei.close()
  35. """---------------------------------KLASSE: SOUND-----------------------------------------------------------------------
  36. Sound beinhaltet alle Variablen, die zum erstellen einer .wav-Datei benötigt werden (siehe soun.wav_speichern())
  37. Das 'sound_obj' ist für das dynamische abspielen zuständig (siehe sound.play())
  38. Beim Initialisieren muss eine Tinnitus-Objekt übergeben werden
  39. ---------------------------------------------------------------------------------------------------------------------"""
  40. class Sound:
  41. def __init__(self, tinnitus, name="sound.wav", audio=None, nchannels=1, sampwidth=2, framerate=44100,
  42. comptype="NONE", compname="not compressed", mute=True):
  43. if audio is None:
  44. audio = []
  45. self.tinnitus = tinnitus
  46. self.name = name
  47. self.audio = audio # ein Array, in das die Sound-Werte geschrieben werden
  48. self.nchannels = nchannels # Zahl der audio channels (1:mono 2:stereo)
  49. self.sampwidth = sampwidth # Größe eines einzelnen Sound-Werts (in bytes)
  50. self.framerate = framerate # Abtastrate
  51. self.nframes = len(audio) # Anzahl der Sound-Werte -> Muss bei jeder audio-Änderung aktuallisiert werden
  52. self.comptype = comptype
  53. self.compname = compname
  54. self.mute = mute # wenn der mute boolean auf true gesetzt ist, sollte kein Ton ausgegeben werden
  55. self.sound_obj = sd.OutputStream(channels=2, callback=self.callback,
  56. samplerate=self.framerate) # Objekt fürs Abspielen (siehe sound.play())
  57. self.start_idx = 0 # wird für sound_obj benötigt
  58. def wav_speichern(self): # ezeugt/aktuallisiert die .wav-Datei
  59. wav_file = wave.open(self.name, "w")
  60. print("Sound wird als .wav-Datei gespeichert. Bitte warten...\nDer Vorgang kann ca. 30 Sekunden dauern")
  61. # zuerst muss ein Array mit Audiodaten gefüllt werden
  62. audio = []
  63. freq = self.tinnitus.linksFrequenz
  64. dauer_ms = 10000.0 # 10 Sekunden
  65. amp = self.tinnitus.linksLautstaerke
  66. rauschen = self.tinnitus.linksRauschenLautstaerke
  67. num_samples = dauer_ms * (self.framerate / 1000.0) # framerate -pro Sekunde- umgerechnet in -pro Millisekunde-
  68. for x in range(int(num_samples)): # einen einfachen Sinus ins array schreiben
  69. audio.append(amp * math.sin(2 * math.pi * freq * (x / self.framerate)))
  70. if rauschen: # das Rauschen addieren
  71. for x in range(int(num_samples)):
  72. audio[x] += (np.random.rand() - 0.5) * rauschen
  73. # erst werden die Rahmen-Parameter gesetzt
  74. self.nframes = len(audio) # Anpassen des nframes-Parameters
  75. wav_file.setparams((self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname))
  76. # dann wird das audio-Array an die Datei übertragen
  77. for sample in audio:
  78. wav_file.writeframes(struct.pack('h', int(sample * 32767.0)))
  79. wav_file.close()
  80. print("Speichern beendet.")
  81. """Die Objekt-Funktion 'start()' startet die asynchrone Soundwiedergabe. Sie ruft dabei immer wieder die Funktion
  82. sound.callback() auf. Daher können die dort genutzten Variablen dynamisch geändert werden. """
  83. def play(self):
  84. if not self.mute: # NEVER play sound when patient mutes GUI
  85. self.sound_obj.start()
  86. def stop(self):
  87. self.sound_obj.stop() # beendet die asynchrone Soundwiedergabe
  88. """Die Funktion callback() erzeugt bei jedem Aufruf die Audiodaten, abhängig von den aktuellen Tinnitus-Variablen.
  89. Die errechneten Werte werden in das NumPy-Array 'outdata' geschrieben """
  90. # momentan nur Mono. Es werden die Werte des linken Ohrs genutzt
  91. def callback(self, outdata, frames, time, status):
  92. if status: # Warnungen, wenn das Soundobj. auf Fehler stößt (hauptsächlich over/underflow wg. Timingproblemen)
  93. print(status, file=sys.stderr)
  94. # Sinus ins Array schreiben: f(t) = A * sin(2 * pi * f * t)
  95. for x in range(frames):
  96. # links:
  97. if self.tinnitus.linksLautstaerke:
  98. outdata[x][0] = self.tinnitus.linksLautstaerke * np.sin(2 * np.pi * self.tinnitus.linksFrequenz *
  99. ((x + self.start_idx) / self.framerate))
  100. # rechts:
  101. if self.tinnitus.rechtsLautstaerke:
  102. outdata[x][1] = self.tinnitus.rechtsLautstaerke * np.sin(2 * np.pi * self.tinnitus.rechtsFrequenz *
  103. ((x + self.start_idx) / self.framerate))
  104. # Rauschen addieren
  105. for x in range(frames):
  106. rand = (np.random.rand() - 0.5) # Zufallszahl zwischen -0.5 und 0.5
  107. #links:
  108. if self.tinnitus.linksRauschenLautstaerke:
  109. outdata[x][0] += rand * self.tinnitus.linksRauschenLautstaerke
  110. #rechts:
  111. if self.tinnitus.rechtsRauschenLautstaerke:
  112. outdata[x][1] += rand * self.tinnitus.rechtsRauschenLautstaerke
  113. self.start_idx += frames