Ich versuche, ein Python-Skript zu schreiben, um als Violin-Tuner/Echtzeit-Spektralanzeige zu fungieren. Bisher habe ich pyaudio aufgenommen, um Datenblöcke vom Mikrofon aufzunehmen und das Frequenzspektrum für kurze Zeitreihen von Audio zu berechnen. Ich möchte diese in Echtzeit mit Matplotlib plotten, aber mein Figurenfenster ist leer, während die Daten aufgezeichnet wurden und nur das letzte Plot auf dem Bildschirm aktualisiert wird, nachdem das Skript beendet wurde. Was mache ich falsch?Violin-Tuner mit Python und Matplotlib
# -*- coding: utf-8 -*-
"""
Created on Mon May 1 00:03:55 2017
@author: Hugo.
"""
import pyaudio
import struct
import numpy as np
import matplotlib.pyplot as plt
from time import sleep
CHUNK = 2**14 #2**15 #4096
WIDTH = 2
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
dt = 1.0/RATE
### frequencies of the strings for the violin (tunned in A), in Hz
f4 = 195.998 ## G3
f3 = 293.665 ## D4
f2 = 440.000 ## A4
f1 = 659.255 ## E5
n = CHUNK
freqs = np.fft.rfftfreq(n, d = dt)
def Frequency_of_position(position):
""" Returns the frequency (Hz) of the note in from its position (halftones)
relative to A4 in an equal tempered scale. Ex: 0 -> 440 Hz (A4),
12 -> 880 Hz (A5)."""
return 440.0*(2**(1.0/12.0))**position
def Position_to_note(position):
"A A# B C C# D D# E F F# G G#"
SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
LETTER = SCALE[position % 12]
NUMBER = str(int((position+48)/12))
return LETTER+NUMBER
pos = np.array(range(-36,48))
vnote_freqs = np.vectorize(Frequency_of_position)
note_freqs = vnote_freqs(pos)
def get_frequency(spectrum):
return freqs[np.argmax(spectrum)]
class Freq_analysis(object):
def __init__(self):
self.pa = pyaudio.PyAudio()
self.stream = self.open_mic_stream()
self.plots = self.prepare_figure()
#self.fig_and_axes = self.prepare_figure()
#self.first_plot = self.plot_first_figure()
def stop(self):
self.stream.close()
def open_mic_stream(self):
device_index = self.find_input_device()
stream = self.pa.open( format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
input_device_index = device_index,
frames_per_buffer = CHUNK)
return stream
def find_input_device(self):
device_index = None
for i in range(self.pa.get_device_count()):
devinfo = self.pa.get_device_info_by_index(i)
print("Device %d: %s"%(i,devinfo["name"]))
for keyword in ["mic","input"]:
if keyword in devinfo["name"].lower():
print("Found an input: device %d - %s"% (i,devinfo["name"]))
device_index = i
return device_index
if device_index == None:
print("No preferred input found; using default input device.")
return device_index
def prepare_figure(self):
fig1 = plt.figure(1, figsize = (16,6))
wide_plot = plt.subplot(2,1,1)
plt.vlines([f1,f2,f3,f4],1,1e17, linestyles = 'dashed')
plt.xlabel("freq (Hz)")
plt.ylabel("S^2 (u. arb.)")
plt.xscale('log')
plt.yscale('log')
plt.xlim([80,4000])
#plt.xlim([600,700])
#plt.xlim([400,500])
plt.ylim([1e0,1e17])
spec_w, = plt.plot([1,1],[1,1], '-',c = 'blue')
f4_plot = plt.subplot(2,4,5)
plt.vlines(f4,1,1e17, linestyles = 'dashed')
plt.xlabel("freq (Hz)")
plt.ylabel("S^2 (u. arb.)")
plt.yscale('log')
plt.xlim([140,260])
plt.ylim([1e0,1e17])
spec_f4, = plt.plot([1,1],[1,1], '-',c = 'blue')
f3_plot = plt.subplot(2,4,6)
plt.vlines(f3,1,1e17, linestyles = 'dashed')
plt.xlabel("freq (Hz)")
plt.yscale('log')
plt.xlim([220,380])
plt.ylim([1e0,1e17])
spec_f3, = plt.plot([1,1],[1,1], '-',c = 'blue')
f2_plot = plt.subplot(2,4,7)
plt.vlines(f2,1,1e17, linestyles = 'dashed')
plt.xlabel("freq (Hz)")
plt.yscale('log')
plt.xlim([400,500])
plt.ylim([1e0,1e17])
spec_f2, = plt.plot([1,1],[1,1], '-',c = 'blue')
f1_plot = plt.subplot(2,4,8)
plt.vlines(f1,1,1e17, linestyles = 'dashed')
plt.xlabel("freq (Hz)")
plt.yscale('log')
plt.xlim([600,700])
plt.ylim([1e0,1e17])
spec_f1, = plt.plot([1,1],[1,1], '-',c = 'blue')
plt.show()
#return fig1, wide_plot, f1_plot, f2_plot, f3_plot, f4_plot
return spec_w, spec_f1, spec_f2, spec_f3, spec_f4
def PrintFreq(self, S2):
dominant = get_frequency(S2)
dist = np.abs(note_freqs-dominant)
closest_pos = pos[np.argmin(dist)]
closest_note = Position_to_note(closest_pos)
print(dominant, "(",closest_note, "=",Frequency_of_position(closest_pos),")")
def listen(self):
try:
block = self.stream.read(CHUNK)
except IOError:
# An error occurred.
print("Error recording.")
return
indata = np.array(struct.unpack("%dh"%(len(block)/2),block))
n = indata.size
freqs = np.fft.rfftfreq(n, d = dt)
data_rfft = np.fft.rfft(indata)
S2 = np.abs(data_rfft)**2
#self.PrintFreq(block)
#self.update_fig(block)
self.PrintFreq(S2)
self.update_fig(freqs, S2)
def update_fig(self, freqs, S2):
self.plots[0].set_xdata(freqs)
self.plots[1].set_xdata(freqs)
self.plots[2].set_xdata(freqs)
self.plots[3].set_xdata(freqs)
self.plots[4].set_xdata(freqs)
self.plots[0].set_ydata(S2)
self.plots[1].set_ydata(S2)
self.plots[2].set_ydata(S2)
self.plots[3].set_ydata(S2)
self.plots[4].set_ydata(S2)
#plt.draw()
#plt.show()
if __name__ == "__main__":
Tuner = Freq_analysis()
for i in range(1000):
Tuner.listen()
plt.show()
Für ein einfaches Beispiel mit 'matplotlib.animation' meine Beispielanwendung [plot_input.py] (https://github.com/spatialaudio/python- Sounddevice/Blob/Master/Beispiele/Plot_input.py). Es wandelt das Signal nicht in den Frequenzbereich um, aber ich denke, das könnte leicht hinzugefügt werden. – Matthias