Sitzung: Jeden Freitag ab 14:30 s.t. online. Falls ihr den Link haben wollt, schreibt uns.

C-Kurs/DTMF-Encoder

In dieser Aufgabe soll ein Generator für DTMF-Töne erstellt werden. DTMF steht für Dual-tone multi-frequency und ist auch als Mehrfrequenzwahlverfahren bekannt.

Wenn du diese Aufgabe in den Rechnerräumen bearbeitest, benutze bei dem Abspielen der Töne bitte Kopfhörer, um andere nicht zu stören.

Was ist DTMF?

Kurz gesagt: ein DTMF-Ton besteht aus zwei überlagerten Sinuswellen. Für jede Taste auf dem Telefon ist eine bestimmte Frequenzkombination dieser beiden Wellen festgelegt. Genaueres sowie die Frequenzen für die Tasten finden sich hier.

Wie werden Töne im Rechner dargestellt?

Töne lassen sich als PCM-Daten speichnern. Unser Programm soll solche PCM-Daten generieren. Ausreichend sind hier 8 Bit pro Sample, sodass sich die Tondaten in einem Array aus unsigned char ablegen lassen. Die Samples sind dann vorzeichenlos, was bedeutet, dass bei 127 der Mittelwert ist. Als Samplerate reichen für uns auch recht niedrige 8 kHz, das heißt für jede Sekunde Ton werden 8000 Samples benötigt.

Aufgabenstellung

Jetzt soll Schritt für Schritt der Tongenerator implementiert werden.

Sinuswellen generieren

Schreibe eine Funktion, die Sinuswellen als Mono-8Bit-8kHz-PCM-Daten erzeugt. Der Funktion soll die gewünschte Länge des Tons, die Frequenz des Tons, und die Amplitude, sowie einen Pointer auf ein int, in dem die Anzahl der generierten Samples abgelegt werden kann. Die Funktion soll dann ausreichend Speicher für die Daten allokieren und jedes Sample mit den passenden Sinuswerten befüllen und sie am Ende zurückgeben. Der Prototyp der Funktion sollte also etwa so aussehen:

unsigned char* generateSine(int ms, int freq, float amplitude, int *numSamples)

Rufe nun in der main-Funktion die Generierungsfunktion auf und speichere die Daten mit fwrite in eine Datei (z.B. audiofile). Zum Abspielen der Datei benutzt du je nach System eines der folgenden Kommandos:

  • aplay -t raw -f U8 -c 1 -r 8000 audiofile
  • pacat --format=u8 --channels=1 --rate=8000 -p audiofile
  • cat audiofile > /dev/dsp
  • mplayer -rawaudio samplesize=1:channels=1:rate=8000 -demuxer rawaudio audiofile.

Hinweis zum Debuggen

Um sich die Audiodaten anzusehen, kann das Programm Audacity verwendet werden. Öffnen lassen sie sich dort über Datei -> Importieren -> Rohdaten, dann die Datei auswählen, dann bei Codec Unsigned 8 bit PCM, bei Kanäle 1, bei Samplefrequenz 8000 Hz auswählen. Nun kann man in die Samples reinzoomen (Button mit Lupe und Plus) und sehen, ob der Sinuswellen ordentlich sind. Mit Analyse -> Frequenzanalyse kann überprüft werden, ob die Sinuswelle die richtige Frequenz hat.

Sinuswellen überlagern

Schreibe jetzt eine Funktion, die zwei Sinuswellen gleicher Länge überlagern kann. Die von ihr erstellte Samples sollen durch Addition der beiden Samples der Sinusdaten gebildet werden. Doch Vorsicht: da die Daten vorzeichenlos sind, müssen sie temporär vorzeichenbehaftet werden, so dass der Mittelwert 0 (also Wertebereich -128 bis 127) wird, dann können sie addiert werden und dann wieder vorzeichenlos abgespeichert werden (mit dem Mittelwert 127, Wertebereich 0 bis 255). Der Prototyp der Funktion sollte etwa so aussehen:

unsigned char* mixPcm(unsigned char *a, unsigned char *b, int numSamples)

Teste nun die Funktion wieder, indem du zwei Sinuswellen unterschiedlicher Frequenz erzeugst und sie mit der neuen Funktion überlagerst. Als Amplitude sollte jeweils etwas weniger als die Hälfte gewählt werden, um bei der Addition Clipping zu vermeiden. Untersuche das Ergebnis wieder, wie in der ersten Teilaufgabe.

DTMF-Töne

Schreibe eine Funktion, die für eine Ziffer zwischen 0 und 9 den passenden DTMF-Ton mithilfe der beiden vorigen Funktionen generieren kann. Ihr Prototyp kann etwa so aussehen:

unsigned char* generateDtmfTone(int number, int ms, int *numSamples)

Teste wieder. Zum Vergleich der Töne kannst du jetzt ein Telefon benutzen.

Nummernsequenzen generieren

Benutze jetzt die Generierungsfunktion, um eine Folge von Ziffern in eine Datei zu schreiben. Die Töne sollten durch Stille getrennt sein. Du brauchst also noch eine Funktion, die Samples generiert, die alle den Wert 127 haben. Die Nummer soll als Kommandozeilenparameter oder Benutzereingabe eingegeben werden.

Wenn du jetzt ein analoges Telefon zur Hand hast, kannst du die Töne ins Mikrofon des Hörers anspeilen und damit Nummern wählen.

Hinweis

Vergesse nicht, alle Tondaten, die du nicht mehr brauchst, mit free wieder freizugeben.

Kommentare

Wenn du Anmerkungen zur Aufgabe hast oder Lob und Kritik loswerden möchtest, ist hier die richtige Stelle dafür. Klicke einfach ganz rechts auf "bearbeiten" und schreibe deinen Kommentar direkt ins Wiki. Keine Scheu, es geht nichts kaputt ;)