覆盖pyqtgraph中的线图和散点图,但只有线图才能更新

发布于 2025-02-12 04:06:34 字数 5562 浏览 1 评论 0原文

我正在根据此视频中的脚本进行音频频率分析仪。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文