从现场乐器读取MIDI输入流



我正在尝试创建一种自动乐谱查看器,只是为了好玩,我最终会尝试将其与树莓派或其他东西集成在我的数字钢琴中,但那是另一个故事。

为了使这个工作,我必须比较MIDI输入来自我的钢琴,与MIDI文件,代表我正在播放的乐谱。

当我想实时比较两种MIDI时,我发现很难直接从我的钢琴上获得现场MIDI。

我已经尝试过读取10秒的MIDI并将所有内容写入文件,并且它完美地工作,但是我找不到任何方法来接收从我的钢琴发送的消息的回调。

这是我目前的测试代码(这是可怕的,我知道,但我只是测试现在),我基本上写一个。mid文件10秒的MIDI来自我的钢琴,我也尝试使用一个ControllerEventListener,但它似乎不工作。

import java.io.File;
import java.io.IOException;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
import javax.sound.midi.Transmitter;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) throws Exception {

Info[] infos = MidiSystem.getMidiDeviceInfo();
for(int i = 0; i < infos.length; i++) {
System.out.println(infos[i].getName() + " - " + infos[i].getDescription());
}

MidiDevice inputDevice = MidiSystem.getMidiDevice(infos[1]);

Sequencer sequencer = MidiSystem.getSequencer();
Receiver receiver;
Transmitter transmitter;

inputDevice.open();
sequencer.open();

transmitter = inputDevice.getTransmitter();
receiver = sequencer.getReceiver();
transmitter.setReceiver(receiver);

Sequence seq = new Sequence(Sequence.PPQ, 24);

Track currentTrack = seq.createTrack();

sequencer.setSequence(seq);
sequencer.setTickPosition(0);

sequencer.addControllerEventListener(new ControllerEventListener() {

@Override
public void controlChange(ShortMessage event) {
System.out.println("listener works");
}
}, new int[] {127});

sequencer.recordEnable(currentTrack, -1);

sequencer.startRecording();

new Timer(10000, e -> {

Sequence tmp = sequencer.getSequence();
sequencer.stopRecording();

try {
MidiSystem.write(tmp, 0, new File("midi.mid"));
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}).start();

}
}

正如你所看到的,我试图添加一个ControllerEventListener到Sequencer,但它似乎不工作时,监听事件,同时记录:

sequencer.addControllerEventListener(new ControllerEventListener() {
@Override
public void controlChange(ShortMessage event) {
System.out.println("listener works");
}
}, new int[] {127});

不管我怎么努力,但我找不到任何可以帮助我的…有办法接收现场MIDI乐器上的事件吗?

我找不到任何方法来接收回调每当消息是我的钢琴发出的

如果你想在一个Note ON midmessage从你的钢琴到达时被实时通知,使用自定义Receiver。

...
transmitter = inputDevice.getTransmitter();
receiver = new MyReceiver();
transmitter.setReceiver(receiver);
...

private class MyReceiver implements Receiver
{
...

@Override
public void send(MidiMessage mm, long timeStamp)
{
if (mm instanceof ShortMessage sm && sm.getCommand() == ShortMessage.NOTE_ON)
{           
// Do something with the NoteOn message, preferably in another thread
} 
}
}

如果你想要Sequencer记录MidiMessages并行创建一个序列,从inputDevice获得第二个发射器,并将其连接到Sequencer的接收器。

最新更新