如何解析MIDI键盘输出的音符值以执行对应命令?
如何解析MIDI键盘输出的音符值以执行对应命令?
看起来你遇到的核心问题是MIDI数据流的实时处理和消息过滤,我来帮你梳理下解决方案:
首先得明确你看到的那些十六进制行对应的MIDI消息类型:
- 以
E0开头的是控制消息(比如弯音轮、表情踏板这类),不是我们需要的音符触发事件 - 以
90开头的是Note On消息(按键按下时发送),这正是你要捕捉来执行命令的目标 - 以
80开头的是Note Off消息(按键松开时发送),如果不需要处理松开动作可以暂时忽略
你之前用head -n NUM截取固定行数的方式行不通,原因很简单:MIDI消息是异步流数据,只有按键时才会输出,而且消息顺序完全随机(可能先出控制消息,再出音符消息),固定行数会导致要么等待很久,要么拿到的是无关消息,还会打乱顺序。
正确的实时处理方案
我们需要逐行读取amidi的输出,只过滤出Note On消息,这样每按下一个键就能立刻触发对应命令,不会出现顺序混乱的问题。
这里给你一个修改后的完整脚本:
#!/bin/bash set -x MIDI_DEVICE="hw:1,0,0" # 记得替换成你的实际MIDI设备ID # 实时读取MIDI输出,逐行处理每一条消息 amidi -p "$MIDI_DEVICE" -d | while read -r line; do # 只处理按键按下的Note On消息(以90开头) if [[ "$line" == 90* ]]; then # 如果你需要精准匹配完整的Note On消息(包含力度) full_note="$line" echo "检测到按键消息: $full_note" case "$full_note" in "904E63") xdotool key ctrl+alt+t # 打开终端 ;; # 可以添加更多完整消息对应的操作 "904F60") xdotool key ctrl+w # 关闭当前窗口 ;; esac # 如果你想忽略力度,只按音符编号匹配(更灵活) # note_num=${line:2:2} # 提取消息的第3-4位(十六进制音符编号) # case "$note_num" in # "4E") # xdotool key ctrl+alt+t # ;; # esac fi # 如果需要处理按键松开事件,可以添加下面的判断 # elif [[ "$line" == 80* ]]; then # echo "按键松开: $line" # # 这里可以添加松开时的操作 # fi done
关键细节解释
- 实时流处理:用
amidi ... | while read -r line代替一次性截取,amidi的输出会实时传给循环,每收到一条消息就处理一条,不需要等待固定行数。 - 消息过滤:通过
[[ "$line" == 90* ]]筛选出Note On消息,直接跳过无关的控制消息和松开消息。 - 两种匹配方式:
- 匹配完整消息:适合需要区分不同力度的场景(比如轻按和重按执行不同命令)
- 匹配音符编号:提取
line的第3到第4位(${line:2:2}),忽略最后两位的力度值,同一个音符不管力度大小都会触发命令,更通用。
调试小技巧
先单独运行amidi -p "hw:1,0,0" -d,然后按你想映射的键盘按键,记录下对应的90开头的消息,再把这些消息添加到脚本的case分支里就可以了。
备注:内容来源于stack exchange,提问作者j0h
相关产品推荐
相关产品推荐

