You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于mido与PySimpleGUI的MIDI显示界面延迟优化求助

优化MIDI与PySimpleGUI界面的低延迟交互

首先,你的核心问题主要来自两个方面:GUI事件循环与MIDI消息处理的耦合,以及Canvas元素未清理导致的渲染性能下降。另外,用pynput做中间触发完全是多余的,反而会增加延迟和不可靠性。下面是具体的优化思路和改进代码:


1. 分离MIDI消息处理与GUI事件循环

将MIDI消息的监听放到独立线程中,避免阻塞GUI的事件循环。这样MIDI消息能实时被捕获,不会因为GUI的渲染逻辑拖慢消息处理速度。

2. 复用Canvas元素,避免重复创建

每次演奏音符时都创建新的rect和text元素,会导致Canvas中积累大量无用元素,随着演奏次数增加,渲染效率会急剧下降。应该只创建一次元素,然后修改它们的属性。

3. 直接传递MIDI消息到GUI,去掉pynput中间层

通过线程间通信(比如queue)将MIDI消息直接发送到GUI事件循环,不需要借助键盘模拟,减少延迟环节。


改进后的完整代码

import PySimpleGUI as sg
import mido
import threading
import queue

# 用于线程间传递MIDI消息的队列
midi_queue = queue.Queue()

def midi_listener(port_name):
    """独立线程监听MIDI输入"""
    try:
        with mido.open_input(name=port_name) as port:
            for msg in port:
                if msg.type == 'note_on' and msg.velocity > 0:  # 只处理有效的note_on(排除静音触发)
                    midi_queue.put(msg.note)
                elif msg.type == 'note_off':
                    midi_queue.put(0)  # 发送0表示音符关闭
    except Exception as e:
        print(f"MIDI监听线程错误: {e}")

def main():
    width = 1300
    height = 600

    # 布局:只创建一次Canvas和文本元素
    layout = [
        [sg.Canvas(size=(width, height), background_color='black', key='canvas')]
    ]

    window = sg.Window('MIDI Testing', layout, size=(width, height), finalize=True)
    canvas = window['canvas']

    # 初始化Canvas元素:背景矩形和显示文本
    bg_rect = canvas.TKCanvas.create_rectangle(0, 0, width, height, fill='black')
    note_text = canvas.TKCanvas.create_text(width/2, height/2, text="", fill="White", font=('Times', '24', 'bold'))

    # 启动MIDI监听线程
    midi_thread = threading.Thread(target=midi_listener, args=('IAC Driver Mido Test',), daemon=True)
    midi_thread.start()

    while True:
        # 非阻塞读取GUI事件和队列消息,timeout设为0保证循环快速响应
        event, values = window.read(timeout=0)

        # 处理关闭事件
        if event in (sg.WIN_CLOSED, 'Quit'):
            break

        # 处理队列中的MIDI消息
        while not midi_queue.empty():
            note = midi_queue.get_nowait()
            if note != 0:
                # 更新文本内容,而不是创建新元素
                canvas.TKCanvas.itemconfig(note_text, text=str(note))
            else:
                # 关闭音符时清空文本
                canvas.TKCanvas.itemconfig(note_text, text="")

    window.close()

if __name__ == '__main__':
    main()

关键优化点解释

  • 线程分离:MIDI监听在独立线程运行,不会被GUI的渲染逻辑阻塞,保证消息实时性。
  • 元素复用:只创建一次背景矩形和文本元素,每次更新只修改元素的text属性,避免Canvas元素堆积。
  • 非阻塞事件循环:window.read(timeout=0)让GUI循环不会一直等待事件,而是定期检查MIDI队列,提升响应速度。
  • 去掉pynput依赖:直接通过队列传递MIDI消息,消除了键盘模拟带来的额外延迟和不可靠性。

额外建议

  • 如果仍然存在轻微延迟,可以尝试降低window.read()的timeout值(比如设为1ms,即timeout=1),但不要设为0以外的过小值,避免CPU占用过高。
  • 确保你的MIDI驱动设置为低延迟模式(比如在系统音频设置中调整MIDI缓冲区大小),硬件层面的优化也能显著提升响应速度。

内容的提问来源于stack exchange,提问作者jwol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:07:41