【发布时间】:2010-12-01 21:37:54
【问题描述】:
我从连接到计算机的钢琴上继承了一个使用 an old C++ dll to receive MIDI data 的 Java 项目。
既然 Java 已经内置了对 MIDI 设备的支持,我想摆脱传统的 C++ dll 并只使用纯 Java。 Java 是否支持从连接到计算机的钢琴接收数据?我在 Google 上搜索了示例,但无济于事。
【问题讨论】:
我从连接到计算机的钢琴上继承了一个使用 an old C++ dll to receive MIDI data 的 Java 项目。
既然 Java 已经内置了对 MIDI 设备的支持,我想摆脱传统的 C++ dll 并只使用纯 Java。 Java 是否支持从连接到计算机的钢琴接收数据?我在 Google 上搜索了示例,但无济于事。
【问题讨论】:
如果您只想使用 Java (javax.sound.midi.*) 的 MIDI api 进行录制,这很容易完成。这不是复制和粘贴的代码,但它应该可以帮助您开始编写自己的 MIDI 录音机,这实际上很容易。
第一步是定义你的输入和输出 MidiDevice。因此,首先您必须找到 IO 可能性列表并制作一个 GUI,您可以在其中选择用于 MIDI 录制和播放的输入和输出设备。
Info[] infos = MidiSystem.getMidiDeviceInfo();
for(int i=0;i<infos.length;i++)
{
System.out.println(infos[i].getName() + " - " + infos[i].getDescription());
}
所以有一个您的 MIDI 设备列表。接下来您要选择一个 MIDI 设备,例如您可以选择 infos 数组中的索引。
MidiDevice inputDevice = MidiSystem.getMidiDevice(infos[x]);
MidiDevice outputDevice = MidiSystem.getMidiDevice(infos[y]);
您还需要指定一些全局变量:定序器、发送器和接收器。
Sequencer sequencer = MidiSystem.getSequencer();
Transmitter transmitter;
Receiver receiver;
现在有一个您要使用的录制按钮。
// Open a connection to your input device
inputDevice.open();
// Open a connection to the default sequencer (as specified by MidiSystem)
sequencer.open();
// Get the transmitter class from your input device
transmitter = inputDevice.getTransmitter();
// Get the receiver class from your sequencer
receiver = sequencer.getReceiver();
// Bind the transmitter to the receiver so the receiver gets input from the transmitter
transmitter.setReceiver(receiver);
// Create a new sequence
Sequence seq = new Sequence(Sequence.PPQ, 24);
// And of course a track to record the input on
Track currentTrack = seq.createTrack();
// Do some sequencer settings
sequencer.setSequence(seq);
sequencer.setTickPosition(0);
sequencer.recordEnable(currentTrack, -1);
// And start recording
sequencer.startRecording();
小心,此代码可能会抛出 MidiUnavailableExceptions,您应该在 finally 语句中打开的所有内容上调用 close 方法。
但这只是代码应该是什么样子的核心。只要您调用方法sequencer.startRecording(),它就会将所有内容记录到序列seq。
然后您想停止录制,并能够将序列作为 MIDI 保存到文件中,或者进行播放。例如,当您按下停止录制按钮或其他东西时,这可能是代码。
// Stop recording
if(sequencer.isRecording())
{
// Tell sequencer to stop recording
sequencer.stopRecording();
// Retrieve the sequence containing the stuff you played on the MIDI instrument
Sequence tmp = sequencer.getSequence();
// Save to file
MidiSystem.write(tmp, 0, new File("MyMidiFile.mid"));
}
Track 类(一个序列可以有多个轨道)也包含实际的输入数据,您可以通过 get 方法轻松访问这些数据。 Track 类由 MidiEvents 组成。例如 Track 是:
MidiEvent 0: The C key is pressed
MidiEvent 1: The D key is pressed
MidiEvent 2: The C key of MidiEvent 0 is released
MidiEvent 3: The sustain pedal is pressed
etc...
每个 MidiEvent 都有一个特定的时间戳,以 MIDI Ticks 表示,因此您可以通过增加或减少每秒的节拍数来轻松更改节奏。
这里最困难的问题是 MidiEvents 以字节码表示,因此您必须使用参考字节码表,它告诉您什么字节代表什么动作。这应该让你开始:http://www.onicos.com/staff/iz/formats/midi-event.html
【讨论】:
Track 视为List<MidiEvent>。仅此而已,see also this Oracle Java doc on javax.sound.midi.Track。
Track 类开始,然后拦截add 事件,并将其传递给另一个正在播放传入事件队列的线程。请务必使用synchronize 以确保防止出现多线程问题。但是,我想知道如何“实时”制作 MIDI。例如,我的 MIDI 到 USB 设备有大约四分之一秒左右的明显延迟(如果我没记错的话)。
midiDevice.getTransmitter().setReceiver(receiver1); midiDevice.getTransmitter().setReceiver(receiver2); 每次调用 getTransmitter() 都会实例化一个新的发送器。不直观,但有效。
是的,JavaSound API 可用于从 MIDI 设备读取 MIDI 数据。
JFugue 是一个用于音乐编程的 Java API,它使用 JavaSound API,可以帮助简化您与 JavaSound 的交互。在 JFugue 5.x 中,从 MIDI 设备捕获 10 秒 MIDI 数据的示例代码如下:
MidiDevice device = /* specify a MIDI device */
MusicTransmitterToSequence transmitter = new MusicTransmitterToSequence(device);
transmitter.listenForMillis(10000);
Sequence music = transmitter.getSequence();
您还可以开始和停止收听设备:
MidiDevice device = /* specify a MIDI device */
MusicTransmitterToSequence transmitter = new MusicTransmitterToSequence(device);
transmitter.startListening();
// Do stuff
transmitter.stopListening();
Sequence music = transmitter.getSequence();
【讨论】: