如何使用Java识别XG格式MIDI文件中的鼓点音轨及音符
XG格式MIDI识别与鼓点检测实现方案(Java javax.sound.midi场景)
1. 判断MIDI文件是否为XG格式
XG格式的核心判定依据是文件头部的系统专有消息(SysEx),标准XG初始化SysEx的固定前缀为 F0 43 10 4C 00 00 7E 00,结尾为 F7,遍历MIDI文件所有轨道的系统消息匹配该前缀即可完成判定。
示例代码:
import javax.sound.midi.*; public boolean isXGFormat(Sequence sequence) { // XG标准初始化SysEx前缀 byte[] XG_SYS_EX_HEADER = new byte[]{(byte)0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00}; for (Track track : sequence.getTracks()) { for (int i = 0; i < track.size(); i++) { MidiMessage msg = track.get(i).getMessage(); if (msg instanceof SysexMessage) { byte[] data = msg.getMessage(); if (data.length >= XG_SYS_EX_HEADER.length) { boolean isMatch = true; for (int j = 0; j < XG_SYS_EX_HEADER.length; j++) { if (data[j] != XG_SYS_EX_HEADER[j]) { isMatch = false; break; } } if (isMatch) return true; } } } } return false; }
2. XG鼓点音符检测逻辑
原有仅检测通道9的方案漏判,是因为XG标准除了默认的10号通道(Java中索引为9)为鼓组通道外,还支持用户自定义最多8个鼓组通道,正确的检测需要先标记所有鼓组通道,再判断Note ON事件所属通道:
第一步:预标记所有鼓组通道
- 初始化时默认通道9为鼓组通道
- 遍历所有MIDI事件,满足以下所有条件的通道也标记为鼓组通道:
- 控制改变消息(Control Change)CC#0值为127
- 控制改变消息CC#32值为0
- 程序改变消息(Program Change)值在120~127区间
第二步:检测鼓点音符
所有属于已标记鼓组通道、且力度>0的Note ON消息,均为鼓点音符
示例代码:
// 初始化标记数组 boolean[] isDrumChannel = new boolean[16]; boolean[] cc0Flag = new boolean[16]; // 存储各通道CC#0=127的状态 boolean[] cc32Flag = new boolean[16]; // 存储各通道CC#32=0的状态 isDrumChannel[9] = true; // 默认通道9为鼓组通道 // 遍历所有事件更新鼓组通道标记 for (Track track : sequence.getTracks()) { for (int i = 0; i < track.size(); i++) { MidiMessage msg = track.get(i).getMessage(); if (msg instanceof ShortMessage sm) { int channel = sm.getChannel(); switch (sm.getCommand()) { case ShortMessage.CONTROL_CHANGE -> { if (sm.getData1() == 0 && sm.getData2() == 127) cc0Flag[channel] = true; if (sm.getData1() == 32 && sm.getData2() == 0) cc32Flag[channel] = true; } case ShortMessage.PROGRAM_CHANGE -> { int progNum = sm.getData1(); if (cc0Flag[channel] && cc32Flag[channel] && progNum >=120 && progNum <=127) { isDrumChannel[channel] = true; } } } } } } // 鼓点判定方法 public boolean isDrumNote(ShortMessage sm) { return sm.getCommand() == ShortMessage.NOTE_ON && sm.getData2() > 0 && isDrumChannel[sm.getChannel()]; }
注意事项
- 上述逻辑仅适配标准XG格式MIDI,非规范自制XG文件可能需要额外适配
- 判定SysEx时注意完整匹配前缀,避免将GM/GS格式的系统消息误判为XG
内容的提问来源于stack exchange,提问作者Invariant
相关产品推荐
相关产品推荐

