Echtzeit-Stimmhöhen-Teiler
Aus
einer hohen Stimme eine tiefere machen, das hat einen ganz praktischen
Nutzen. Ich denke da besonders einen älteren Herrn, dessen
Gehör in letzter Zeit nicht mehr so gut funktioniert wie
früher, was dazu führt, dass er seine Frau nicht mehr richtig
verstehen kann. Auf einer Familienfeier konnte ich kürzlich
beobachten, dass er dagegen eine tiefe Männestimme ganz ohne
Probleme verstehen konnte. Es ist also eine Frage der oberen
Grenzfrequenz, die bei jedem Menschen im Alter absinkt. Irgendwann
reicht es nicht mehr für eine hohe Frauenstimme. Das führt
nicht nur zu dauernden Missverständnissen, sondern auch zu Streit
und Ärger. „Wenn du endlich mal deutlich sprechen
würdest, könnte ich dich auch verstehen!“ „Ich
rede wie immer! Wenn du mal richtig zuhören würdest,
könntest du mich auch verstehen!“ Und so weiter. Könnte
man aber die hohe Stimme der Frau in eine tiefere umsetzen, wäre
der Frieden gerettet. Und genau das ist mein Ziel.
Der erste Versuch geht von einem bestehenden VB6-Programm aus, das ich mal für den Einsatz als SSB-Demodulator
in einem Software Defined Radio eingesetzt habe. Eine FFT rechnet das
Signal in die einzelnen Frequenzen um, die dann passend verschoben oder
umgerechnet werden. Danach erzeugt eine inverse FFT wieder ein
Audiosignal. Für ein SSB-Signal muss man Frequenzen um einen
konstanten Betrag verschieben. Für den Stimmhöhen-Teiler muss
dagegen jede einzelne Frequenz eines Audiosignals um einen konstanten
Faktor verändert werden. Im Kern sieht das so aus:
FourierTransform NFFT, Single1(), Single2(), Single3(), Single4()
For n = 0 To 4095
Single1(n) = Single3(n)
Single2(n) = Single4(n)
Next n
For n = 0 To 4095
Single3(n) = 0
Single4(n) = 0
Next n
j = 1 + (HScroll2.Value) / 100
For n = 2 To 1000 '
u = (j * n) - Int(j * n) 'upper bin
l = 1 -
u
'lower bin
Single3(n) = l * Single1(Int(j * n)) + u * Single1(Int(j * n) + 1)
Single4(n) = l * Single2(Int(j * n)) + u * Single2(Int(j * n) + 1)
Next n
FourierTransform NFFT, Single3(), Single4(), Single1(), Single2(), True
Download: DSPpitch.zip
Das
Verfahren funktioniert zwar grundsätzlich, aber es gibt noch
einige Schönheitsfehler. Insbesondere entstehen
Knackgeräusche am Ende jedes Frames. Man kann aber schon mal
ausprobieren, wie es sich anhört. Eine hohe Frauenstimme wird zu
einer auffällig tiefen Männerstimme, wenn man jede Frequenz
durch 1,5 dividiert. Das Programm kann das Spektrum zwar sogar
halbieren, aber dabei leiden alle Konsonanten so stark, dass die
Verständlichkeit gegen Null geht.
Das folgende
Youtube-Video zeigt das Programm in Aktion. Das originale Audiosignal
stammt aus dem Radio, und zwar aus der von mir hoch geschätzten
„Spielart“ im WDR5 am Sonntagnachmittag. Eine Sprecherin
mit hoher Stimme liest eine Geschichte vor. Die Stimme wird bis zu
einer tiefen Männerstimme verändert. Die
Störgeräusche muss man sich erst mal wegdenken.
Video: http://youtu.be/a_4tlksCDfA
Das
jetzige Programm ist nicht mehr als eine erste Machbarkeitsstudie.
Damit so ein Gerät in der Praxis einsetzbar wird, muss es viel
kleiner sein und über zwei echte Potis mit Drehknöpfen
einstellbar sein. Ich stelle mir vor, dass man einen Kopfhörer
verwendet und das Mikrofon in das kleine tragbare Gerät eingebaut
ist.
Vermutlich gibt es mehrere mögliche
Lösungsansätze, vielleicht mit einem DSP-Board, mit dem
Raspberry Pi, dem Elektor-Linux-Board, einem Android-System oder
vielleicht sogar mit einem ATmega oder dem Xmega? Ich vermute auch,
dass es Leute gibt, die locker mit solchen Aufgaben der
Signalverarbeitung umgehen. Deshalb habe ich die Aufgabe auf der Elektor-Project-Page
vorgestellt, mit der Bitte an alle Fachleute, bei der Umsetzung zu
helfen. Oder gibt es vielleicht sogar schon eine fertige Lösung?
Wenn nicht, muss sie jedenfalls entwickelt werden. Allein schon als
Beitrag zum Frieden in der Welt. Und außerdem sind wir alle in
einigen Jahrzehnten vielleicht auch schon in der Situation, dass das
Gehör nachlässt. Wenn man dann mal was genau hören will,
ist das Gerät schon da, wenn man gerade lieber nichts verstehen
will, dann schaltet man es einfach aus.
Version 0.2
Inzwischen bin ich einen Schritt weiter gekommen und konnte
die Störgeräusche etwas verringern. Der Trick dabei: Ich merke mir jeweils den
letzten Wert eines Frames und passe dann den Anfang des nächsten daran an. Dabei
werden große Sprünge im Signal vermieden. Nicht allerdings Phasensprünge,
weshalb immer noch ein geringes Knacken übrig bleibt.
For n = 0
To BLKSIZE - 1
Single1(n) = Single1(n) + Single2(n)
If n = 0 Then diff = Last - Single1(0)
Single1(n) = Single1(n) + diff
diff = diff * 0.98
If n = BLKSIZE - 1 Then Last = Single1(n)
Single1(n) = Single1(n) * Multiplier
If Single1(n) < -1 Then Single1(n) = -1
If Single1(n) > 1 Then Single1(n) = 1
outBuffer(n * 2) = Int(Single1(n) * 32000)
outBuffer(n * 2 + 1) = outBuffer(n * 2) 'interleave L and R
Next n
Download: DSPpitch2.zip
Hier gibt es ein Hörbeispiel. Eine Dame spricht im Kinderfunk
über Kaffeebohnen und Elefantenkacke, natürlich in wechselnder Tonhöhe. http://youtu.be/YVhDQnprYUo
Das Singer Tempophon Verfahren (OLA)
xyphro hat auf der Elektorseite dieses Verfahren beschreiben und in ein Delphi-Programm umgesetzt:
http://www.elektor-projects.com/contribution/singer-tempophon-verfahren-ola.12557.html
http://www.katjaas.nl/pitchshift/pitchshift.html
Dieses Verfahren hat er auch einen MSP430-Mikrocontroller umgesetzt: Experimental voice shifter with MSP430 Launchpad
http://www.youtube.com/watch?v=LMa5t2K1-F0&feature=plcp
http://www.elektor-projects.com/contribution/msp430-basierte-l-sung.12573.html
Lösung mit Mikrocontroller ATmega168
Vorverstärker LM358, ALC, Tiefpass (passiv, 2 x RC), ADC-Controller-PWM, Tiefpass, NF-Verstärker LM386
Dank xyphro habe ich nun neue Wege beschritten und die ganze Sache
auf einem ATmega168 mit Bascom zum Laufen gebracht. Es ist noch nicht
ganz perfekt, zeigt aber schon wo es mal hingehen könnte. Im Moment
arbeite ich mit 8-Bit-Daten und 512 Byte Pufferlänge. Das Tiefpassfilter
am Eingang sollte noch verbessert werden. Außerdem wäre eine höhere
Auflösung gut. Vermutlich ist die MSP430-Lösung von xyphro besser in
Bezug auf Klang und Störungsfreiheit.
Vorverstärker und ALC habe ich aus meiner Mini-Lichtorgel
übernommen. Das Anti-Aliasing-Filter am Eingang besteht nur aus
zwei RC-Gliedern und ist noch nicht optimal. Deshalb war es
wichtig, die Abtastrate möglichst hoch zu wählen. Das
Ausgangsfilter ist dagegen weniger kritisch, weil die PWM-Ausgabe 32
kHz hat.
Hier ein Video zum Gerät, das ich in ein Radiogehäuse eingebaut habe:
http://youtu.be/Lfpzolm6-PE
Nach der Aufnahme habe ich das Verfahren noch etwas
verbessert und insbesondere die Abtastrate bis auf 19 kHz hochsetzen können.
Dazu war es nötig, die Signalverarbeitung aus der Interruptroutine ins
Hauptprogramm zu verschieben. Der AD-Wandler arbeitet mit höherem Takt als
normal, muss aber auch nur 8 Bit Genauigkeit liefern. Damit ist der Klang schon sehr klar.
'Bascom ATmega168, übertaktet mit 25 MHz
$regfile = "m168def.dat"
$crystal = 25000000 '25 MHz übertaktet
$hwstack = 16
$swstack = 16
$framesize = 32
'Baud = 9600
Dim Ringpuffer(512) As Byte
Dim Zeiger1 As Word
Dim Zeiger2 As Word
Dim Zeiger3 As Word
Dim Zeiger4 As Word
Dim Phasor As Word
Dim Fenster As Integer
Dim Dout As Integer
Dim Din As Word
Dim Dout1 As Word
Dim Dout2 As Word
Dim Df As Byte
Dim Dt As Word
Config Timer1 = Pwm , Prescale = 1 , Pwm = 8 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
Start Timer1 'PWM 32 kHz
Config Adc = Single , Prescaler = 8 , Reference = Off
Start Adc
Config Portb = Output
Do
Toggle Portb.0 'Abtastrate 19,4 kHz
If Zeiger1 = 0 Then
Dt = Getadc(0)
Shift Dt , Right , 5
End If
Zeiger1 = Zeiger1 + 1
Zeiger1 = Zeiger1 And 511
Din = Getadc(1) / 4 'Speichern
Ringpuffer(zeiger1 + 1) = Din
Zeiger4 = Zeiger4 + Dt
Phasor = Zeiger4
Shift Phasor , Right , 7
Zeiger2 = Zeiger1 - Phasor 'Zeiger erhöhen
Zeiger2 = Zeiger2 And 511
If Phasor < 256 Then
Fenster = Phasor
Else
Fenster = 512 - Phasor
End If
Dout1 = Ringpuffer(zeiger2 + 1) * Fenster '* Fenster
Zeiger3 = Zeiger2 + 256
Zeiger3 = Zeiger3 And 511
Fenster = 255 - Fenster
Dout2 = Ringpuffer(zeiger3 + 1) * Fenster
Dout = Dout1 + Dout2
Shift Dout , Right , 8
Pwm1a = Dout
Loop
Praxistest
Gestern habe ich das fertige Gerät mit Mikrofon, Mega168 und
Kopfhörer in der Praxis getestet. Der besagte ältere Herr hat alle
Einstellungen für verschiedene Stimmen durchprobiert. Es gab tatsächlich eine
Einstellung bei etwa 75 % der Normafrequenz, bei der er seine Frau besser
verstehen konnte. Auch meine Stimme kam mit dieser Einstellung gut rüber und wurde
sogar noch bei nur 50% der Originalfrequenz verstanden.
Auffällig war, dass die Lautstärke nicht sehr weit
aufgedreht werden musste. Eine tiefere Frequenz bringt also mehr als große Lautstärke.
Das erklärt auch, warum das vorhandene Hörgerät nicht mehr richtig hilft.
Eine Schwierigkeit ist noch, dass die eigene Stimme auch
gehört werden kann, was sehr irritierend ist. Hilfreich war ein großer Abstand
zum Mikrofon und zum Sprecher, sodass der Sprecher deutlich lauter durchkommt
als die eigene Stimme.
Insgesamt habe ich den Eindruck, dass dieses Gerät keine
Wunder vollbringt, wenn man schon länger schlecht hört. Man muss dann relativ
viel trainieren, was viel Geduld aller beteiligten Personen erfordert. Das
bleibt spannend, ob das Gerät letztendlich akzeptiert werden kann.
Hinweise zum Time-Domain
Harmonic Scaling von Thomas Gries:
Eine Sache, die mich schon seit meinem Studium beschäftigt (Tip von Prof. Peter
Noll, Inst. f. Fernmeldetechnik, TU Berlin) ist das Time-Domain
Harmonic Scaling (TDHS).
Siehe kleinen Artikel dazu https://en.wikipedia.org/wiki/Time-domain_harmonic_scaling#
mit Referenzen und mit Link auf eine Software.
PICOLA and TDHS: http://keizai.yokkaichi-u.ac.jp/~ikeda/research/picola.html