Seite 1 von 1
Tonaufnahme einlesen und Komprimieren
Verfasst: Dienstag 7. November 2017, 17:34
von yakari9
Guten Abend miteinander
folgendes: Ich baue ein Messgerät mit einem Raspberry Pi. Am Raspberry Pi ist ein Piezosensor angeschlossen welcher an einem Objekt befestigt wird. Nun mache ich eine Tonaufnahme (arecord) und erstelle daraus ein wave-File. Als nächstes muss ich prüfen, ob überhaupt etwas "spannendes" aufgenommen wurde. Dazu soll überprüft werden, ob ein gewisser Amplituden-Wert überschritten wird. Falls nein, kann die Aufnahme wieder gelöscht werden. Falls der Wert erreicht wird sollen Alle Werte die diesen Wert überschritten haben mit ihrem "Timestamp"/Index in eine Datei gepeichert werden. Also so ala:
Sekunden | Wert
1.2 | 0.2
1.3 | 0.5
(Diese Datei muss nachher über eine langsame Netzwerkverbindung mit Datenlimit verschickt werden und somit sollen "uninteressante" Messungen gar nicht verschickt werden)
Ich bin aktuell so vorgegangen, dass ich mit arecord eine .wav erstellt habe und sie nachher über "sox" in eine .dat Datei umgewandelt habe. Nun soll diese .dat eingelesen werden und in einer Schleife eine Grenzwert geprüft werden.
Problem ist jetzt, dass dieses .dat mit open(datei.dat) als String eingelesen wird, mit ganz vielen Leerschlägen und ";" usw.
Beispiel (eben mit ganz vielen Leerschlägen vor und nach den Zahlen:
0.0013605442 0.00012207031
0.0014058957 0.00015258789
0.0014512472 0
0.0014965986 6.1035156e-05
0.0015419501 0.00021362305
Wie kann ich diese Strings nun in ein sinnvolles Zahlenformat formatieren, damit ich dann Grenzwerte prüfen kann?
Ich hoffe jemand versteht was ich gerne möchte und kann mir helfen.
Lieber Gruss
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Dienstag 7. November 2017, 18:05
von Sirius3
@yakari9: beim Umwandeln dieser Strings in Zahlen via `float` sind Leerzeichen egal. Noch besser machst das Einlesen per `numpy.loadtxt`.
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 8. November 2017, 05:08
von __deets__
Statt einen solch mühseligen mehrschrittigen Prozess benutze pyaudio und arbeite direkt auf den angelieferten Buffern.
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 8. November 2017, 16:17
von yakari9
Hallo
vielen Dank für die Antworten.
Ich habe mich jetzt mal mit dem pyaudio befasst.
Code: Alles auswählen
Testrecording_Dauer = int(Rate / Chunk)*Dauer
t1=time.time()
for i in range(0, Testrecording_Dauer): #Eine Sekunde aufnehmen und Schauen was abgeht...
data = stream.read(CHUNK, exception_on_overflow = False)
aframe = np.asarray(data)
npframes = np.vstack((npframes,aframe))
t2=time.time()
print ("finished testrecording. Dauer: ", t2-t1)
Wenn ich jetzt 1 Senkunde aufnehme, dann ergibt die Rechnung t2-t1 ca 1.6-2.3 Sekunden!
Wenn ich jetzt 20 Sekunden aufnehme, dann ergibt die Rechnung t2-t1 ca. 33 Sekunden... Warum ist das so?
Danke für eure Hilfe und Gruss
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 8. November 2017, 23:54
von __deets__
das sieht etwas knoedelig aus. Schau dir mal PEP8 zu Namenskonvention in Python an. So ist das ein ziemliches Chaos und schlecht lesbar.
Welche Werte haben die einzelnen Variablen? Warum mal Chunk und mal CHUNK?
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Donnerstag 9. November 2017, 01:00
von snafu
yakari9 hat geschrieben:Als nächstes muss ich prüfen, ob überhaupt etwas "spannendes" aufgenommen wurde. Dazu soll überprüft werden, ob ein gewisser Amplituden-Wert überschritten wird. Falls nein, kann die Aufnahme wieder gelöscht werden.
Also die Werte aus einem Numpy-Array, die einen bestimmten Wert überschreiten, kriegt man so:
Code: Alles auswählen
# 10 Zufallswerte als Beispiel
values = np.random.rand(10)
print(values)
print(values[values >= 0.5])
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Donnerstag 9. November 2017, 11:44
von yakari9
Also, natürlich ist das CHUNK immer gleich geschrieben. Ich habe hier mal den ganzen Code eingefügt.
Es wird folgendes gemacht:
1. Importieren von Modulen
2. Variablen definieren
3. Jetzt kommt eine While Schleife, mit dieser soll eine kontinuierliche Ausführung des Programmes möglich sein
a) Testrecording starten (Kurze Aufnahme die prüft, ob ein Geräusch auftritt = Grenzwert überschritten wird)
Falls nicht, beginnt die while Schleife wieder von vorne...
b) sobald "Lärm" detektiert wird (= Grenzwert überschritten) kommt, startet die eigentliche Messung (hier Dauer 10Sekunden siehe Variable Messdauer). DIese funktioniert wiefolgt:
i.) wieder Aufnehmen wie punkt 3.a
ii.) Gerenzwert prüfen wie von "snafu" vorgeschlagen
iii.) Gefundene Überschreitungen mit Ihren Indizes in ein Array schreiben und abspeichern
iv.) je nach Wert der Variable "go" wird jetzt der Prozess wieder bei Punkt 3.a gestartet oder beendet
4. Wird der Prozess beendet, wird noch die Mess-Aufnahme als .wav gespeichert. Damit soll überprüft werden können ob das Programm funktioniert
Das ganze umwandeln, überprüfen, abspeichern und kopieren geht keine Sekunde! Die Aufnahme ist jedoch einfach immer langsamer als sie sollte. Die Dauer eines Testrecordings (welche 1s sein sollte) ist 1.6-2.3s
Die Dauer eines Messdurchgangs (welche 16s sein sollte) ist 16s
Komischerweise scheint der Faktor 1.6 irgendwie mitzuspielen. Die erstellte .wav Datei ist aber nicht um diesen Faktor verlängert!
Was meint ihr?
Code:
Code: Alles auswählen
import pyaudio
import wave
import time
import sys
import numpy as np
import utility
from shutil import copyfile
### Aufnahmevariablen ###
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = 1
WAVE_OUTPUT_FILENAME = "record.wav"
go=0
audio = pyaudio.PyAudio()
###-----###
### stream definieren ###
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True, input_device_index=2,
frames_per_buffer=CHUNK)
###-----###
##### Messvariablen ###########
Grenzwert=0.002
Testrecording_Dauer = 1 #Sekunden
Messdauer = 10 #Sekunden
###-----###
while go<2:
### Testrecording ###
npframes=np.array([0]) # np.Array für die Aufnahme generieren
print("Testrecording...")
t1=time.time()
for i in range(0, int(RATE / CHUNK * Testrecording_Dauer)): #Eine Sekunde aufnehmen und Schauen was abgeht...
data = stream.read(CHUNK, exception_on_overflow = False)
#frames.append(data)
aframe = np.asarray(data)
npframes = np.vstack((npframes,aframe))
t2=time.time()
print ("finished testrecording. Dauer: ", t2-t1)
### Daten umwandeln ###
sig = np.frombuffer(npframes, dtype='<i2').reshape(-1, CHANNELS) #Das Signal wird konvertiert
normalized = utility.pcm2float(sig, np.float32) #und normalisiert
### Überprüfen ob der Grenzwert überschritten wurde ###
for i in range(0, len(normalized)):
if normalized[i] > Grenzwert:
go = 1 #Falls ja(go=1), wird das Flag go=1 gesetzt und die Schleife abgebrochen
break
### Messung - Wird gestartet wenn ein Geräusch detektiert wurde ###
if go == 1: #Falls go=1 dann soll eine lange Messung durchgeführt werden. Falls nicht werden die Testmessungen weitergeführt
print("Messung starten")
go=0 #Zurücksetzen des Go-Flags = nach dem Messdurchgang wieder mit Testrecording beginnen
t1=time.time()
frames=[]
for i in range(0, int(RATE / CHUNK * Messdauer)): #Messdauer Sekunden lang messen
data = stream.read(CHUNK, exception_on_overflow = False)
aframe = np.asarray(data)
npframes = np.vstack((npframes,aframe))
t2=time.time()
print ("finished recording. Dauer: ", t2-t1)
frameswav=npframes #Variable für das Abspeichern der wav-Datei
### Daten umwanden ###
sig = np.frombuffer(npframes, dtype='<i2').reshape(-1, CHANNELS)
normalized = utility.pcm2float(sig, np.float32)
go=2 # go=2 Messung nur einmal durchführen und anschliessend Programm beenden
t8=time.time()
### Grenzwerte prüfen ###
overgrenzwert=normalized[normalized >= Grenzwert] # Array mit allen Werten über dem Grenzwert
overgrenzwert = np.transpose(overgrenzwert)
indexes=np.nonzero(normalized >= Grenzwert) # Indexes der Werten über dem Grenzwert
indexes = np.transpose(indexes)
indexes = np.delete(indexes, 1,1)
compressed = np.stack((indexes,overgrenzwert[:, None]),axis=1) # Zusammenführen der Arrays
### Datei speichern
np.savetxt('compressed.dat',compressed,delimiter=';') #Speichern der Datei "Compressed.dat"
copyfile('compressed.dat', '/home/shares/test/compressed.dat') # Kopieren in Share-Ordner
print("Das hat gedauert, und zwar: ", time.time()-t1," Sekunden")
#stop Recording
stream.stop_stream()
stream.close()
audio.terminate()
### Wave Datei zur Überprüfung speichern
waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
frameswav.tolist()
waveFile.writeframes(b''.join(frameswav))
waveFile.close()
copyfile(WAVE_OUTPUT_FILENAME, "/home/shares/test/wavaufnahme.wav")
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Donnerstag 9. November 2017, 14:10
von yakari9
Hmm.. habe den Wert von CHUNK auf 512 reduziert... jetzt dauert eine 20-Sekunden-Messung, inkl. Grenzwerten prüfen, speichern und verschieben 21.2Sekunden.
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Donnerstag 9. November 2017, 23:34
von snafu
Wozu brauchst du eigentlich
vstack()? Falls du die Frames hintereinander weg schreiben willst, dann ist dafür
hstack() gut geeignet:
Code: Alles auswählen
data = np.hstack(
stream.read(chunk_size) for _ in range(num_chunks)
)
Aber grundsätzlich würde ich die Daten überhaupt nicht gesammelt speichern, sondern sofort verarbeiten.
Hier mal grob als Generator umgesetzt:
Code: Alles auswählen
def get_chunks(stream, chunk_size, num_chunks):
for _ in range(num_chunks):
yield np.asarray(stream.read(chunk_size))
def filter_chunks(stream):
for chunk in get_chunks(...):
if is_interesting(chunk):
yield chunk
Oder gleich mit dem passenden Python-Builtin:
Code: Alles auswählen
chunks = filter(is_interesting, get_chunks(stream, chunk_size, num_chunks))
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 29. November 2017, 11:35
von yakari9
Hallo zusammen
das funktioniert übrigens sehr gut!
Jetzt steht die nächste Herausforderung an. Ich möchte ein zweites Mikrofon hinzufügen. Da meine USB-Soundkarte nur einen Mono-Eingang hat, habe ich mir eine zweite gekauft... nun ist die Frage ob es eine Möglichkeit gibt, paralell von 2 Soundkarten aufzunehmen...?
Ich habe mal einen Hinweis auf pyjack gefunden... das Ding ist aber nicht Dokumentiert und so für mich nicht einsetzbar...
Jemand eine Idee...
Irgendwie sowas wie
Code: Alles auswählen
audio = pyaudio.PyAudio()
stream = [audio.open(input_device_index=1),audio.open(input_device_index=2)]
data =np.hstack(stream.read(CHUNK) for in range (num_CHUNK)
geht nicht, oder?
Lieber Gruss
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 29. November 2017, 12:31
von __deets__
Fuer mehrere Devices wirst du ueber callbacks gehen muessen, denn jedes von denen hat seine eigene Zeitbasis & ggf. andere Aspekte wie zB Buffergroesse etc.
Eine Alternative waere ein Audio-Device das mehrere Kanaele unterstuetzt, die bekommst du dann in einem Rutsch, und bist auch synchron.
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 29. November 2017, 16:04
von yakari9
MIt callbacks?
Dann kriege ich sie aber nich parallel zum laufen, oder? also, dass ich dann verschiedene Tonspuren, welche gleichzeitig aufgenommen wurden, vergleichen kann... ?
Gruss
Re: Tonaufnahme einlesen und Komprimieren
Verfasst: Mittwoch 29. November 2017, 16:43
von __deets__
Doch, eben genau das bekommst du dann. Du oeffnest zwei Geraete, und bekommst fuer jedes einen Rueckruf wenn es einen Audio-Buffer fuer dich hat. Den kannst du zB in eine Queue pappen, und in deinem Haupthread Buffer beider Devices verarbeiten.