减少 MIDI GUI 中的延迟
我正在尝试使用 mido 和 PySimpleGUI 创建一个简单的 MIDI 显示。我让它工作得很好,但我希望减少 MIDI 控制器(即 MIDI 键盘)和界面显示之间的延迟。特别是,一旦音符弹奏得相对较快,显示就会开始滞后,甚至在我放慢速度并继续以较慢的速度弹奏之后。只有当我关闭 GUI 并重新启动它时,延迟才会消失。我无法准确判断问题是否出在 mido、PySimpleGUI 或我的实现中的其他内容上,但由于实际发出的声音没有任何延迟,并且当我单独使用 mido 时似乎没有延迟(即只是将笔记打印到 Jupyter 笔记本上),我的钱花在 PySimpleGUI 上,或者我的低效代码是罪魁祸首。
为了这篇文章的目的,我尝试将我的实现简化为尽可能简单的术语,这只是一个脚本,它使在 MIDI 控制器上按下的音符触发在计算机键盘上按下的“c”键,使用 pynput (这是一个奇怪的解决方法,因为据我所知,您无法通过 MIDI 控制器直接触发 PySimpleGUI 事件),以及显示正在演奏的音符的音高值的基本 PySimpleGUI 界面。
下面是我在单独的笔记本中异步运行的 MIDI 脚本:
from pynput.keyboard import Key, Controller
def trigger():
keyboard = Controller()
key = "c"
try:
with mido.open_input(name='IAC Driver Mido Test') as port:
for message in port:
keyboard.press(key)
keyboard.release(key)
except KeyboardInterrupt:
pass
下面是用于读取 MIDI 数据的简化 PySimpleGUI 设置:
import PySimpleGUI as sg
with mido.open_input(name='IAC Driver Mido Test') as port:
# Window Dimensions
width = 1300
height = 600
# Arbitrary 'c' key linked to MIDI controller through pynput
callbacks = ['c']
canvas = [[sg.Canvas(size=(width, height), background_color='black', key= 'canvas')]]
# Show the Window to the user
window = sg.Window('MIDI Testing', canvas, size=(width, height), return_keyboard_events=True, use_default_focus=False)
# Event loop. Read buttons, make callbacks
while True:
canvas = window['canvas']
# Initialize note
note = 0
for msg in port.iter_pending():
note_type = msg.type
if note_type == 'note_on':
note = msg.note
# Read the Window
event, value = window.read()
# If a note is played
if event in callbacks:
if note!=0:
rect = canvas.TKCanvas.create_rectangle(0, 0, width, height)
canvas.TKCanvas.itemconfig(rect, fill="Black")
# Display the pitch value
canvas.TKCanvas.create_text(width/2, height/2, text=str(note), fill="White", font=('Times', '24', 'bold'))
# Close the window
if event in (sg.WIN_CLOSED, 'Quit'):
break
window.close()
我很难找到关于这个问题的很多信息,因为它非常小众,但我想所有的如果有更先进的音乐软件具有低延迟 MIDI 显示(即 Ableton、GarageBand),可能有更好的方法来完成我在这里想要完成的任务。任何指示或批评将不胜感激!
I'm trying to create a simple MIDI display using mido and PySimpleGUI. I have it working decently well, but am hoping to reduce latency between the MIDI controller (i.e. a MIDI keyboard) and the interface display. Particularly, the display will begin to lag once notes are played relatively fast, and then even after I slow down and continue to play at a slower rate. The latency will then only go away if I close out of the GUI and re-launch it. I can't tell exactly if the issue is with mido, PySimpleGUI, or something else in my implementation, but since there isn't any latency in the actual sound coming out, and it appears there's no delay when I use mido in isolation (i.e. just printing notes to a Jupyter notebook), my money is on PySimpleGUI or my inefficient code being the culprit.
For the sake of this post I've tried to reduce my implementation to the simplest terms possible, which is just a script that makes a note being pressed on the MIDI controller trigger a 'c' key being pressed on the computer keyboard using pynput (this is a weird workaround because as far as I can tell you cannot directly trigger a PySimpleGUI event through a MIDI controller), as well as a basic PySimpleGUI interface that displays the pitch value of the note being played.
Below is the MIDI script which I run asynchronously in a separate notebook:
from pynput.keyboard import Key, Controller
def trigger():
keyboard = Controller()
key = "c"
try:
with mido.open_input(name='IAC Driver Mido Test') as port:
for message in port:
keyboard.press(key)
keyboard.release(key)
except KeyboardInterrupt:
pass
And below is the simplified PySimpleGUI setup to read MIDI data:
import PySimpleGUI as sg
with mido.open_input(name='IAC Driver Mido Test') as port:
# Window Dimensions
width = 1300
height = 600
# Arbitrary 'c' key linked to MIDI controller through pynput
callbacks = ['c']
canvas = [[sg.Canvas(size=(width, height), background_color='black', key= 'canvas')]]
# Show the Window to the user
window = sg.Window('MIDI Testing', canvas, size=(width, height), return_keyboard_events=True, use_default_focus=False)
# Event loop. Read buttons, make callbacks
while True:
canvas = window['canvas']
# Initialize note
note = 0
for msg in port.iter_pending():
note_type = msg.type
if note_type == 'note_on':
note = msg.note
# Read the Window
event, value = window.read()
# If a note is played
if event in callbacks:
if note!=0:
rect = canvas.TKCanvas.create_rectangle(0, 0, width, height)
canvas.TKCanvas.itemconfig(rect, fill="Black")
# Display the pitch value
canvas.TKCanvas.create_text(width/2, height/2, text=str(note), fill="White", font=('Times', '24', 'bold'))
# Close the window
if event in (sg.WIN_CLOSED, 'Quit'):
break
window.close()
I've had trouble finding much info out there on this issue as it's pretty niche, but I imagine with all the much more advanced music software out there that have low latency MIDI displays (i.e. Ableton, GarageBand), there might be a better way to go about doing what I'm trying to accomplish here. Any pointers or critiques would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
对MIDO一无所知,但是这里可能
window.write_event_value
生成事件。port.iter_pending
。在这里,用于监视MIDO的输入的多线程和调用
write_event_value
将事件生成事件循环以更新GUI。不确定
port.iter_pending
是否会继续运行以监视MIDO的输入,因此添加了一段循环以保持其运行。睡眠
致电可能有助于减少CPU消耗,当然,延迟延迟10ms。遵循尚未执行的代码,也许未能因错过或错误而运行。
Know nothing about mido, but something maybe wrong here
window.write_event_value
to generate event.port.iter_pending
each time before you read event may got latency in your event loop.Here, multithread used to monitor the input from mido and call
write_event_value
to generate event to event loop to update GUI.Not sure if
port.iter_pending
will keep running to monitor the input of mido, so a while loop added to keep it running. Asleep
call there maybe help to reduce CPU consumption, of course, there will be a 10ms delay.Following code not yet executed, maybe failed to run for something missed or wrong.