Funktionen zur Soundwiedergabe geändert. Wiedergabe ist jetzt echt dynamisch

This commit is contained in:
Heiko Ommert 2020-06-13 15:05:06 +02:00
parent eddc1e4926
commit 600968195e
2 changed files with 55 additions and 66 deletions

View File

@ -2,8 +2,8 @@ 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 sounddevice as sd #zum abspielen des audio-arrays
import random as random #für die Rauscherzeugung import numpy as np
import time #nur für die Tests, um Zeit zum Anhören der Sounds einzubauen import sys #für Fehlermeldungen
#In .wav-Dateien wird der Ton in absoluten Werte eingetragen. Die Standart-framerate ist 44100 #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 #das heißt für jede Sekunde an Ton gibt es 44100 Werte, die die Tonwelle über die Zeit beschreiben
@ -40,8 +40,9 @@ class Tinnitus: #beinhaltet alle Werte, die vom Nutzer eingestellt werden
return 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. """Sound beinhaltet alle Variablen, die zum erstellen einer .wav-Datei benötigt werden (siehe soun.wav_speichern())
#Die Audiodaten werden in das Array "audio" geschrieben 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:
@ -56,83 +57,71 @@ class Sound:
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 self.mute = True # wenn der mute boolean auf true gesetzt ist, sollte kein Ton ausgegeben werden
return 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 neu_audio(self): #Schreibt Werte in das audio-Array. Soll später abhängig sein von den Tinnitus-Werten(Frequenz, Rauschen, ...)
freq = self.tinnitus.linksFrequenz
dauer_ms = 2000.0
amp = self.tinnitus.linksLautstaerke
rauschen = self.tinnitus.linksRauschenLautstaerke
self.audio.clear()
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)))
#das Rauschen addieren
if(self.tinnitus.linksRauschenLautstaerke != 0):
for x in range(int(num_samples)):
self.audio[x] += random.random() * rauschen
return return
def wav_speichern(self): #ezeugt/aktuallisiert die .wav-Datei def wav_speichern(self): #ezeugt/aktuallisiert die .wav-Datei
self.neu_audio()
#das 100ms audio-array strecken:
for x in range(5): # entspricht 10 Sekunden
for y in range(int(self.framerate/10)):
self.audio.append(self.audio[y])
wav_file = wave.open(self.name, "w") wav_file = wave.open(self.name, "w")
print("Sound wird als .wav-Datei gespeichert. Bitte warten...\nDer Vorgang kann ca. 30 Sekunden dauern")
#zuerst muss ein Array mit Audiodaten gefüllt werden
audio = []
freq = self.tinnitus.linksFrequenz
dauer_ms = 10000.0 #10 Sekunden
amp = self.tinnitus.linksLautstaerke
rauschen = self.tinnitus.linksRauschenLautstaerke
num_samples = dauer_ms * (self.framerate / 1000.0) #framerate -pro Sekunde- umgerechnet in -pro Millisekunde-
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)))
if(rauschen): #das Rauschen addieren
for x in range(int(num_samples)):
audio[x] += (np.random.rand() - 0.5) * rauschen
#erst werden die Rahmen-Parameter gesetzt #erst werden die Rahmen-Parameter gesetzt
self.nframes = len(self.audio) # Anpassen des nframes-Parameters 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 #dann wird das audio-Array an die Datei übertragen
for sample in self.audio: 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()
print("Speichern beendet.")
return return
"""Die Objekt-Funktion __enter__() 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 if not self.mute: # NEVER play sound when patient mutes GUI
sd.play(self.audio, self.framerate, loop=1) self.sound_obj.__enter__()
return return
def stop(self): def stop(self):
sd.stop() self.sound_obj.__exit__() #beendet die asynchrone Soundwiedergabe
return return
# tinnitus1 = Tinnitus() """Die Funktion callback() erzeugt bei jedem Aufruf die Audiodaten, abhängig von den aktuellen Tinnitus-Variablen.
# sound = Sound(tinnitus1) Die errechneten Werte werden in das NumPy-Array 'outdata' geschrieben """
# #momentan nur Mono. Es werden die Werte des linken Ohrs genutzt
# if(1): def callback(self, outdata, frames, time, status):
# print("======abnehmende Lautstärke==========") if status: #gibt Warnungen aus, wenn das Soundobjekt auf Fehler stößt (hauptsächlich over/underflow wegen timingproblemen)
# for x in range(10): print(status, file=sys.stderr)
# print(" Lautstärke = ", tinnitus1.linksLautstaerke) t = (self.start_idx + np.arange(frames)) / self.framerate
# sound.play() t = t.reshape(-1, 1)
# time.sleep(0.6)
# tinnitus1.linksLautstaerke -= 0.1 #Sinuston ins Array schreiben
# print("\n\n") outdata[:] = self.tinnitus.linksLautstaerke * np.sin(2 * np.pi * self.tinnitus.linksFrequenz * t)
# time.sleep(1)
# tinnitus1.linksLautstaerke = 1 #Rauschen addieren
# if(self.tinnitus.linksRauschenLautstaerke):
# for x in range(len(outdata)):
# rand = (np.random.rand() - 0.5) * self.tinnitus.linksRauschenLautstaerke
# if(0): outdata[x] += rand
# print("=========RauschTest============")
# for x in range(10): self.start_idx += frames
# tinnitus1.linksRauschenLautstaerke += 0.01
# print(" Rauschen = ", tinnitus1.linksRauschenLautstaerke)
# sound.play()
# time.sleep(1)
#
# if(0):
# tinnitus1.speichern()
#
# sd.stop()
# print("ENDE")

View File

@ -16,13 +16,13 @@ def links_scale_lautstärke_change(self):
def links_scale_frequenz_change(self): def links_scale_frequenz_change(self):
tinnitus.linksFrequenz = float(linksScaleFrequenz.get())*1000 # scale liefert 20-20kHz, tinnitus.frequenz in Hz tinnitus.linksFrequenz = float(linksScaleFrequenz.get())*1000 # scale liefert 20-20kHz, tinnitus.frequenz in Hz
print("Links Frequenz = ", linksFrequenz, " Hz") print("Links Frequenz = ", tinnitus.linksFrequenz, " Hz")
sound.play() sound.play()
def links_scale_rauschen_lautstärke_change(self): def links_scale_rauschen_lautstärke_change(self):
tinnitus.linksRauschenLautstaerke = float(linksScaleRauschenLautstärke.get()/200) tinnitus.linksRauschenLautstaerke = float(linksScaleRauschenLautstärke.get()/1000)
print("Links Rauschen Lautstärke = ", tinnitus.linksRauschenLautstaerke*200, "%") print("Links Rauschen Lautstärke = ", tinnitus.linksRauschenLautstaerke*1000, "%")
sound.play() sound.play()