python - 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()
解决方案
我也遇到了这个问题,并发现了这个问题,希望能找到答案,最终自己弄清楚了。
回调返回的数据必须与帧数匹配(p.open 中的 frames_per_buffer 参数)。我看到您没有指定一个,所以我认为默认值为 1024。
问题是 frames_per_buffer 不代表字节,而是代表帧。因此,由于您将格式指定为 pyaudio.paInt24,这意味着一帧由 3 个字节(24 / 8)表示。因此,在您的回调中,您应该返回 3072 字节,否则由于某种原因不会再次调用回调。
如果您使用阻塞模式而不是在 stream.write() 中写入这 3072 个字节,则会导致缓慢且噼啪作响的音频的奇怪效果。
推荐阅读
- r - 插入符号训练预处理中的相关截止
- mysql - 为使用 SELECT 选择的每个 ID 在 sql 中运行 INSERT
- python - python3.6m等python版本后面的“m”是什么
- c# - 是否可以为 UWP 更改 Telerik RadRating 上的星号?
- docker - TypeError:描述不是函数。在 Gitlab CI 上运行 Selenium 测试时
- html - Spring - 电子邮件正文中具有多个链接的 java 邮件
- tosca - 如何在 tosca 自动化工具中选择文件上传
- php - 如何使用 php 在一个软件上运行命令
- javascript - JavaScript 在数组中查找单个元素
- c# - 如何在完整多边形的两点之间的某处添加一个点