使用 PyAudio 和 TK 在按键保持上播放音符

playing note on key hold using PyAudio and TK

我想使用 python 演奏一个关于按键保持的音符。我目前正在实施和修改找到的代码 here。但是,我遇到的问题是按住按键时会重复调用keydown函数,导致每次调用keydown函数时都会出现音频切入切出的问题。

我正在寻找一种方法来保持演奏和弦的能力,但是通过在对所述键调用 keyup 之前不在同一键上调用 keydown 来消除这种进出切割。我一直在处理的代码的具体部分如下所示。

提前致谢。

p = pyaudio.PyAudio()
chord = Chord()
gen = NoteGen()

def callback(in_data, frame_count, time_info, status):
    wave = chord(frame_count)
    data = wave.astype(numpy.float32).tostring()
    return (data, pyaudio.paContinue)

stream = p.open(
    format=pyaudio.paFloat32,
    channels=1,
    rate=44100,
    output=True,
    stream_callback=callback
)

stream.start_stream()


def keydown(event):
    k = gen(event.char)
    print("add note: " + event.char)
    chord.add_note(k)
    print [(n.name, n.frequency) for n in chord.notes]


def keyup(event):
    k = gen(event.char)
    print("remove note: " + event.char)
    chord.remove_note(k)
    print [n.name for n in chord.notes]


root = Tk()
frame = Frame(root, width=100, height=100)
frame.bind_all("<KeyPress>", keydown)
frame.bind_all("<KeyRelease>", keyup)
frame.pack()
root.mainloop()

stream.stop_stream()
stream.close()
p.terminate()

您可以使用一些全局变量来检查是否已经触发了按键事件。

key_pressed = False

def keydown(event):
    if key_pressed:
        return

    key_pressed = True
    k = gen(event.char)
    print("add note: " + event.char)
    chord.add_note(k)
    print [(n.name, n.frequency) for n in chord.notes]


def keyup(event):
    key_pressed = False
    k = gen(event.char)
    print("remove note: " + event.char)
    chord.remove_note(k)
    print [n.name for n in chord.notes]

有一个字典,其中将包含一个键代码作为键和该键的状态(例如布尔值)作为值。

因此,当调用 key down 时,检查 key code 是否在字典中,如果不在,则将其添加值 1。然后开始播放音符。 如果存在,检查该键的状态,如果按下则忽略该事件。否则去玩音符。 在按键上将按键状态设置为 0。

还有,不要使用回调来播放音频,打断这样播放的音符会让你做噩梦。

最好在线程中将注释写入流。

分块写入数据并检查停止标志。

调用 key up 时设置标志并执行:

stream.stop_stream(); stream.start_stream()

准确切断声音。

将写入部分放入 try-except 块中,这样当您从线程外部强制停止流时,不会发生任何不良情况。

如果你希望有更好的方法来使用 pyaudio 来做这些事情,这将允许你一次播放多个声音,请在 PyPI 中搜索 SWmixer 模块。你还需要 numpy。然后使用 SWMixer 将每个声音加载到它自己的对象中,并根据需要启动和停止它。它的用法与pygame.mixer模块非常相似。

顺便说一句,如果您尝试并行使用多个 PyAudio 流,我不确定这将如何工作。

我的意思是,我知道您可以并行使用一个输出流和一个输入流而不会遇到太多麻烦,但是超过 2 个输出流可能会造成一些问题。这就是我提到 SWMixer 的原因。我从未尝试过使用多个,所以我可能是错的。

但我确实尝试过使用 PyAudio 本身的多个实例。它通过减少先前调用的音量来工作。这样我的流也发生了。所以你会听到最后一个音符比之前开始的音符更响亮,或者你可能会听到有趣的声音。