我有一个简单的 python 代码,它通过连接到 PC 的 USB 从音乐键盘获取 MIDI 信号,然后将命令发送到 Arduino 板以写入其数字输出。这工作得很好,没有任何问题。 我尝试将相同的代码迁移到 Raspberry Pi,并进行一些特定于 Pi 的修改。我的代码如下:
import pygame
import mido
import rtmidi
import time
import RPi.GPIO as GPIO
pygame.init()
BLACK = [0,0,0]
WHITE = [255, 222, 111]
note_list = []
note_list_off = []
outport=mido.open_output()
inport=mido.open_input()
GPIO.setmode(GPIO.BCM)
done = False
GPIO.setup(4,GPIO.OUT)
print("START!")
while done == False:
for msg in inport.iter_pending():
try:
n = msg.note
except:
continue
if msg.velocity>0 and msg.velocity != 64:
print(msg.velocity)
GPIO.output(4,True)
time.sleep(1)
GPIO.output(4,False)
else:
msg=mido.Message('note_off',note=n)
outport.send(msg)
pygame.quit ()
添加RPi.GPIO是与Windows上运行的代码唯一的区别。如果我将代码移至该行上方
GPIO.output(4,True)
time.sleep(1)
GPIO.output(4,False)
Raspberry Pi 会写入我通过连接 LED 进行测试的正确端口。这里的问题是
print("START!")
之后的行永远不会被执行。我检查了 Pi 是否检测到键盘,这些是输出:
for msg in inport.iter_pending():
lsusb
amidi -l
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 07cf:6803 Casio Computer Co., Ltd CTK-3500 (MIDI keyboard)
aconnect -i
Dir Device Name
IO hw:3,0,0 CASIO USB-MIDI MIDI 1
Pi 可以很好地从键盘读取 MIDI,因为这是
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 28: 'CASIO USB-MIDI' [type=kernel,card=3]
0 'CASIO USB-MIDI MIDI 1'
aseqdump -p 28 在键盘上弹奏时的输出 我的 Python 版本是 3.11。 任何帮助深表感谢。
Waiting for data. Press Ctrl+C to end.
Source Event Ch Data
28:0 Note on 0, note 64, velocity 9
28:0 Note on 0, note 60, velocity 27
28:0 Note on 0, note 57, velocity 1
28:0 Note off 0, note 57, velocity 64
28:0 Note on 0, note 60, velocity 30
28:0 Note on 0, note 57, velocity 23
28:0 Note on 0, note 55, velocity 31
28:0 Note off 0, note 55, velocity 64
28:0 Note off 0, note 60, velocity 64
28:0 Note off 0, note 57, velocity 64
28:0 Note on 0, note 55, velocity 29
28:0 Note off 0, note 55, velocity 64
28:0 Note on 0, note 57, velocity 35
My Python version is 3.11. Any help is much appreciated.
代码的问题在于
inport.iter_pending()
方法的阻塞性质。这种方法等待传入的 MIDI 消息,如果没有消息,它不会让代码的其余部分运行。这就是为什么你的代码在打印“开始!”之后似乎就停止了,因为它停滞在等待 MIDI 输入的循环中。
你可以通过使用非阻塞方法来读取 MIDI 输入,或者使用异步方法来处理 MIDI 输入,从而解决这个问题。下面介绍了这两种方法的示例:
1. 非阻塞方法:
你可以使用
inport.poll()
方法来检查是否有任何可用的 MIDI 消息,而不会阻塞代码的执行。如果有一个消息可用,
inport.poll()
会返回该消息;否则,它会返回
None
。
下面是使用非阻塞方法修改后的代码:
import pygame
import mido
import time
import RPi.GPIO as GPIO
pygame.init()
BLACK = [0,0,0]
WHITE = [255, 222, 111]
note_list = []
note_list_off = []
outport=mido.open_output()
inport=mido.open_input()
GPIO.setmode(GPIO.BCM)
done = False
GPIO.setup(4,GPIO.OUT)
print("START!")
while not done:
msg = inport.poll() # 非阻塞方法检查消息
if msg:
try:
n = msg.note
except:
continue
if msg.velocity > 0 and msg.velocity != 64:
print(msg.velocity)
GPIO.output(4,True)
time.sleep(1)
GPIO.output(4,False)
else:
msg=mido.Message('note_off',note=n)
outport.send(msg)
pygame.quit()
2. 异步方法:
你也可以使用 Python 的
asyncio
库来异步处理 MIDI 输入。这将允许你的代码在等待 MIDI 输入的同时继续执行其他任务。
下面是使用
asyncio
库修改后的代码:
import pygame
import mido
import asyncio
import time
import RPi.GPIO as GPIO
pygame.init()
BLACK = [0,0,0]
WHITE = [255, 222, 111]
note_list = []
note_list_off = []
outport = mido.open_output()
inport = mido.open_input()
GPIO.setmode(GPIO.BCM)
done = False
GPIO.setup(4,GPIO.OUT)
async def process_midi():
async for msg in inport:
try:
n = msg.note
except:
continue
if msg.velocity > 0 and msg.velocity != 64:
print(msg.velocity)
GPIO.output(4,True)
await asyncio.sleep(1)
GPIO.output(4,False)
else:
msg = mido.Message('note_off', note=n)
outport.send(msg)
async def main():
print("START!")
await process_midi()
if __name__ == "__main__":
asyncio.run(main())
pygame.quit()
选择哪种方法取决于你的具体需求。如果你需要在等待 MIDI 输入的同时执行其他任务,那么异步方法是更好的选择。如果你的代码相对简单,并且不需要同时执行多个任务,那么非阻塞方法就足够了。
标签:python,raspberry-pi,midi,mido From: 78616189