覆盖pyqtgraph中的线图和散点图,但只有线图才能更新
我正在根据此视频中的脚本进行音频频率分析仪。
https://www.youtube.com/watch?v=rhmtgaplu4 84S
基本上,它实时采用麦克风输入并绘制波形。然后,它执行波形的傅立叶变换,并将转换绘制在第二个图上。这正在不断更新。
我想在代码中添加一些峰值查找,然后突出显示检测到峰值的点。我试图通过在FFT图上覆盖散点图来做到这一点,但是只生成了第一个散点图,并且永远不会更新。我进行了一些故障排除,并且可以看到我的峰值坐标得到更新,但是当调用“ self.set_plotdata()”时,它只会更新线图而不是散布。任何Advidce都将不胜感激。
另外,我想为散点点添加数据标签以显示每个峰值的频率是多少,但是我找不到pyqtgraph中的任何文档来显示如何执行此操作。
这是代码。
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 27 16:35:59 2022
@author: corn-
"""
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import struct
import pyaudio
from scipy.fftpack import fft
from scipy.signal import find_peaks
import sys
import time
class AudioStream(object):
def __init__(self):
# pyqtgraph stuff
pg.setConfigOptions(antialias=True)
self.traces = dict()
self.app = QtGui.QApplication(sys.argv)
self.win = pg.GraphicsWindow(title='Spectrum Analyzer')
self.win.setWindowTitle('Spectrum Analyzer')
self.win.setGeometry(5, 115, 1910, 1070)
#X labels for time domain graph
wf_xlabels = [(0, '0'), (2048, '2048'), (4096, '4096')]
wf_xlabel2 = 'time'
wf_xaxis = pg.AxisItem(orientation='bottom')
wf_xaxis.setTicks([wf_xlabels])
wf_xaxis.setLabel(text = wf_xlabel2)
#Y labels for time domain graph
wf_ylabels = [(0, '0'), (127, '128'), (255, '255')]
wf_yaxis = pg.AxisItem(orientation='left')
wf_yaxis.setTicks([wf_ylabels])
#X labels for frequency domain graph
sp_xlabels = [
(np.log10(10), '10'), (np.log10(100), '100'),
(np.log10(1000), '1000'), (np.log10(22050), '22050')
]
sp_xlabel2 = 'log frequency'
sp_xaxis = pg.AxisItem(orientation='bottom')
sp_xaxis.setTicks([sp_xlabels])
sp_xaxis.setLabel(sp_xlabel2)
self.waveform = self.win.addPlot(
title='WAVEFORM', row=1, col=1, axisItems={'bottom': wf_xaxis, 'left': wf_yaxis},
)
self.spectrum = self.win.addPlot(
title='SPECTRUM', row=2, col=1, axisItems={'bottom': sp_xaxis},
)
# pyaudio stuff
self.FORMAT = pyaudio.paInt16
self.CHANNELS = 1
self.RATE = 44100
self.CHUNK = 1042 * 4
self.p = pyaudio.PyAudio()
self.stream = self.p.open(
format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
output=True,
frames_per_buffer=self.CHUNK,
)
# waveform and spectrum x points
self.x = np.arange(0, 2 * self.CHUNK, 2)
self.f = np.linspace(0, self.RATE / 2, self.CHUNK // 2)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def set_plotdata(self, name, data_x, data_y, peak_x, peak_y):
if name in self.traces:
self.traces[name].setData(data_x, data_y)
else:
if name == 'waveform':
self.traces[name] = self.waveform.plot(pen='c', width=3)
self.waveform.setYRange(0, 255, padding=0)
self.waveform.setXRange(0, 2 * self.CHUNK, padding=0.005)
if name == 'spectrum':
self.traces[name] = self.spectrum.plot(pen='m', width=50)
self.spectrum.setLogMode(x=True, y=True)
self.spectrum.setYRange(-4, 0, padding=0)
self.spectrum.setXRange(
np.log10(20), np.log10(self.RATE / 2), padding=0.005)
#plot the peak tips
self.peakTops = self.spectrum.plot()
self.peakTops.setData(peak_x, peak_y, pen=None, symbol='o')
#self.peakTops.setLogMode(x=True, y=True)
#self.PeakTops.setYRange(-4,0,padding=0)
#self.PeakTops.setXrange(np.log10(20), np.log10(self.RATE / 2), padding=0.005)
def get_peaks(self, name, data_x, data_y):
if name == 'spectrum':
peak_idx, _ = find_peaks(data_y, height=(0.1))
peak_height = data_y[peak_idx]
#print(peak_idx, peak_height)
return (peak_idx, peak_height)
def update(self):
wf_data = self.stream.read(self.CHUNK)
wf_data = struct.unpack(str(2 * self.CHUNK) + 'B', wf_data)
wf_data = np.array(wf_data, dtype='b')[::2] + 128
temp_x = 0 #placeholder to feed to set_plotdata
temp_y = 0 #placeholder to feed to set_plotdata
self.set_plotdata(name='waveform', data_x=self.x, data_y=wf_data, peak_x = temp_x, peak_y = temp_y)
sp_data = fft(np.array(wf_data, dtype='int8') - 128)
sp_data = np.abs(sp_data[0:int(self.CHUNK / 2)]
) * 2 / (128 * self.CHUNK)
[peak_idx, peak_height] = self.get_peaks(name = 'spectrum', data_x = self.f, data_y = sp_data)
self.set_plotdata(name='spectrum', data_x=self.f, data_y=sp_data, peak_x = peak_idx, peak_y = peak_height)
def animation(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.update)
timer.start(20)
self.start()
if __name__ == '__main__':
audio_app = AudioStream()
audio_app.animation()
I am making an audio frequency analyzer based on the script from this video.
https://www.youtube.com/watch?v=RHmTgapLu4s&t=84s
Basically, it takes the microphone input in real time and plots the waveform. Then, it performs a fourier transform of the waveform, and plots the transform on a second plot. This is updating constantly.
I wanted to add some peak finding to the code, and then highlight points where a peak is detected. I tried to do this by overlaying a scatter plot on the fft plot, but only the first scatter plot is generated and it never updates. I did some troubleshooting, and I can see my peak coordinates getting updated, but when "self.set_plotdata()" gets called, it only updates the line plot and not the scatter. Any advidce is appreciated.
Also, I would like to add data labels for the scatter points to show what frequency each peak is, but I can't find any documentation in PyQtGraph to show how to do this.
Here is the code.
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 27 16:35:59 2022
@author: corn-
"""
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import struct
import pyaudio
from scipy.fftpack import fft
from scipy.signal import find_peaks
import sys
import time
class AudioStream(object):
def __init__(self):
# pyqtgraph stuff
pg.setConfigOptions(antialias=True)
self.traces = dict()
self.app = QtGui.QApplication(sys.argv)
self.win = pg.GraphicsWindow(title='Spectrum Analyzer')
self.win.setWindowTitle('Spectrum Analyzer')
self.win.setGeometry(5, 115, 1910, 1070)
#X labels for time domain graph
wf_xlabels = [(0, '0'), (2048, '2048'), (4096, '4096')]
wf_xlabel2 = 'time'
wf_xaxis = pg.AxisItem(orientation='bottom')
wf_xaxis.setTicks([wf_xlabels])
wf_xaxis.setLabel(text = wf_xlabel2)
#Y labels for time domain graph
wf_ylabels = [(0, '0'), (127, '128'), (255, '255')]
wf_yaxis = pg.AxisItem(orientation='left')
wf_yaxis.setTicks([wf_ylabels])
#X labels for frequency domain graph
sp_xlabels = [
(np.log10(10), '10'), (np.log10(100), '100'),
(np.log10(1000), '1000'), (np.log10(22050), '22050')
]
sp_xlabel2 = 'log frequency'
sp_xaxis = pg.AxisItem(orientation='bottom')
sp_xaxis.setTicks([sp_xlabels])
sp_xaxis.setLabel(sp_xlabel2)
self.waveform = self.win.addPlot(
title='WAVEFORM', row=1, col=1, axisItems={'bottom': wf_xaxis, 'left': wf_yaxis},
)
self.spectrum = self.win.addPlot(
title='SPECTRUM', row=2, col=1, axisItems={'bottom': sp_xaxis},
)
# pyaudio stuff
self.FORMAT = pyaudio.paInt16
self.CHANNELS = 1
self.RATE = 44100
self.CHUNK = 1042 * 4
self.p = pyaudio.PyAudio()
self.stream = self.p.open(
format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
output=True,
frames_per_buffer=self.CHUNK,
)
# waveform and spectrum x points
self.x = np.arange(0, 2 * self.CHUNK, 2)
self.f = np.linspace(0, self.RATE / 2, self.CHUNK // 2)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def set_plotdata(self, name, data_x, data_y, peak_x, peak_y):
if name in self.traces:
self.traces[name].setData(data_x, data_y)
else:
if name == 'waveform':
self.traces[name] = self.waveform.plot(pen='c', width=3)
self.waveform.setYRange(0, 255, padding=0)
self.waveform.setXRange(0, 2 * self.CHUNK, padding=0.005)
if name == 'spectrum':
self.traces[name] = self.spectrum.plot(pen='m', width=50)
self.spectrum.setLogMode(x=True, y=True)
self.spectrum.setYRange(-4, 0, padding=0)
self.spectrum.setXRange(
np.log10(20), np.log10(self.RATE / 2), padding=0.005)
#plot the peak tips
self.peakTops = self.spectrum.plot()
self.peakTops.setData(peak_x, peak_y, pen=None, symbol='o')
#self.peakTops.setLogMode(x=True, y=True)
#self.PeakTops.setYRange(-4,0,padding=0)
#self.PeakTops.setXrange(np.log10(20), np.log10(self.RATE / 2), padding=0.005)
def get_peaks(self, name, data_x, data_y):
if name == 'spectrum':
peak_idx, _ = find_peaks(data_y, height=(0.1))
peak_height = data_y[peak_idx]
#print(peak_idx, peak_height)
return (peak_idx, peak_height)
def update(self):
wf_data = self.stream.read(self.CHUNK)
wf_data = struct.unpack(str(2 * self.CHUNK) + 'B', wf_data)
wf_data = np.array(wf_data, dtype='b')[::2] + 128
temp_x = 0 #placeholder to feed to set_plotdata
temp_y = 0 #placeholder to feed to set_plotdata
self.set_plotdata(name='waveform', data_x=self.x, data_y=wf_data, peak_x = temp_x, peak_y = temp_y)
sp_data = fft(np.array(wf_data, dtype='int8') - 128)
sp_data = np.abs(sp_data[0:int(self.CHUNK / 2)]
) * 2 / (128 * self.CHUNK)
[peak_idx, peak_height] = self.get_peaks(name = 'spectrum', data_x = self.f, data_y = sp_data)
self.set_plotdata(name='spectrum', data_x=self.f, data_y=sp_data, peak_x = peak_idx, peak_y = peak_height)
def animation(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.update)
timer.start(20)
self.start()
if __name__ == '__main__':
audio_app = AudioStream()
audio_app.animation()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论