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

时间:2016-03-24 16:38:08

标签: python callback pyaudio

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

  1. 记录131072个样本(48kHz时应为2.73秒),使用audio_in()然后将缓冲区推送到ain_queue。如果输入是2声道(立体声),则将其加到单声道
  2. 在主线程中执行self.ain_queue.get(),然后处理数据
  3. 将输出数据推送到aout_queue
  4. 在audio_out中从aout_queue获取数据,将其播放给扬声器。如果输出是立体声,那么两个扬声器将播放相同的数据
  5. 虽然输出流工作正常,但输入流似乎与数据完全不同步。一张声卡(USB)上的缓冲区录制时间为3.6秒至5.8秒,PCI声卡的录制时间约为13秒。更令人困惑的是,简单的in->输出线如本例中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
    

    通过以下方式验证代码:

     import chrono.py
     tst = chrono.chrono()
     tst.go(10)
    

    我正在研究gentoo Linux,python 2.7和pyaudio 2.9。 我在这里错过了什么? 在此先感谢Michael Widlok

0 个答案:

没有答案