Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

ss15:tonverarbeitung

Tonverarbeitung

1. Aufbau

Die Tonverarbeitung besteht aus den zwei Komponenten Taktrückgewinnung und Decodierung. Die Taktrückgewinnung muss dabei zuerst ablaufen, da sie aus dem gesendeten Taktsignal den Sendetakt rekonstruiert, der enorm wichtig für die Decodierung ist, weil diese immer taktweise stattfindet. Die Taktrückgewinnung liefert den ermittelten zugrunde liegenden Sendetakt an die Decodierungsfunktion. Diese übersetzt nun den aufgenommenen Schall mithilfe des übermittelten Taktes in eine Liste von Nullen und Einsen, welche dann noch weiter verarbeitet werden kann. Eine dritte neu hinzu gekommene Komponente ist die Queue. Deren Aufgabe ist es kontinuierlich, aber kontrolliert stückweise Audio-Daten von der Soundkarte zu liefern, die dort vom Mikrofon ankommen. Diese zusätzliche Komponente ist nötig um eine ständige Aufnahme zu ermöglichen, bei der automatisch erkannt wird wann etwas gesendet wird und das Gesendete sofort decodiert wird.

2. Taktrückgewinnung

3. Decodierung

3.1 Decodierung für Taktrückgewinnung 1.1

Die eigentliche Decodierung wird durch decode erledigt. Zu Anfang werden auch hier wieder wichtige lokale Variablen gesetzt, die aber auch global festgelegt werden könnten. Außerdem wird der Startindex und einige leere Listen zur späteren Verwendung definiert. Dann erfolgt die Diskrete Fourier-Transformation. Dafür wird der Index idx immer um die Taktlänge tl erhöht und die Fourier-Funktion immer auf dem Abschnitt zwischen aktuellem und vorigen Index angewendet. a enthält jetzt das Frequenz-Spektrum in diesem Abschnitt, allerdings benötigen wir nur den reellen Teil des Ergebnisses welches wir mit (a*np.conjugate(a)).real berechnen und an f anhängen. Nachdem alle Abschnitte in y abgearbeitete wurden werden die für uns interessanten Frequenzen f0 und f1 herausgeschrieben und in w0 bzw. w1 gespeichert. Anschließend werden die Schranken als Bedingung zur Bit-Listen-Berechnung. Zum Schluss wird noch geprüft ob f0 oder f1 im jeweiligen Abschnitt mit einer Intensität über dem Schranken-Wert vorhanden sind und so wird eine 0 oder 1 in l geschrieben, sodass man eine decodierte Bit-List erhält.

def decode(y, dt):
	'''erzeugt aus der Aufnahme y eine Liste der übertragenen Bits'''
	tdauer = 0.05 # Taktlänge in Sekunden
	tl = tdauer*RATE # Taktlänge in Samples
	f_0 = 13000*tdauer # Frequenz zur codierung der 0 umgerechnet für das Frequenz-Spektrum in f
	f_1 = 12800*tdauer # Frequenz zur codierung der 1 umgerechnet für das Frequenz-Spektrum in f
 
	idx = dt + tl # Startindex
	f = [] # Liste zur Speicherung des Frequenz-Spektrums
	l = [] # Liste für die fertig decodierten Bits
	w0 = [] # Liste zur Speicherung der Intensität bei f0 auf jeweiligen Abschnitten
	w1 = [] # Liste zur Speicherung der Intensität bei f1 auf jeweiligen Abschnitten
 
 
	while idx < len(y): # Abschnittsweise Diskrete Fourier-Transformation
		a = FFT.fft(y[idx-tl:idx+1])
		f.append((a*np.conjugate(a)).real)
		idx += tl
 
	for x in f: # Intensität bei Sendefrequenzen in jeweiligen Abschnitten
		w0.append(x[f_0])
		w1.append(x[f_1])
 
	s0 = max(w0)*0.1 # Berechnung der Schranke für intensität von f0
	s1 = max(w1)*0.1 # Berechnung der Schranke für intensität von f1
 
	for x in f: # Schreiben einer 0 oder 1 in Bit-Liste
		if x[f_0] > s0:
			l.append(0)
		elif x[f_1] > s1:
			l.append(1)
 
	return l

Signale mit Takteinteilung und Taktverschiebung

3.2 Decodierung für Taktrückgewinnung 1.2

Diese Decodierungs-Funktion gleicht überwiegend der vorigen. Allerdings wird hier keine Taktverschiebung übergeben, sondern eine Liste idx mit konkreten Indizes zur Einteilung in Abschnitte. Nun werden die Indizes vom ersten bis zum vorletzen durchgegangen und die Diskrete Fourier-Transformation auf den Abschnitt vom jeweils aktuellen bis zum nächsten Index angewendet. Weiter geht es äquivalent zur anderen decode-Funktion.

def decode(y, idx):
	'''erzeugt aus der Aufnahme y eine Liste der übertragenen Bits'''
	f_0 = 13000*tdauer # Frequenz zur codierung der 0
	f_1 = 12800*tdauer # Frequenz zur codierung der 1
	f = []
	l = []
	w0 = []
	w1 = []
 
 
	for i in range(0, len(idx)-1):
		a = FFT.fft(y[idx[i]:idx[i+1]])
		f.append((a*np.conjugate(a)).real)
 
	# [...]

Da wir aufgrund der Taktrückgewinnung 1.2 davon ausgehen können, dass in allen Abschnitten auch wirklich etwas gesendet wurde machen wir eine kleine Änderung bei der Bit-Listen-Erzeugung um zu verhindern, dass hier Bits vollständig verloren gehen, weil beide Schranken s0 und s1 nicht erreicht werden. Man kann hier dann in einem else-Zweig ein beliebiges Bit setzen, welches dann von der Fehlerkorrektur behoben wird falls es falsch ist. Logischer und wahrscheinlich auch zuverlässiger ist es aber zu prüfen welche Frequenz mit größerer Intensität vorhanden ist und dann den zur größeren Intensität zugehörigen Bit-Wert zu setzen, wie in der alternativen Variante gezeigt.

	# [...]
 
	for x in f:
		if x[f_0] > s0:
			l.append(0)
		elif x[f_1] > s1:
			l.append(1)
		else:
			l.append(1)
 
	# oder alternativ:
 
	for x in f:
		if x[f_0] > s0:
			l.append(0)
		elif x[f_1] > s1:
			l.append(1)
		elif x[f0] > x[f1]:
			l.append(0)
		else:
			l.append(1)

Signalintensitäten abschnittsweise und Schranken

4. Queue

Die Umsetzung der Queue basiert auf microlistener_queue.py von Stefan. Wir haben noch die Funktionen get_frames und set_frames ergänzt. Über diese Klasse können wir Objekte erzeugen, die einen Stream von der Soundkarte erhalten und diesen kontinuierlich in die queue schreiben. Über get_frames kann man sich daraus immer n Frames zur Weiterverarbeitung holen, die dann zuerst zur Taktrückgewinnung kommen. Mit der Funktion set_frames werden Frames zurück in die queue geschrieben, die bei der Weiterverarbeitung übrig geblieben sind und beim nächsten Aufruf von get_frames verarbeitet werden. Leider haben wir das gesamte Programm bisher noch nicht mit Verwendung der Queue zum fehlerfreien Laufen gebracht.

#!/usr/bin/python
# coding=utf8
 
from __future__ import division
 
import pyaudio
import time
import numpy as np
 
# GLOBALE VARIABLEN
 
CHANNELS=1
RATE=44100
CHUNK=2**11
 
 
### Diese Klasse wird im Modul microlistener definiert.
### Sie kann in allen Projekten verwendet werden, die eine
### kontinuierliche Audio-Eingabe brauchen.
 
 
class MicroListener(object):
 
	def __init__(self,rate,channels,chunk,playback=False):
		'''kreiert einen Stream, der mit der Samplerate 'rate'
		und 'channels' Kanälen über das Microphon Audio aufnimmt.
		Nach jeweils 'chunk' Frames wird die callback-Funktion
		aufgerufen. 
 
		Die Callback-Funktion hängt die neuesten Daten an das
		numpy-Array queue an (es ist ein Nx1-Array für 1-Kanal-Aufnahmen
		und ein Nx2-Array für 2-Kanal-Aufnahmen.) 
 
		Falls playback==True, werden die Daten an den Lautsprecher
		durchgereicht.
		'''
 
		self.p = pyaudio.PyAudio()
		self.stream = self.p.open(format=pyaudio.paInt16 ,
                channels=channels,
                rate=rate,
                input=True,
                output=playback,
                stream_callback=self.micro_callback,
                frames_per_buffer=chunk)
		self.queue=np.zeros((0,CHANNELS))
		self.stream.start_stream()
		time.sleep(0.5)
 
	def micro_callback(self, in_data, frame_count, time_info,status):
		'''callback Funktion: wird vom PyAudio-Objekt aufgerufen,
		wenn CHUNK Frames von der Soundkarte (Mikrophon) gelesen wurden'''
		y=np.array(np.fromstring(in_data,dtype=np.short),dtype=np.float)
		if CHANNELS==2:
			y=y.reshape((y.shape[0]//2,2))
		else:
			y=y.reshape((y.shape[0],1))
		#### Eine Art Lautstärkeanzeige, nur zu Testzwecken, sonst
		#### auskommentieren:
		# print int(np.linalg.norm(y[:,0])/2000.)*'*'  
		self.queue=np.concatenate((self.queue,y),axis=0)
		return (in_data, status)
 
	def __del__(self):
		self.stream.stop_stream()
		self.stream.close()
		self.p.terminate()
 
	def get_frames(self, n):
		while self.queue.shape[0] < n:
			time.sleep(0.001)
		y = self.queue[:n+1,:]
		self.queue = self.queue[n+1:,:]
		return y
 
	def set_frames(self, r):
		self.queue = np.concatenate(r, self.queue)
ss15/tonverarbeitung.txt · Zuletzt geändert: 2016/05/10 14:46 (Externe Bearbeitung)