如何使用pyaudio播放音频,同时在pyqtgraph中显示来自同一音频文件的输入?

2024-04-20 09:38:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用pyqtgraph建立一个实时频谱和波形显示。pyqtgraph的输入应该来自一个音频文件,该文件也应该同时通过pyaudio播放。每个部分单独工作,但我想把它们连接在一起

我知道我需要两个并行运行的进程,但我不知道如何实现这一点。从我看到的打印消息来看,play和animation函数是我希望在单独的进程中运行的两个函数

import tkinter as tk
from tkinter import filedialog, messagebox
from pydub import AudioSegment
from pydub.playback import play
import pyaudio
import struct
import numpy as np
from scipy.fftpack import fft
from pyqtgraph.Qt import *
import pyqtgraph as pg
import sys
import subprocess
import wave
import tempfile
import os
from threading import *
from multiprocessing import Process
from cv2 import plot
import asyncio

class Plot2D(object):

    def __init__(self):
        root = tk.Tk()
        root.withdraw()

        #audio konstanten
        self.CHUNK = 1024 * 2
        self.FORMAT = pyaudio.paInt16
        self.CHANNELS = 1
        self.RATE = 44100
        self.pause = False

        messagebox.showinfo("Info", "Wählen Sie eine Audiodatei")
        self.file = filedialog.askopenfilename()

        self.path = os.getcwd()
        subprocess.call(['sox', self.file, '-b', '16', '-c', '1', '-r', '44100', self.path + 'aud.wav'])

        self.wf = wave.open(self.path + 'aud.wav', 'rb')
        self.p = pyaudio.PyAudio()
        #self.stream = self.p.open(
        #    format = self.p.get_format_from_width(self.wf.getsampwidth()),
        #    channels = self.wf.getnchannels(),
        #    rate = self.wf.getframerate(),
        #    output = True
        #)
        #audiospur
        self.stream = self.p.open(
            format=self.FORMAT,
            channels=self.CHANNELS,
            rate=self.RATE,
            #input_device_index=chosen_device_index,
        #    input=True,
            output=True,
            frames_per_buffer=self.CHUNK
        )
        print("Audio init")

        self.traces = dict()
        self.phase = 0
        self.t = np.arange(0, 3.0, 0.01)
        pg.setConfigOptions(antialias=True)
        self.app = QtGui.QApplication(sys.argv)        
        self.win = pg.GraphicsWindow(title="Waveform & Spectrum")
        self.win.resize(1000, 600)
        self.win.setWindowTitle('Waveform & Spectrum')
        self.win.setGeometry(5, 115, 1910, 1070)

        self.waveform = self.win.addPlot(title='Waveform', row=1, col=1)
        self.spectrum = self.win.addPlot(title='Spectrum', row=2, col=1)

        #richtiges Aufnahmegerät festlegen
        #chosen_device_index = -1
        #for x in range(0,self.p.get_device_count()):
        #    info = self.p.get_device_info_by_index(x)
            #print self.p.get_device_info_by_index(x)
        #    if info["name"] == "pulse":
        #        chosen_device_index = info["index"]
        #        print( "Chosen index: ", chosen_device_index)



        #self.stream = self.p.open(
        #    channels = Audio().wf.getnchannels(),
        #    rate = self.RATE,
        #    output = True
        #)

        #plot variabeln
        self.x = np.arange(0, self.CHUNK)
        self.xf = np.linspace(0, self.RATE, self.CHUNK)
        print("Plot2D init")

    def start(self):
        if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()
        print("Plot2D start")

    def set_plotdata(self, name, data_x, data_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(-500, 500, padding=0)
                self.waveform.setXRange(20, self.CHUNK, padding=0.005)
            if name == 'spectrum':
                self.traces[name] = self.spectrum.plot(pen='m', width=3)
                self.spectrum.setLogMode(x=True, y=True)
                self.spectrum.setYRange(-4, 0, padding=0)
                self.spectrum.setXRange(4, np.log10(self.RATE), padding=0.005)
        print("Plot2D set_plotdata")

    def play(self):
        print("playing")
        data = self.wf.readframes(self.CHUNK)
        while data != '':
            self.stream.write(data)
            data = self.wf.readframes(self.CHUNK)
        print("Plot2D play")

    def update(self):
        print("updating")
        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
        self.set_plotdata(name='waveform', data_x=self.x, data_y=wf_data)

        sp_data = fft(np.array(wf_data, dtype='int8') - 128)
        sp_data = np.abs(sp_data[0:int(self.CHUNK)]) * 2 / (128 * self.CHUNK)
        self.set_plotdata(name='spectrum', data_x=self.xf, data_y=sp_data)
        print("Plot2D update")

    def animation(self):
        timer = QtCore.QTimer()
        timer.timeout.connect(self.update)
        timer.start(30)
        print("Plot2D animation")
        self.start()

    #def close(self):
    #    self.stream.close()
    #    self.p.terminate()
    #    print("Audio close")

if __name__=='__main__':
    Plot2D().animation()
    #p1 = Process(target=Plot2D().animation())
    #p1.start()

Tags: namefromimportselftruedataindexdevice
1条回答
网友
1楼 · 发布于 2024-04-20 09:38:13

您是否尝试过从动画逻辑的update-方法中调用play-方法?然后,当前区块应更新绘图,并在同一调用中写入音频流

我用matplotlib实现了一个类似的实时音频显示

可能是这样的:

def play(self, data):
        print("playing")
        self.stream.write(data)

def update(self):
        print("updating")
        wf_data = self.stream.read(self.CHUNK)

        # write to audio stream
        self.play(wf_data)

        # then update plot
        wf_data = struct.unpack(str(2 * self.CHUNK) + 'B', wf_data)
        wf_data = np.array(wf_data, dtype='b')[::2] + 128
        self.set_plotdata(name='waveform', data_x=self.x, data_y=wf_data)

        sp_data = fft(np.array(wf_data, dtype='int8') - 128)
        sp_data = np.abs(sp_data[0:int(self.CHUNK)]) * 2 / (128 * self.CHUNK)
        self.set_plotdata(name='spectrum', data_x=self.xf, data_y=sp_data)
        print("Plot2D update")

[希望这能有所帮助,这是我的第一个答案]

相关问题 更多 >