使用 Sequencer 方法
原文: https://docs.oracle.com/javase/tutorial/sound/MIDI-seq-methods.html
Sequencer
接口提供以下几种方法:
- 从 MIDI 文件或
Sequence
对象加载序列数据,并将当前加载的序列数据保存到 MIDI 文件的方法。 - 类似于录音机的传输功能的方法,用于停止和开始播放和录制,启用和禁用特定轨道上的录制,以及在
Sequence
中穿梭当前播放或录制位置。 - 用于查询和设置对象的同步和定时参数的高级方法。
Sequencer
可以以不同的速度播放,其中一些Tracks
静音,并且与其他对象处于各种同步状态。 - 用于注册“监听器”对象的高级方法,这些对象在
Sequencer
处理某些类型的 MIDI 事件时得到通知。
无论您调用哪种Sequencer
方法,第一步是从系统中获取Sequencer
设备并保留它以供程序使用。
获取序列发生器
应用程序不实例化Sequencer
;毕竟,Sequencer
只是一个接口。相反,与 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 文件中读取序列数据
- 通过接收来自其他设备(如 MIDI 输入端口)的 MIDI 信息实时录制
- 通过向空序列添加轨道并将
MidiEvent
对象添加到这些轨道,以“从头开始”以编程方式构建它
我们现在看一下获取序列数据的第一种方法。 (另外两种方式分别在记录和保存序列和编辑序列中描述。)第一种方法实际上包含两种稍微不同的方法。一种方法是将 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 数据捕获到Sequence
,然后再捕获到文件,您需要执行除上述步骤之外的其他一些步骤。以下概述显示了设置录制到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
参数指定 resolution 参数的单位。在这种情况下,我们已经指定我们创建的Sequence
的定时分辨率将是每季度 10 个脉冲。Sequence
构造器的另一个可选参数是多个轨道参数,这将导致初始序列以指定数量的(最初为空)Tracks
开始。否则将创建Sequence
而没有初始Tracks
;可以根据需要稍后添加它们。- 使用
Sequence.createTrack
在Sequence
中创建一个空Track
。如果使用初始Tracks
创建Sequence
,则不需要此步骤。 - 使用
Sequencer.setSequence
,选择我们的新Sequence
接收录音。setSequence
方法将现有的Sequence
与Sequencer
连接在一起,这有点类似于将磁带装入磁带录音机。 - 为每个
Track
调用Sequencer.recordEnable
进行记录。如有必要,通过调用Sequence.getTracks
获取Sequence
中可用Tracks
的参考。 - 调用
Sequencer
上的startRecording
。 - 完成录制后,调用
Sequencer.stop
或Sequencer.stopRecording
。 - 用
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
。
结合使用,这些低级方法为全功能音序器程序所需的编辑功能提供了基础。