PyAudio回调仅被调用一次

时间:2018-09-20 00:34:43

标签: python python-3.x pyaudio

我正在尝试创建一个简单的应用程序,该应用程序可加载wav文件(每个键盘音符一个),并在按下(或弹奏)MIDI音符时播放特定文件。到目前为止,我已经在两个单独的线程中使用mido创建了midi输入流,并使用pyaudio创建了音频流。目标是让midi流更新当前正在播放的音符,以及pyaudio流的回调以检查活动的音符并播放那些音符。 MIDI流可以正常工作,但是我的音频流似乎只在脚本启动时调用一次回调(print(notes))。知道如何让音频流回调不断更新吗?

import wave
from io import BytesIO
import os
from mido import MidiFile
import pyaudio
from time import sleep
from threading import Thread
import numpy

# Pipe: active, released
# Rank: many pipes
# Stop: one or more ranks
# Manual: multiple ranks
# Organ: multiple manuals

pipes = []
notes = []
p = pyaudio.PyAudio()


def mapRange(num, inMin, inMax, outMin, outMax):
    return int((num - inMin) * (outMax - outMin) / (inMax - inMin) + outMin)

def callback(in_data, frame_count, time_info, status):
    data = bytes(frame_count)
    print(notes)
    for note in notes:
        pipedata = bytes()
        if len(data) != 0:
            data1 = numpy.fromstring(data, numpy.int16)
            data2 = numpy.fromstring(note['sample'].readframes(frame_count), numpy.int16)
            pipedata = (data1 * 0.5 + data2 * 0.5).astype(numpy.int16)
        else:
            data2 = numpy.fromstring(note['sample'].readframes(frame_count), numpy.int16)
            pipedata = data2.astype(numpy.int16)
        data = pipedata.tostring()
    return (data, pyaudio.paContinue)

stream = p.open(format=pyaudio.paInt24,
                channels=2,
                rate=48000,
                output=True,
                stream_callback=callback,
                start=True)

# start the stream (4)
stream.start_stream()

for root, dirs, files in os.walk("samples"):
    for filename in files:
        file_on_disk = open(os.path.join(root, filename), 'rb')
        pipes.append(
            {"sample": wave.open(BytesIO(file_on_disk.read()), 'rb')})
for msg in MidiFile('test.mid').play():
    if msg.type == "note_on":
        notes.append(pipes[mapRange(msg.note, 36, 96, 0, 56)])
        print("on")
    if msg.type == "note_off":
        #notes[mapRange(msg.note, 36, 96, 0, 56)] = False
        print("off")

# wait for stream to finish (5)
while stream.is_active():
    sleep(0.1)

# stop stream (6)
stream.stop_stream()
stream.close()

# close PyAudio (7)
p.terminate()

1 个答案:

答案 0 :(得分:0)

我也面临这个问题,发现了这个问题,希望找到答案,最后自己弄清楚。

在回调中返回的数据必须与帧数匹配(p.open中的frames_per_buffer参数)。我看到您没有指定一个,所以我认为默认值为1024。

问题是frames_per_buffer并不代表字节,而是代表帧。因此,由于您将格式指定为pyaudio.paInt24,这意味着一帧由3个字节(24/8)表示。因此,在您的回调中,您应该返回3072个字节,否则由于某种原因将不会再次调用该回调。

如果使用阻塞模式而不在stream.write()中写入这3072个字节,则会导致音频缓慢和crack啪声的怪异效果。