首页 > 解决方案 > 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()

标签: pythonpython-3.xpyaudio

解决方案


我也遇到了这个问题,并发现了这个问题,希望能找到答案,最终自己弄清楚了。

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

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

如果您使用阻塞模式而不是在 stream.write() 中写入这 3072 个字节,则会导致缓慢且噼啪作响的音频的奇怪效果。


推荐阅读