PyAudio输入输出“有线”与单独的流

2024-05-13 23:19:26 发布

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

我正在尝试使用python和pyaudio进行一些实时音频处理。问题是我不能让输入流正常工作(回调版本)。其目的是:

  1. 记录131072个样本(48kHz时应为2.73s),使用audio_in()将缓冲区推送到ain_队列。如果输入为2声道(立体声),则将其与单声道相加
  2. 在主线程do中self.ain_队列.get(),然后处理数据
  3. 将输出数据推送到aout_队列
  4. 在audio-out-get-data-from-aout-queue,播放给扬声器。如果输出为立体声,则两个扬声器将播放相同的数据

虽然输出流正常工作,但输入流似乎与数据完全不同步。在一个声卡(USB)上,一个缓冲区的录制时间从3.6秒到5.8秒不等,PCI声卡的录制时间甚至约为13秒。更令人困惑的是,简单的in->;outwire(如本例所示)https://gist.github.com/fwaechter/8795472工作正常。报告的两个流的cpu负载都非常小~0.002。在

以下是代码(剥离):

    import Queue, scipy, numpy, pyaudio

class chrono:

def __init__(self, indev=4, outdev=6, clockt=200, samplerate=48000, buflen=131072, lowpass=10000, highpass=2000):
    self.samplerate = samplerate
    self.buffer_len = buflen
    self.frames_per_buf = 4096
    self.err_cnt1 = 0
    self.err_cnt2 = 0
    self.ain_queue = Queue(maxsize=1000)
    self.aout_queue = Queue(maxsize=1000)
    self.indata = {"dat":scipy.zeros(self.buffer_len, dtype="int16"), "cur_frame": 0, "start_tm": 0.0}
    self.outdata = {"dat":scipy.zeros(self.buffer_len, dtype="int16"), "cur_frame": self.buffer_len, "out_buf2":scipy.zeros(self.frames_per_buf*2, dtype="int16")}

    self.pya = pyaudio.PyAudio()
    printd("chrono.__init__:        Audio devices:")
    for i in range(self.pya.get_device_count()):
        cdev = self.pya.get_device_info_by_index(i)
        printd("chrono.__init__\tDev: %d: Name %s, Input channels %d, Output channels %d, Sample rate %d"%(i,cdev["name"],\
                cdev["maxInputChannels"],cdev["maxOutputChannels"],cdev["defaultSampleRate"]))
    self.auin_channels = self.pya.get_device_info_by_index(indev)["maxInputChannels"]
    self.auout_channels = self.pya.get_device_info_by_index(outdev)["maxOutputChannels"]
    self.pya.is_format_supported(rate=self.samplerate, input_device=indev, input_channels=self.auin_channels, input_format=pyaudio.paInt16)
    self.pya.is_format_supported(rate=self.samplerate, output_device=outdev, output_channels=self.auout_channels, output_format=pyaudio.paInt16)
    self.auin = pyaudio.Stream(PA_manager=self.pya, rate=self.samplerate, channels=self.auin_channels, format=pyaudio.paInt16, input=True, \
            input_device_index=indev, frames_per_buffer=self.frames_per_buf, stream_callback=self.audio_in, start=False)
    self.auout = pyaudio.Stream(PA_manager=self.pya, rate=self.samplerate, channels=self.auout_channels, format=pyaudio.paInt16, output=True, \
            output_device_index=outdev, frames_per_buffer=self.frames_per_buf, stream_callback=self.audio_out, start=False)
    return

def close(self):
    self.auin.stop_stream()
    self.auout.stop_stream()
    self.auin.close()
    self.auout.close()
    self.pya.terminate()
    return

def __del__(self):
    return self.close()


def audio_in(self, data, frame_count, time_info, status):
    self.err_cnt1 += 1
    if (self.auin_channels == 2):
        data = numpy.fromstring(data, dtype="int16")
        self.indata["dat"][self.indata["cur_frame"]:self.indata["cur_frame"]+frame_count] = \
                (data[0::2].astype("int32") + data[1::2]) / 2                           # get mean of both channels
    else:
        self.indata["dat"][self.indata["cur_frame"]:self.indata["cur_frame"]+frame_count] = numpy.fromstring(data, dtype="int16")
    if (self.indata["cur_frame"] == 0):                                         # keep track of time - this is start of buffer
        self.indata["start_tm"] = time_info["input_buffer_adc_time"]
    self.indata["cur_frame"] += frame_count                                         # keep track of data position in buffer
    if (self.indata["cur_frame"] >= self.buffer_len):
        self.indata["cur_frame"] = 0
        self.ain_queue.put((self.indata["dat"].astype("float"), self.indata["start_tm"], time_info["current_time"]))    # put new full buffer into queue
    return(None, pyaudio.paContinue)

def audio_out(self, data, frame_count, time_info, status):
    self.err_cnt2 += 1
    if (self.outdata["cur_frame"] >= self.buffer_len):
        try:
            self.outdata["dat"] = self.aout_queue.get_nowait().astype("int16")
        except:
            return("\0"*frame_count*2*self.auout_channels, pyaudio.paContinue)
        self.outdata["cur_frame"] = 0
        self.aout_queue.task_done()
    if (self.auout_channels == 2):
        samples = self.outdata["dat"][self.outdata["cur_frame"]:self.outdata["cur_frame"]+frame_count]
        self.outdata["out_buf2"][0::2] = samples
        self.outdata["out_buf2"][1::2] = samples
        data = self.outdata["out_buf2"].tostring()
    else:
        data = (self.outdata["dat"][self.outdata["cur_frame"]:self.outdata["cur_frame"]+frame_count]).tostring()
    self.outdata["cur_frame"] += frame_count
    return(data, pyaudio.paContinue)


def go(self, cnt=5):
    self.auin.start_stream()
    self.auout.start_stream()
    while(cnt>0):
        dat = self.ain_queue.get(timeout=15)
        print("rec time %f"%(dat[2]-dat[1]))
        self.aout_queue.put(dat[0])
        cnt -= 1
        print(cnt)
        self.ain_queue.task_done()
    self.auin.stop_stream()
    self.auout.stop_stream()
    return dat

测试代码的方法是:

^{pr2}$

我正在研究gentoolinux、python2.7和pyaudio2.9。 我错过了什么? 先谢谢你,迈克尔·韦德洛克


Tags: selfdatagetqueuebuffercountframedat