使用定序器方法
Sequencerinterface提供了几种类别的方法:
-
与磁带录音机的传输功能类似的方法,用于停止和开始播放和记录,启用和禁用特定轨道上的记录以及在
Sequence
中调整当前播放或记录位置。 -
用于查询和设置对象的同步和定时参数的高级方法。
Sequencer
可能以不同的速度播放,其中某些Tracks
被静音,并且与其他对象处于各种同步状态。
无论您要调用哪种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 文件数据 Importing 到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
,如果它遇到任何麻烦。
播放音序
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 数据catch到Sequence
,然后catch到文件,您需要执行上述其他步骤。以下概述显示了设置以在Sequence
中记录到Track
的必要步骤:
-
设置 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
方法将现有的Sequence
与Sequencer
绑定在一起,这有点类似于将磁带加载到磁带 Logger 上。 -
为每个要记录的
Track
1 调用Sequencer.recordEnable
。如有必要,可以通过调用Sequence.getTracks
获得对Sequence
中可用Tracks
的引用。 -
使用
MidiSystem.write
将记录的Sequence
保存到 MIDI 文件。MidiSystem
的write
方法将Sequence
作为其参数之一,并将该Sequence
写入流或文件。
编辑序列
许多应用程序都允许通过从文件中加载序列来创建音序,还有许多应用程序也可以通过从实时 MIDIImporting(即录制)中catch音序来创建音序。但是,某些程序将需要以编程方式或响应用户 Importing 从头开始创建 MIDI 序列。功能齐全的定序器程序允许用户手动构建新序列,以及编辑现有序列。
这些数据编辑操作不是通过Sequencer
方法,而是通过数据对象本身的方法Sequence
,Track
和MidiEvent
在 Java Sound API 中实现的。您可以使用Sequence
构造函数之一创建一个空序列,然后通过调用以下Sequence
方法向其添加轨道:
Track createTrack()
如果您的程序允许用户编辑序列,则需要此Sequence
方法来删除曲目:
boolean deleteTrack(Track track)
序列中包含曲目后,您可以通过调用Track
类的方法来修改曲 Object 内容。 Track
中包含的MidiEvents
作为java.util.Vector
存储在Track
对象中,并且Track
提供了一组用于访问,添加和删除列表中事件的方法。方法add
和remove
很容易解释,可以从Track
中添加或删除指定的MidiEvent
。提供了get
方法,该方法将Track's
事件列表中的索引作为索引并返回存储在其中的MidiEvent
。此外,还有size
和tick
方法,它们分别返回轨道中MidiEvents
的数目以及轨道的持续时间,以Ticks
的总数表示。
要在将新事件添加到轨道之前创建一个新事件,您当然会使用MidiEvent
构造函数。要指定或修改事件中嵌入的 MIDI 消息,您可以调用适当的MidiMessage
子类(ShortMessage
,SysexMessage
或MetaMessage
)的setMessage
方法。要修改事件发生的时间,请调用MidiEvent.setTick
。