使用 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。
结合使用,这些低级方法为全功能音序器程序所需的编辑功能提供了基础。
