Echtzeit-Stimmhöhen-Teiler

Elektronik-Labor  Lernpakete  Projekte  HF  



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



Elektronik-Labor  Lernpakete  Projekte  HF