# 使用高级定序器功能

> 原文： [https://docs.oracle.com/javase/tutorial/sound/MIDI-seq-adv.html](https://docs.oracle.com/javase/tutorial/sound/MIDI-seq-adv.html)

到目前为止，我们专注于 MIDI 数据的简单播放和录制。本节将简要介绍通过`Sequencer`接口和`Sequence`类的方法提供的一些更高级的功能。

## 移动到序列中的任意位置

有两种`Sequencer`方法可以获得序列中序列发生器的当前位置。第一个：

```
long getTickPosition()

```

返回从序列开头以 MIDI 标记测量的位置。第二种方法：

```
long getMicrosecondPosition()

```

以微秒为单位返回当前位置。此方法假定序列以默认速率播放，如存储在 MIDI 文件或`Sequence`中。如果你改变了播放速度，那么 _ 而不是 _ 会返回不同的值，如下所述。

您可以根据一个或另一个单位设置音序器的当前位置：

```
void setTickPosition(long tick)

```

要么

```
void setMicrosecondPosition(long microsecond)

```

## 改变播放速度

如前所述，序列的速度由其速度表示，速度可以在序列的过程中变化。序列可以包含封装标准 MIDI 速度变化消息的事件。当音序器处理这样的事件时，它会改变播放速度以反映指示的速度。此外，您可以通过调用任何这些`Sequencer`方法以编程方式更改速度：

```
    public void setTempoInBPM(float bpm)
    public void setTempoInMPQ(float mpq)
    public void setTempoFactor(float factor)

```

这两种方法中的前两种分别将节奏设置为每分钟节拍或每季度音符的微秒。速度将保持在指定的值，直到再次调用其中一个方法，或直到在序列中遇到一个速度变化事件，此时当前速度被新指定的速度覆盖。

第三种方法`setTempoFactor`的性质不同。它可以缩放为音序器设置的任何速度（无论是通过速度变化事件还是通过前面两种方法之一）。默认标量为 1.0（无更改）。虽然这种方法使播放或录音比标称速度更快或更慢（除非系数为 1.0），但它不会改变标称速度。换句话说，`getTempoInBPM`和`getTempoInMPQ`返回的速度值不受速度因子的影响，即使速度因素确实影响实际播放或录制速率。此外，如果速度由速度变化事件或前两种方法之一改变，它仍然可以通过上次设置的任何速度因子进行缩放。但是，如果加载新序列，则速度因子将重置为 1.0。

请注意，当序列的除法类型是 SMPTE 类型之一而不是 PPQ 时，所有这些速度变化指令都是无效的。

## 使序列中的单个轨道静音或独奏

音序器的用户通常可以方便地关闭某些音轨，更清楚地听到音乐中正在发生的事情。功能齐全的音序器程序可让用户选择播放时应播放的音轨。 （更确切地说，由于音序器本身并不实际产生声音，因此用户选择哪些音轨将对音序器产生的 MIDI 信息流做出贡献。）通常，每个音轨上有两种类型的图形控制：a _ 静音 _ 按钮和 _ 独奏 _ 按钮。如果静音按钮被激活，则在任何情况下都不会发出该音轨，直到静音按钮被取消激活。 Soloing 是一个鲜为人知的特征。这与静音大致相反。如果激活任何音轨上的独奏按钮，则只会激活其独奏按钮被激活的曲目。此功能允许用户快速试听少量曲目，而无需将所有其他曲目静音。静音按钮通常优先于独奏按钮：如果两者都被激活，则曲目不会发声。

使用`Sequencer`方法，可以轻松完成静音或独奏音轨（以及查询音轨的当前静音或独奏状态）。假设我们已经获得了默认`Sequencer`，并且我们已经将序列数据加载到其中。将序列中的第五个轨道静音如下：

```
    sequencer.setTrackMute(4, true);
    boolean muted = sequencer.getTrackMute(4);
    if (!muted) { 
        return;         // muting failed
    }

```

关于上面的代码片段，有几点需要注意。首先，序列的轨道从 0 开始编号，并以轨道总数减 1 结束。另外，`setTrackMute`的第二个参数是布尔值。如果是的话，请求是将轨道静音;否则请求是取消静音指定的曲目。最后，为了测试静音是否生效，我们调用`Sequencer getTrackMute`方法，将它传递给我们查询的轨道号。如果它返回`true`，正如我们在这种情况下所期望的那样，则静音请求起作用。如果它返回`false`，则失败。

静音请求可能因各种原因而失败。例如，`setTrackMute`调用中指定的曲目编号可能超过曲目总数，或者音序器可能不支持静音。通过调用`getTrackMute`，我们可以确定我们的请求是成功还是失败。

另外，`getTrackMute`返回的布尔值确实可以告诉我们是否发生了故障，但它无法告诉我们为什么会发生故障。我们可以测试是否通过将无效的轨道号传递给`setTrackMute`方法来导致故障。为此，我们将调用`Sequence`的`getTracks`方法，该方法返回包含序列中所有轨道的数组。如果`setTrackMute`调用中指定的轨道号超过该阵列的长度，则我们知道我们指定了无效的轨道号。

如果静音请求成功，那么在我们的示例中，第五首曲目在播放序列时不会发声，当前静音的任何其他曲目也不会发声。

用于独奏轨道的方法和技术与用于静音的方法和技术非常相似。要独奏曲目，请调用`Sequence:`的`setTrackSolo`方法

```
void setTrackSolo(int track, boolean bSolo)

```

与`setTrackMute`中一样，第一个参数指定从零开始的轨道号，如果`true`，则指定第二个参数指定轨道应处于独奏模式;否则赛道不应该独奏。

默认情况下，曲目既不是静音也不是独奏。

## 与其他 MIDI 设备同步

`Sequencer`有一个叫做`Sequencer.SyncMode`的内部类。 `SyncMode`对象表示 MIDI 音序器的时间概念可以与主设备或从设备同步的方式之一。如果音序器正在与主控器同步，则音序器会修改其当前时间以响应来自主控制器的某些 MIDI 消息。如果音序器具有从机，则音序器类似地发送 MIDI 消息以控制从机的时序。

有三种预定义模式可指定音序器的可能主控：`INTERNAL_CLOCK`，`MIDI_SYNC`和`MIDI_TIME_CODE`。如果音序器从另一个设备接收 MIDI 消息，则后两个工作。在这两种模式中，定序器的时间分别根据系统实时定时时钟消息或 MIDI 时间码（MTC）消息进行复位。 （有关这些类型的消息的更多信息，请参阅 MIDI 规范。）这两种模式也可用作从模式，在这种情况下，音序器将相应类型的 MIDI 消息发送到其接收器。第四种模式`NO_SYNC`用于指示定序器不应向其接收器发送定时信息。

通过使用支持的`SyncMode`对象作为参数调用`setMasterSyncMode`方法，可以指定如何控制顺控程序的时序。同样，`setSlaveSyncMode`方法确定定序器将发送给其接收器的定时信息。此信息控制使用序列发生器作为主定时源的设备的定时。

## 指定特殊事件监听器

序列的每个轨道可以包含许多不同种类的`MidiEvents`。此类事件包括 Note On 和 Note Off 消息，程序更改，控件更改和元事件。 Java Sound API 为最后两种事件类型（控制更改事件和元事件）指定“侦听器”接口。在播放序列期间发生此类事件时，您可以使用这些接口接收通知。

支持`ControllerEventListener`接口的对象可以在`Sequencer`处理特定控制变更消息时收到通知。控制改变消息是标准类型的 MIDI 消息，表示 MIDI 控制器的值的变化，例如弯音轮或数据滑块。 （有关控制变更消息的完整列表，请参阅 MIDI 规范。）在重放序列期间处理此类消息时，该消息指示从定序器接收数据的任何设备（可能是合成器）更新值。一些参数。该参数通常控制声音合成的某些方面，例如，如果控制器是弯音轮，则当前发声音符的音高。当正在记录序列时，控制改变消息意味着已经移动了创建消息的外部物理设备上的控制器，或者已经在软件中模拟了这样的移动。

以下是`ControllerEventListener`接口的使用方法。假设您已经开发了一个实现`ControllerEventListener`接口的类，这意味着您的类包含以下方法：

```
    void controlChange(ShortMessage msg)

```

我们还假设您已经创建了类的实例并将其分配给名为`myListener`的变量。如果在程序中的某处包含以下语句：

```
    int[] controllersOfInterest = { 1, 2, 4 };
    sequencer.addControllerEventListener(myListener,
        controllersOfInterest);

```

每次定序器处理 MIDI 控制器编号 1,2 或 4 的控制变更消息时，都会调用类的`controlChange`方法。换句话说，当`Sequencer`处理一个设置任何值的值的请求时注册的控制器，`Sequencer`将调用您的类的`controlChange`方法。 （请注意，MIDI 控制器编号到特定控制设备的分配在 MIDI 1.0 规范中有详细说明。）

`controlChange`方法传递一个`ShortMessage`，其中包含受影响的控制器编号，以及设置控制器的新值。您可以使用`ShortMessage.getData1`方法获取控制器编号，并使用`ShortMessage.getData2`方法获取控制器值的新设置。

另一种特殊事件监听器由`MetaEventListener`接口定义。根据标准 MIDI 文件 1.0 规范，元消息是 MIDI 线协议中不存在但可以嵌入 MIDI 文件中的消息。它们对合成器没有意义，但可以由音序器解释。元消息包括指令（例如速度改变命令），歌词或其他文本，以及其他指示符（例如音轨结束）。

`MetaEventListener`机制类似于`ControllerEventListener`。在任何类中实现`MetaEventListener`接口，当序列器处理`MetaMessage`时需要通知其实例。这涉及到类中添加以下方法：

```
void meta(MetaMessage msg)

```

您通过将其作为参数传递给`Sequencer addMetaEventListener`方法来注册此类的实例：

```
boolean b = sequencer.addMetaEventListener
        (myMetaListener);

```

这与`ControllerEventListener`接口采用的方法略有不同，因为您必须注册才能接收所有`MetaMessages,`，而不仅仅是所选择的`MetaMessages,`。如果定序器在其序列中遇到`MetaMessage`，它将调用`myMetaListener.meta`，并将遇到的`MetaMessage`传递给它。 `meta`方法可以在其`MetaMessage`参数上调用`getType`，以获得 0 到 127 之间的整数，表示消息类型，由标准 MIDI 文件 1.0 规范定义。