暂时记录。
这里的简单原理就是获取声卡输入输出设备中的数据(注意驱动什么的没有问题,能用麦克风),然后 matplotlib 绘制出来,想要看到音乐的节奏振动就再 fft 一下。至于如何不断更新波形,matplotlib 有一个 animation 方法可以用(见下面第二种方法),但实际上我用了之后发现显示效果不如第一种(可能是姿势不对)。之前用 matlab 做的,也很不错。
第一种方法(波形显示更流畅,代码参考这个视频):
from _tkinter import TclError import pyaudio import numpy as np import matplotlib.pyplot as plt import struct channels = 1 rate = 44100 chunk = 1024 * 2 p = pyaudio.PyAudio() stream = p.open( format=pyaudio.paInt16, channels=channels, rate=rate, input=True, output=True, frames_per_buffer=chunk ) stream.start_stream() xf = np.linspace(0, rate, chunk) fig, ax = plt.subplots() lf, = ax.semilogx(xf, np.zeros(chunk), '-', lw=1) ax.set_xlim(20, rate / 2) ax.set_ylim(0, 1) plt.show(block=False) while stream.is_active(): data = stream.read(chunk) data_int = struct.unpack(str(chunk * 2) + 'B', data) yf = np.fft.fft(data_int) lf.set_ydata(np.abs(yf[:chunk]) * 2 / (256 * chunk)) try: ax.figure.canvas.draw() ax.figure.canvas.flush_events() except TclError: stream.stop_stream() stream.close() break
第二种方法:
import pyaudio import numpy as np # from scipy.fftpack import fft import matplotlib.pyplot as plt import struct from matplotlib.animation import FuncAnimation channels = 1 rate = 44100 chunk = 1024 * 2 p = pyaudio.PyAudio() stream = p.open( format=pyaudio.paInt16, channels=channels, rate=rate, input=True, output=True, ) stream.start_stream() x = np.arange(0, 2*chunk, 2) xf = np.linspace(0, rate, chunk) fig, (ax1, ax2) = plt.subplots(2) l, = ax1.plot(x, np.zeros(chunk), '-', lw=1) lf, = ax2.semilogx(xf, np.zeros(chunk), '-', lw=1) ax1.set_xlim(0, 2*chunk) ax1.set_ylim(0, 255) ax2.set_xlim(20, rate / 2) ax2.set_ylim(0, 1) plt.setp(ax1, xticks=[0, chunk, 2 * chunk], yticks=[0, 128, 255]) def gen(): while stream.is_active(): data = stream.read(chunk) data_int = struct.unpack(str(chunk*2) + 'B', data) data_np = np.array(data_int, dtype='b')[::2] + 128 yf = np.fft.fft(data_int) yield data_np, yf def init(): lf.set_ydata(np.random.rand(chunk)) return lf, def update(data): ax1.figure.canvas.draw() ax2.figure.canvas.draw() l.set_ydata(data[0]) lf.set_ydata(np.abs(data[1][:chunk]) * 2 / (256 * chunk)) return lf, animate = FuncAnimation(fig, update, gen, blit=True, interval=0.1, repeat=False, init_func=init) plt.show() stream.stop_stream() stream.close()