Java教程适用于JDK 8。本页面描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言功能摘要。
请参阅JDK发行说明,了解所有JDK版本的新功能、增强功能和已删除或弃用选项的信息。
Java Sound API提供了一个灵活的模型,用于MIDI系统配置,就像对采样音频系统的配置一样。Java Sound API的实现本身可以提供不同类型的MIDI设备,而且还可以通过服务提供者提供额外的设备,并由用户安装。您可以以这样的方式编写程序,使其对计算机上安装的特定MIDI设备做出很少的假设。相反,程序可以利用MIDI系统的默认设置,或者允许用户从当前可用的设备中进行选择。
本节展示了您的程序如何了解已安装的MIDI资源,以及如何访问所需的资源。在访问和打开设备之后,您可以将它们连接在一起,如后面在传输和接收MIDI消息中所讨论的。
Java Sound API的MIDI包中的MidiSystem
类的作用与采样音频包中的AudioSystem
类直接类似。 MidiSystem
充当访问已安装MIDI资源的中转站。
您可以查询MidiSystem
以了解安装了哪些类型的设备,然后可以遍历可用设备并获得对所需设备的访问权限。例如,应用程序可以首先询问MidiSystem
有哪些合成器可用,然后显示一个列表供用户选择。一个更简单的应用程序可能只使用系统的默认合成器。
MidiSystem
类还提供了在MIDI文件和Sequences
之间进行转换的方法。它可以报告MIDI文件的文件格式,并可以写入不同类型的文件。
应用程序可以从MidiSystem
获取以下资源:
本页面重点介绍了这四种类型的资源。其他类型的资源将在本教程的后面讨论。
使用Java Sound API的典型MIDI应用程序首先通过获取所需的设备(可能包括一个或多个序列器、合成器、输入端口或输出端口)来开始。
有一个默认的合成器设备、默认的序列器设备、默认的发送设备和默认的接收设备。后两个设备通常代表系统上可用的MIDI输入和输出端口。(在这里很容易混淆方向性。将端口的传输或接收与软件有关,而不是与连接到物理端口的任何外部物理设备有关。一个MIDI输入端口将数据从外部设备传输到Java Sound API的接收器,同样,一个MIDI输出端口从一个软件对象接收数据并将数据中继给外部设备。)
一个简单的应用程序可能只使用默认值而不探索所有已安装的设备。MidiSystem类包括以下方法用于检索默认资源:
static Sequencer getSequencer() static Synthesizer getSynthesizer() static Receiver getReceiver() static Transmitter getTransmitter()
这些方法中的前两个方法获取系统的默认序列和合成资源,它们可以代表物理设备或完全在软件中实现。getReceiver方法获取一个接收器对象,它接收发送到它的MIDI消息并将它们传递给默认的接收设备。类似地,getTransmitter方法获取一个传输器对象,它可以代表默认的发送设备向某个接收器发送MIDI消息。
与使用默认设备不同,更彻底的方法是从安装在系统上的所有设备中选择所需的设备。应用程序可以以编程方式选择所需的设备,也可以显示一个可用设备列表,并让用户选择要使用的设备。MidiSystem类提供了一个方法来了解安装了哪些设备,并提供了一个相应的方法来获取给定类型的设备。
static MidiDevice.Info[] getMidiDeviceInfo()
如你所见,它返回一个信息对象的数组。这些返回的MidiDevice.Info对象中的每一个都标识了一种安装的序列器、合成器、端口或其他设备类型。(通常一个系统最多只有一个给定类型的实例。例如,一个特定厂商的合成器型号只会安装一次。)MidiDevice.Info包括以下字符串来描述设备:
您可以在用户界面中显示这些字符串,以让用户从设备列表中进行选择。
然而,要在编程中使用这些字符串来选择设备(而不是向用户显示字符串),您需要事先知道可能的选择。每个提供设备的公司应该在其文档中包含此信息。需要或优先选择特定设备的应用程序可以使用此信息来定位该设备。这种方法的缺点是将程序限制为事先已知的设备实现。
另一种更通用的方法是继续遍历MidiDevice.Info
对象,获取每个相应的设备,并在程序中确定其是否适合使用(或至少适合包含在用户可以选择的列表中)。下一节描述了如何实现此操作。
一旦找到适当的设备信息对象,应用程序可以调用以下MidiSystem
方法来获取相应的设备本身:
static MidiDevice getMidiDevice(MidiDevice.Info info)
如果您已经找到描述所需设备的信息对象,则可以使用此方法。但是,如果您无法解释getMidiDeviceInfo
返回的信息对象以确定您需要的设备,且不想向用户显示所有设备的信息,则可以尝试以下操作:遍历getMidiDeviceInfo
返回的所有MidiDevice.Info
对象,使用上述方法获取相应的设备,并测试每个设备以查看其是否适合。换句话说,您可以在将设备包含在显示给用户的列表中之前,查询每个设备的类和功能,或者作为在不涉及用户的情况下以编程方式决定设备的一种方式。例如,如果您的程序需要一个合成器,您可以获取所有已安装的设备,查看哪些是实现了Synthesizer
接口的类的实例,然后将它们显示在用户可以选择的列表中,如下所示:
// 获取所有已安装合成器的信息。 Vector synthInfos; MidiDevice device; MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); for (int i = 0; i < infos.length; i++) { try { device = MidiSystem.getMidiDevice(infos[i]); } catch (MidiUnavailableException e) { // 处理或抛出异常... } if (device instanceof Synthesizer) { synthInfos.add(infos[i]); } } // 现在,在GUI中显示synthInfos列表中的字符串。
作为另一个示例,您可以以编程方式选择设备,而不涉及用户。假设您想要获取能够同时播放最多音符的合成器。您可以按照上述方式遍历所有的MidiDevice.Info
对象,但在确定设备为合成器后,通过调用Synthesizer
的getMaxPolyphony
方法查询其功能。您可以保留具有最大多音性的合成器,如下一节所述。即使您不要求用户选择合成器,您可能仍然会显示所选MidiDevice.Info
对象的字符串,以供用户参考。
上一节介绍了如何获取已安装的设备。然而,设备可能已安装但不可用。例如,另一个应用程序可能独占使用它。要为您的程序实际保留设备,您需要使用MidiDevice
的open
方法:
if (!(device.isOpen())) { try { device.open(); } catch (MidiUnavailableException e) { // 处理或抛出异常... } }
一旦您访问并打开了设备,您可能希望将其连接到一个或多个其他设备,以便让MIDI数据在它们之间流动。这个过程将在后面的传输和接收MIDI消息中描述。