📜  Java| MIDI介绍

📅  最后修改于: 2022-05-13 01:55:11.810000             🧑  作者: Mango

Java| MIDI介绍

乐器数字接口 (MIDI) 标准定义了电子音乐设备(例如电子键盘乐器和个人计算机)的通信协议。 MIDI 数据可以在现场表演期间通过特殊电缆传输,也可以存储在标准类型的文件中以供以后播放或编辑。
MIDI 文件包含根据事件表示音乐的数据。每个事件都给出了要播放的音符的描述——持续时间、音高、速度、通道等。包 javax.sound.midi 为 MIDI 数据的 I/O、排序和合成提供接口和类。
MIDI 既是硬件规范,也是软件规范。
javax.sound.midi包用于在Java中创建和使用 midi 事件来生成简单的音轨。

Java Sound API 对 MIDI 设备的表示

  • MidiDevice 接口: MidiDevice 接口包括一个用于打开和关闭设备的 API。它还包括一个名为 MidiDevice.Info 的内部类,该类提供设备的文本描述,包括其名称、供应商和版本。
  • 发送器和接收器:设备发送数据的方式是通过它“拥有”的一个或多个发送器对象。类似地,设备接收数据的方式是通过它的一个或多个接收器对象。发送器对象实现发送器接口,接收器实现接收器接口。
    每个发射器一次只能连接一个接收器,反之亦然

Midi 系统的基本组件

  • 合成器:这是播放 MIDI 音轨的设备。它可以是软件合成器,也可以是真正的 midi 兼容乐器。
  • 音序器:音序器接收 Midi 数据(通过音序)并命令不同的乐器演奏音符。它根据开始时间、持续时间和要播放的频道安排事件。
  • 频道: Midi 支持多达 16 个不同的频道。我们可以将 midi 事件发送到稍后由音序器同步的任何通道。
  • Track:这是一系列 Midi 事件。
  • 序列:它是一个包含多个音轨和时序信息的数据结构。音序器接收一个序列并播放它。

重要的类和方法

  • MidiSystem :此类提供对已安装的 MIDI 资源的访问,例如音序器、合成器、I/O 端口。所有方法都是静态的,这个类不能被实例化。
  • MidiSystem.getSequencer() – 返回连接到合成器/接收器的音序器接口的实例。
  • sequencer.open() - 打开排序器,以便它可以获取系统资源。
  • sequencer.setSequence(Sequence sequence) – 设置序列器操作的当前序列。
  • sequencer.setTempoInBPM(float bpm) – 以每分钟节拍为单位设置播放速度。
  • sequencer.start() – 开始播放当前加载的序列中的 MIDI 数据。
  • sequencer.isRunning() – 指示 Sequencer 当前是否正在运行。
  • Sequence ——Sequence 类实例包含一个表示一个或多个轨道和时序信息的数据结构。
  • Sequence.PPQ – 基于速度的计时类型,其分辨率以每四分音符的脉冲数(滴答声)表示。
  • Sequence.createTrack() - 创建一个空轨道
  • Track – 包含按时间顺序排列的 midi 事件的类。
  • Track.add(MidiEvent event) – 向轨道添加新事件。
  • MidiEvent(MidiMessage message, long tick) – 一个包含时间戳的 midi 消息的 midi 事件对象。
  • ShortMessage() – 最多两个数据字节的 ShortMessage 对象(从 MidiMessage 扩展)。
  • ShortMessage.setMessage(int command, int channel, int data1, int data2) – 设置一个最多包含两个数据字节(data1 和 data2)的 ShortMessage 对象。

MIDI 命令

CodeCommand
144Note On Event
128Note Off Event
192Program Change for changing default instrument etc
176Control change for sending events
224Pitch Bend

下面的程序说明了 MIDI 在Java中的用法:
程序1:说明一个简单记录的实现。

Java
// Java program showing the implementation of a simple record
import javax.sound.midi.*;
import java.util.*;
 
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;
    }
}


Java
// Java program showing how to change the instrument type
import javax.sound.midi.*;
import java.util.*;
 
public class MyMidiPlayer1 {
 
    public static void main(String[] args)
    {
 
        MyMidiPlayer1 player = new MyMidiPlayer1();
 
        Scanner in = new Scanner(System.in);
        System.out.println("Enter the instrument to be played");
        int instrument = in.nextInt();
        System.out.println("Enter the note to be played");
        int note = in.nextInt();
 
        player.setUpPlayer(instrument, note);
    }
 
    public void setUpPlayer(int instrument, int note)
    {
 
        try {
 
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            Sequence sequence = new Sequence(Sequence.PPQ, 4);
            Track track = sequence.createTrack();
 
            // Set the instrument type
            track.add(makeEvent(192, 1, instrument, 0, 1));
 
            // Add a note on event with specified note
            track.add(makeEvent(144, 1, note, 100, 1));
 
            // Add a note off event with specified note
            track.add(makeEvent(128, 1, note, 100, 4));
 
            sequencer.setSequence(sequence);
            sequencer.start();
 
            while (true) {
 
                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 a = new ShortMessage();
            a.setMessage(command, channel, note, velocity);
 
            event = new MidiEvent(a, tick);
        }
        catch (Exception ex) {
 
            ex.printStackTrace();
        }
        return event;
    }
}


Input: Enter the number of notes to be played: 
       15 
Output: 15 sound notes with increasing pitch are played

Input: Enter the number of notes to be played: 
       25
Output: 25 sound notes with increasing pitch are played

(Note: Number of notes should not exceed 31 for reasons cited later)

为什么音符数量限制为 31 个?
由于 ShortMessage 的 data1 和 data2 字段是字节类型的,所以在使用 setMessage(int command, int channel, int note, int velocity) 时,note 和 velocity 不能超过 127。
方案二:使用指令码 192 更改仪器型号

Java

// Java program showing how to change the instrument type
import javax.sound.midi.*;
import java.util.*;
 
public class MyMidiPlayer1 {
 
    public static void main(String[] args)
    {
 
        MyMidiPlayer1 player = new MyMidiPlayer1();
 
        Scanner in = new Scanner(System.in);
        System.out.println("Enter the instrument to be played");
        int instrument = in.nextInt();
        System.out.println("Enter the note to be played");
        int note = in.nextInt();
 
        player.setUpPlayer(instrument, note);
    }
 
    public void setUpPlayer(int instrument, int note)
    {
 
        try {
 
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            Sequence sequence = new Sequence(Sequence.PPQ, 4);
            Track track = sequence.createTrack();
 
            // Set the instrument type
            track.add(makeEvent(192, 1, instrument, 0, 1));
 
            // Add a note on event with specified note
            track.add(makeEvent(144, 1, note, 100, 1));
 
            // Add a note off event with specified note
            track.add(makeEvent(128, 1, note, 100, 4));
 
            sequencer.setSequence(sequence);
            sequencer.start();
 
            while (true) {
 
                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 a = new ShortMessage();
            a.setMessage(command, channel, note, velocity);
 
            event = new MidiEvent(a, tick);
        }
        catch (Exception ex) {
 
            ex.printStackTrace();
        }
        return event;
    }
}
Input : Enter the instrument to be played
        102
        Enter the note to be played
        110

Output : Sound note is played

Input : Enter the instrument to be played
        40
        Enter the note to be played
        100

Output : Sound note is played

注意:代码不会在在线 IDE 上运行,因为代码需要几秒钟的运行时间才能播放,而 IDE 不允许。