Tkinter多进程与pyaudio音频输入

0 投票
1 回答
1162 浏览
提问于 2025-04-20 04:44

我正在写一个程序,这个程序会不断从麦克风获取输入,使用的是pyaudio。每隔5秒钟,它会进行一些计算,然后更新tkinter窗口中的matplotlib图表。

我已经做到了一些:

import threading
from array import array
from Queue import Queue, Full
import pyaudio
from matplotlib import pyplot as plt
import numpy as np

CHUNK_SIZE = 1000
MIN_VOLUME = 500
PLOT_CHUNK_SIZE = CHUNK_SIZE * 5
BUF_MAX_SIZE = CHUNK_SIZE * 10
PLOT_MAX_SIZE = PLOT_CHUNK_SIZE * 10

big_chunk = np.array( [])

def main():

    stopped = threading.Event()
    q = Queue(maxsize=int(round(BUF_MAX_SIZE / CHUNK_SIZE)))
    plot_queue = Queue(maxsize=int(round(PLOT_MAX_SIZE / PLOT_CHUNK_SIZE)))

    listen_t = threading.Thread(target=listen, args=(stopped, q))
    listen_t.start()
    record_t = threading.Thread(target=record, args=(stopped, q, plot_queue))
    record_t.start()
    plot_t = threading.Thread(target=plot_chunk, args=(stopped, plot_queue))
    plot_t.start()

    try:
        while True:
            listen_t.join(0.1)
            record_t.join(0.1)
            plot_t.join(0.1)
    except KeyboardInterrupt:
            stopped.set()

    listen_t.join()
    record_t.join()
    plot_t.join()


def record(stopped, q, plot_queue):
    global big_chunk, p, ready
    while True:
        if stopped.wait(timeout=0):
            break
        vol = max(q.get())
        if vol >= MIN_VOLUME:
            big_chunk = np.append(big_chunk, q.get())
            print 'new chunk'
            if len(big_chunk) > PLOT_CHUNK_SIZE-1:
                plot_queue.put(big_chunk)
                big_chunk = np.array( [])
        else:
            print "-",

def plot_chunk(stopped, plot_queue):
    while True:
        if stopped.wait(timeout=0):
            break
        features = plot_queue.get()
        my_features = do_calculations(features)
        plt.imshow(my_features)


def listen(stopped, q):
    stream = pyaudio.PyAudio().open(
        format=pyaudio.paInt16,
        channels=1,
        rate=44100,
        input=True,
        frames_per_buffer=1024,
    )

    while True:
        if stopped.wait(timeout=0):
            break
        try:
            q.put(array('h', stream.read(CHUNK_SIZE)))
        except Full:
            pass  # discard


if __name__ == '__main__':
    main()

我尝试用线程来实现这个功能,效果还不错,除了绘图部分。看起来matplotlib和tkinter在使用线程时不太兼容,所以我认为使用多进程可能是个更好的选择。于是我尝试了这个:

import Tkinter as Tk
import multiprocessing
from Queue import Empty, Full
import time
import pyaudio
import array

class GuiApp(object):
   def __init__(self,q):
      self.root = Tk.Tk()
      self.root.geometry('300x100')
      self.text_wid = Tk.Text(self.root,height=100,width=100)
      self.text_wid.pack(expand=1,fill=Tk.BOTH)
      self.root.after(100,self.CheckQueuePoll,q)

   def CheckQueuePoll(self,c_queue):
      try:
         str = c_queue.get(0)
         self.text_wid.insert('end',str)
      except Empty:
         pass
      finally:
         self.root.after(100, self.CheckQueuePoll, c_queue)

# Data Generator which will generate Data
def GenerateData(q):
   for i in range(10):
      print "Generating Some Data, Iteration %s" %(i)
      time.sleep(0.1)
      q.put("Some Data from iteration %s \n" %(i))

def listen( q):
    CHUNK_SIZE = 100
    stream = pyaudio.PyAudio().open(
        format=pyaudio.paInt16,
        channels=1,
        rate=44100,
        input=True,
        frames_per_buffer=1024,
    )

    while True:
        try:
            q.put(array('h', stream.read(CHUNK_SIZE)))
        except Full:
            pass  # discard

if __name__ == '__main__':
# Queue which will be used for storing Data

   q = multiprocessing.Queue()
   q2 = multiprocessing.Queue()
   q.cancel_join_thread() # or else thread that puts data will not term
   q2.cancel_join_thread() # or else thread that puts data will not term
   gui = GuiApp(q)
   t1 = multiprocessing.Process(target=GenerateData,args=(q,))
   t2 = multiprocessing.Process(target=listen,args=(q2,))
   t1.start()
   t2.start()
   gui.root.mainloop()

   t1.join()
   t2.join()

结果我遇到了以下错误:

q.put(array('h', stream.read(CHUNK_SIZE)))
TypeError: 'module' object is not callable

有没有什么想法?

1 个回答

1

问题在于你代码中的 array 是一个模块,而不是一个可以直接调用的东西(就像错误信息所说的那样)。

要么 你可以把导入的方式改成

from array import array

要么 你可以把代码改成

array.array(...)

撰写回答