使用定序器方法

Sequencerinterface提供了几种类别的方法:

  • 方法,用于从 MIDI 文件或Sequence对象加载音序数据,并将当前加载的音序数据保存到 MIDI 文件。

  • 与磁带录音机的传输功能类似的方法,用于停止和开始播放和记录,启用和禁用特定轨道上的记录以及在Sequence中调整当前播放或记录位置。

  • 用于查询和设置对象的同步和定时参数的高级方法。 Sequencer可能以不同的速度播放,其中某些Tracks被静音,并且与其他对象处于各种同步状态。

  • 用于注册“侦听器”对象的高级方法,这些对象在Sequencer处理某些类型的 MIDI 事件时会得到通知。

无论您要调用哪种Sequencer方法,第一步都是从系统中获取Sequencer设备并将其保留供程序使用。

获取音序器

应用程序未实例化Sequencer;毕竟,Sequencer只是一个interface。而是像 Java Sound API 的 MIDI 包中的所有设备一样,通过静态MidiSystem对象访问Sequencer。如先前在访问 MIDI 系统资源中提到的,以下MidiSystem方法可用于获取默认的Sequencer

static Sequencer getSequencer()

以下代码片段获取默认的Sequencer,获取所需的任何系统资源,并使之可运行:

Sequencer sequencer;
// Get default sequencer.
sequencer = MidiSystem.getSequencer(); 
if (sequencer == null) {
    // Error -- sequencer device is not supported.
    // Inform user and return...
} else {
    // Acquire resources and make operational.
    sequencer.open();
}

open的调用保留了音序器设备供您的程序使用。想象共享一个音序器没有多大意义,因为它一次只能播放一个音序。使用定序器完成后,可以通过调用close将其提供给其他程序使用。

访问 MIDI 系统资源中所述可以获得非默认音序器。

加载序列

从系统中获取了音序器并进行了保留,然后需要加载音序器应播放的数据。有三种典型的方法可以完成此操作:

  • 从 MIDI 文件中读取音序数据

  • 通过从其他设备(例如 MIDIImporting 端口)接收 MIDI 消息来实时记录

  • 以编程方式“从头开始”构建它,方法是将轨道添加到空序列并将MidiEvent对象添加到这些轨道

现在,我们将探讨获取序列数据的第一种方法。 (下面分别在记录和保存序列编辑序列下描述了其他两种方法。)第一种方法实际上包含两种略有不同的方法。一种方法是将 MIDI 文件数据 Importing 到InputStream,然后通过Sequencer.setSequence(InputStream)直接读到音序器。使用这种方法,您无需显式创建Sequence对象。实际上,Sequencer实现可能甚至不会在幕后创建Sequence,因为某些音序器具有内置的机制来直接处理文件中的数据。

另一种方法是显式创建Sequence。如果要在播放之前编辑序列数据,则需要使用此方法。使用这种方法,您可以调用MidiSystem's重载方法getSequence。该方法能够从InputStreamFileURL获得序列。该方法返回一个Sequence对象,然后可以将其加载到Sequencer中进行播放。扩展前面的代码摘录,下面是一个从File获取Sequence对象并将其加载到我们的sequencer中的示例:

try {
    File myMidiFile = new File("seq1.mid");
    // Construct a Sequence object, and
    // load it into my sequencer.
    Sequence mySeq = MidiSystem.getSequence(myMidiFile);
    sequencer.setSequence(mySeq);
} catch (Exception e) {
   // Handle error and/or return
}

MidiSystem's getSequence方法一样,setSequence可能会引发InvalidMidiDataException,如果是InputStream变体,则可能抛出IOException,如果它遇到任何麻烦。

播放音序

启动和停止Sequencer是使用以下方法完成的:

void start()

and

void stop()

Sequencer.start方法开始播放序列。请注意,播放将从序列中的当前位置开始。如上所述,使用setSequence方法加载现有序列,会将定序器的当前位置初始化到序列的最开始。 stop方法停止定序器,但不会自动倒回当前的Sequence。在不重置位置的情况下启动停止的Sequence只是从当前位置恢复序列的播放。在这种情况下,stop方法已用作暂停操作。但是,有多种Sequencer方法可用于在开始播放之前将当前序列位置设置为任意值。 (我们将在下面讨论这些方法.)

如前所述,Sequencer通常具有一个或多个Transmitter对象,通过它可以将MidiMessages发送到Receiver。正是通过这些TransmittersSequencer通过发出与当前Sequence中包含的MidiEvents相对应的适当定时的MidiMessages来播放Sequence。因此,用于播放Sequence的设置过程的一部分是调用Sequencer's Transmitter对象上的setReceiver方法,实际上是将其输出连接到将使用播放的数据的设备。有关TransmittersReceivers的更多详细信息,请参考传输和接收 MIDI 信息

记录和保存序列

要将 MIDI 数据catch到Sequence,然后catch到文件,您需要执行上述其他步骤。以下概述显示了设置以在Sequence中记录到Track的必要步骤:

  • 使用MidiSystem.getSequencer获得新的音序器以进行录制,如上所述。

  • 设置 MIDI 连接的“接线”。传输要记录的 MIDI 数据的对象应通过其setReceiver方法配置为将数据发送到与记录Sequencer关联的Receiver

  • 创建一个新的Sequence对象,该对象将存储记录的数据。创建Sequence对象时,必须为序列指定全局时序信息。例如:

Sequence mySeq;
      try{
          mySeq = new Sequence(Sequence.PPQ, 10);
      } catch (Exception ex) { 
          ex.printStackTrace(); 
      }

Sequence的构造函数将divisionType和时间分辨率作为参数。 divisionType参数指定分辨率参数的单位。在这种情况下,我们已指定要创建的Sequence的定时分辨率为每四分音符 10 个脉冲。 Sequence构造函数的另一个可选参数是一些 tracks 参数,这将导致初始序列以指定数量的Tracks(最初为空)开头。否则Sequence将被创建而没有初始Tracks;以后可以根据需要添加它们。

  • Sequence中使用Sequence.createTrack创建一个空的Track。如果Sequence是使用初始Tracks创建的,则无需执行此步骤。

  • 使用Sequencer.setSequence,选择新的Sequence以接收录音。 setSequence方法将现有的SequenceSequencer绑定在一起,这有点类似于将磁带加载到磁带 Logger 上。

  • 为每个要记录的Track 1 调用Sequencer.recordEnable。如有必要,可以通过调用Sequence.getTracks获得对Sequence中可用Tracks的引用。

  • Sequencer上调用startRecording

  • 完成录制后,调用Sequencer.stopSequencer.stopRecording

  • 使用MidiSystem.write将记录的Sequence保存到 MIDI 文件。 MidiSystemwrite方法将Sequence作为其参数之一,并将该Sequence写入流或文件。

编辑序列

许多应用程序都允许通过从文件中加载序列来创建音序,还有许多应用程序也可以通过从实时 MIDIImporting(即录制)中catch音序来创建音序。但是,某些程序将需要以编程方式或响应用户 Importing 从头开始创建 MIDI 序列。功能齐全的定序器程序允许用户手动构建新序列,以及编辑现有序列。

这些数据编辑操作不是通过Sequencer方法,而是通过数据对象本身的方法SequenceTrackMidiEvent在 Java Sound API 中实现的。您可以使用Sequence构造函数之一创建一个空序列,然后通过调用以下Sequence方法向其添加轨道:

Track createTrack()

如果您的程序允许用户编辑序列,则需要此Sequence方法来删除曲目:

boolean deleteTrack(Track track)

序列中包含曲目后,您可以通过调用Track类的方法来修改曲 Object 内容。 Track中包含的MidiEvents作为java.util.Vector存储在Track对象中,并且Track提供了一组用于访问,添加和删除列表中事件的方法。方法addremove很容易解释,可以从Track中添加或删除指定的MidiEvent。提供了get方法,该方法将Track's事件列表中的索引作为索引并返回存储在其中的MidiEvent。此外,还有sizetick方法,它们分别返回轨道中MidiEvents的数目以及轨道的持续时间,以Ticks的总数表示。

要在将新事件添加到轨道之前创建一个新事件,您当然会使用MidiEvent构造函数。要指定或修改事件中嵌入的 MIDI 消息,您可以调用适当的MidiMessage子类(ShortMessageSysexMessageMetaMessage)的setMessage方法。要修改事件发生的时间,请调用MidiEvent.setTick

这些低级方法相结合,为功能齐全的音序器程序所需的编辑功能提供了基础。