# 使用 Sequencer 方法 > 原文: [https://docs.oracle.com/javase/tutorial/sound/MIDI-seq-methods.html](https://docs.oracle.com/javase/tutorial/sound/MIDI-seq-methods.html) [`Sequencer`](https://docs.oracle.com/javase/8/docs/api/javax/sound/midi/Sequencer.html) 接口提供以下几种方法: * 从 MIDI 文件或`Sequence`对象加载序列数据,并将当前加载的序列数据保存到 MIDI 文件的方法。 * 类似于录音机的传输功能的方法,用于停止和开始播放和录制,启用和禁用特定轨道上的录制,以及在`Sequence`中穿梭当前播放或录制位置。 * 用于查询和设置对象的同步和定时参数的高级方法。 `Sequencer`可以以不同的速度播放,其中一些`Tracks`静音,并且与其他对象处于各种同步状态。 * 用于注册“监听器”对象的高级方法,这些对象在`Sequencer`处理某些类型的 MIDI 事件时得到通知。 无论您调用哪种`Sequencer`方法,第一步是从系统中获取`Sequencer`设备并保留它以供程序使用。 ## 获取序列发生器 应用程序不实例化`Sequencer`;毕竟,`Sequencer`只是一个接口。相反,与 Java Sound API 的 MIDI 包中的所有设备一样,通过静态`MidiSystem`对象访问`Sequencer`。如前面[访问 MIDI 系统资源](accessing-MIDI.html)中所述,以下`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 系统资源](accessing-MIDI.html)中的描述获得非默认音序器。 ## 加载序列 从系统中获取了一个音序器并保留了它,然后你需要加载音序器应该播放的数据。有三种典型的方法来实现这一目标: * 从 MIDI 文件中读取序列数据 * 通过接收来自其他设备(如 MIDI 输入端口)的 MIDI 信息实时录制 * 通过向空序列添加轨道并将`MidiEvent`对象添加到这些轨道,以“从头开始”以编程方式构建它 我们现在看一下获取序列数据的第一种方法。 (另外两种方式分别在[记录和保存序列](#124654)和[编辑序列](#124674)中描述。)第一种方法实际上包含两种稍微不同的方法。一种方法是将 MIDI 文件数据输入`InputStream`,然后通过`Sequencer.setSequence(InputStream)`将其直接读取到顺控程序。使用此方法,您不会显式创建`Sequence`对象。事实上,`Sequencer`实现甚至可能不会在幕后创建`Sequence`,因为一些序列发生器具有直接从文件处理数据的内置机制。 另一种方法是明确地创建`Sequence`。如果您要在播放之前编辑序列数据,则需要使用此方法。使用此方法,您可以调用`MidiSystem's`重载方法`getSequence`。该方法能够从`InputStream`,`File`或`URL`获得序列。该方法返回一个`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() ``` 和 ``` void stop() ``` `Sequencer.start`方法开始播放序列。请注意,播放从序列中的当前位置开始。使用上述`setSequence`方法加载现有序列,将序列发生器的当前位置初始化为序列的最开头。 `stop`方法停止音序器,但它不会自动倒回当前`Sequence`。在不重置位置的情况下启动已停止的`Sequence`只会从当前位置恢复序列的播放。在这种情况下,`stop`方法用作暂停操作。但是,在开始播放之前,有各种`Sequencer`方法用于将当前序列位置设置为任意值。 (我们将在下面讨论这些方法。) 如前所述,`Sequencer`通常有一个或多个`Transmitter`对象,通过它们将`MidiMessages`发送到`Receiver`。正是通过这些`Transmitters`,`Sequencer`通过适当地发射对应于当前`Sequence`中包含的`MidiEvents`的定时`MidiMessages`来播放`Sequence`。因此,播放`Sequence`的部分设置过程是调用`Sequencer's` `Transmitter`对象上的`setReceiver`方法,实际上将其输出连接到将使用播放数据的设备。有关`Transmitters`和`Receivers`的更多详情,请参阅[发送和接收 MIDI 信息](MIDI-messages.html)。 ## 录制和保存序列 要将 MIDI 数据捕获到`Sequence`,然后再捕获到文件,您需要执行除上述步骤之外的其他一些步骤。以下概述显示了设置录制到`Sequence`中`Track`所需的步骤: 1. 如上所述,使用`MidiSystem.getSequencer`获取用于录制的新音序器。 2. 设置 MIDI 连接的“接线”。发送要记录的 MIDI 数据的对象应通过其`setReceiver`方法配置为将数据发送到与记录`Sequencer`相关联的`Receiver`。 3. 创建一个新的`Sequence`对象,它将存储记录的数据。创建`Sequence`对象时,必须指定序列的全局计时信息。例如: ``` Sequence mySeq; try{ mySeq = new Sequence(Sequence.PPQ, 10); } catch (Exception ex) { ex.printStackTrace(); } ``` `Sequence`的构造器将`divisionType`和时序分辨率作为参数。 `divisionType`参数指定 resolution 参数的单位。在这种情况下,我们已经指定我们创建的`Sequence`的定时分辨率将是每季度 10 个脉冲。 `Sequence`构造器的另一个可选参数是多个轨道参数,这将导致初始序列以指定数量的(最初为空)`Tracks`开始。否则将创建`Sequence`而没有初始`Tracks`;可以根据需要稍后添加它们。 4. 使用`Sequence.createTrack`在`Sequence`中创建一个空`Track`。如果使用初始`Tracks`创建`Sequence`,则不需要此步骤。 5. 使用`Sequencer.setSequence`,选择我们的新`Sequence`接收录音。 `setSequence`方法将现有的`Sequence`与`Sequencer`连接在一起,这有点类似于将磁带装入磁带录音机。 6. 为每个`Track`调用`Sequencer.recordEnable`进行记录。如有必要,通过调用`Sequence.getTracks`获取`Sequence`中可用`Tracks`的参考。 7. 调用`Sequencer`上的`startRecording`。 8. 完成录制后,调用`Sequencer.stop`或`Sequencer.stopRecording`。 9. 用`MidiSystem.write`将录制的`Sequence`保存到 MIDI 文件。 `MidiSystem`的`write`方法将`Sequence`作为其参数之一,并将`Sequence`写入流或文件。 ## 编辑序列 许多应用程序允许通过从文件加载序列来创建序列,还有一些应用程序允许通过从实时 MIDI 输入(即录制)捕获序列来创建序列。但是,某些程序需要从头开始创建 MIDI 序列,无论是以编程方式还是响应用户输入。功能齐全的音序器程序允许用户手动构建新序列,以及编辑现有序列。 这些数据编辑操作是在 Java Sound API 中实现的,而不是通过`Sequencer`方法实现的,而是通过数据对象本身的方法实现的:`Sequence`,`Track`和`MidiEvent`。您可以使用`Sequence`构造器之一创建一个空序列,然后通过调用以下`Sequence`方法向其中添加轨道: ``` Track createTrack() ``` 如果您的程序允许用户编辑序列,则需要使用此`Sequence`方法删除轨道: ``` boolean deleteTrack(Track track) ``` 一旦序列包含轨道,您可以通过调用`Track`类的方法来修改轨道的内容。 `Track`中包含的`MidiEvents`作为`Track`对象存储在`Track`中,`Track`提供了一组访问,添加和删除列表中事件的方法。方法`add`和`remove`相当不言自明,在`Track`中添加或删除指定的`MidiEvent`。提供了`get`方法,该方法将索引存入`Track's`事件列表并返回存储在那里的`MidiEvent`。此外,还有`size`和`tick`方法,它们分别返回轨道中`MidiEvents`的数量和轨道的持续时间,表示为`Ticks`的总数。 要在将新事件添加到轨道之前创建新事件,您当然会使用`MidiEvent`构造器。要指定或修改事件中嵌入的 MIDI 消息,可以调用相应`MidiMessage`子类(`ShortMessage`,`SysexMessage`或`MetaMessage`)的`setMessage`方法。要修改事件发生的时间,请调用`MidiEvent.setTick`。 结合使用,这些低级方法为全功能音序器程序所需的编辑功能提供了基础。