在 Java 中使用 MIDI

Using MIDI in Java

我正在尝试编写一个使用 MIDI 的 Java 程序,因此该程序必须排列:notes(其中包含我想要播放的所有音符)和另一个数组 times(指定何时音符应该播放)音符和时间一次分组三个所以我可以有多个和弦,问题是程序只播放一个非常简短的音符然后停止,我做错了什么?下面是代码,我用的是 Java 16.

package application;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.ShortMessage;

public class App {

    public static void main(String[] args)
            throws MidiUnavailableException, InvalidMidiDataException, InterruptedException {

        var receiver = MidiSystem.getReceiver();

        int[] notes = { 60, 64, 67, 60, 65, 67, 55, 59, 62, 55, 60, 62, 53, 57, 60, 53, 58, 60 };
        int[] times = { 0, 0, 0, 1000, 1000, 1000, 2000, 2000, 2000, 3000, 3000, 3000, 4000, 4000, 4000, 5000, 5000,
                5000 };

        for (int i = 0; i < notes.length; i++) {

            int note = notes[i];
            int time = times[i];
            System.out.println(note + ":" + time);
            receiver.send(new ShortMessage(ShortMessage.NOTE_ON, 0, note, 127), time * 1000);
            receiver.send(new ShortMessage(ShortMessage.NOTE_OFF, 0, note, 127), (time + 1000) * 1000);
        }

        Thread.sleep(7000);
    }

}

我认为这与ShortMessage.NOTE_OFF有关,但我不确定,我无法弄清楚。

问题很可能与您的 times 数组有关。

取自神谕java documentation on sequancers

As previously mentioned, the program can include a time stamp with each MIDI message it sends to the device's receiver. However, such time stamps are used only for fine-tuning the timing, to correct for processing latency. The caller can't generally set arbitrary time stamps; the time value passed to Receiver.send must be close to the present time, or the receiving device might not be able to schedule the message correctly. This means that if an application program wanted to create a queue of MIDI messages for an entire piece of music ahead of time (instead of creating each message in response to a real-time event), it would have to be very careful to schedule each invocation of Receiver.send for nearly the right time.

Fortunately, most application programs don't have to be concerned with such scheduling. Instead of invoking Receiver.send itself, a program can use a Sequencer object to manage the queue of MIDI messages for it.

你盲目假设数字(1000、2000、3000...等)是毫秒,但在api中定义为ticks

就像在任何音乐中一样,您需要定义细分,以及每节拍的脉冲 (PPQ),例如四分音符、十六分音符等。

在 java 中,这通常是通过定义音序器并使用这些设置,然后在所述音序器上创建轨道,然后在该轨道上播放音符来完成的。

这是我在网上找到的例子。

public class MyMidiPlayer {
 
    public static void main(String[] args) {
 
        System.out.println("Enter the number of notes to be played: ");
        Scanner in = new Scanner(System.in);
        int numOfNotes = in.nextInt();
 
        MyMidiPlayer player = new MyMidiPlayer();
        player.setUpPlayer(numOfNotes);
    }
 
    public void setUpPlayer(int numOfNotes) {
 
        try {
 
            // A static method of MidiSystem that returns
            // a sequencer instance.
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
 
            // Creating a sequence.
            Sequence sequence = new Sequence(Sequence.PPQ, 4);
 
            // PPQ(Pulse per ticks) is used to specify timing
            // type and 4 is the timing resolution.
 
            // Creating a track on our sequence upon which
            // MIDI events would be placed
            Track track = sequence.createTrack();
 
                // Adding some events to the track
            for (int i = 5; i < (4 * numOfNotes) + 5; i += 4){
 
                // Add Note On event
                track.add(makeEvent(144, 1, i, 100, i));
 
                // Add Note Off event
                track.add(makeEvent(128, 1, i, 100, i + 2));
            }
 
            // Setting our sequence so that the sequencer can
            // run it on synthesizer
            sequencer.setSequence(sequence);
 
            // Specifies the beat rate in beats per minute.
            sequencer.setTempoInBPM(220);
 
            // Sequencer starts to play notes
            sequencer.start();
 
            while (true) {
 
                // Exit the program when sequencer has stopped playing.
                if (!sequencer.isRunning()) {
                    sequencer.close();
                    System.exit(1);
                }
            }
        }
        catch (Exception ex) {
 
            ex.printStackTrace();
        }
    }
 
    public MidiEvent makeEvent(int command, int channel,
                               int note, int velocity, int tick) {
 
        MidiEvent event = null;
 
        try {
 
            // ShortMessage stores a note as command type, channel,
            // instrument it has to be played on and its speed.
            ShortMessage a = new ShortMessage();
            a.setMessage(command, channel, note, velocity);
 
            // A midi event is comprised of a short message(representing
            // a note) and the tick at which that note has to be played
            event = new MidiEvent(a, tick);
        }
        catch (Exception ex) {
 
            ex.printStackTrace();
        }
        return event;
    }
}