Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明,了解所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息。
Java音频API采用了一种灵活的系统配置方法。计算机上可以安装不同类型的音频设备(混音器)。API对已安装的设备及其功能几乎没有假设。相反,它提供了系统报告可用音频组件的方法,并为您的程序访问这些组件提供了途径。
下面的章节展示了您的程序如何了解计算机上已安装的采样音频资源,以及如何访问可用资源。资源包括混音器和混音器拥有的各种类型的线路。
AudioSystem
类作为音频组件的集散地,包括内置服务和来自第三方提供者的单独安装的服务。 AudioSystem
作为应用程序访问这些已安装的采样音频资源的入口点。您可以查询AudioSystem
了解已安装的资源类型,然后获取对它们的访问权限。例如,应用程序可以首先询问AudioSystem
类是否有具有某种配置的混音器,例如在线路讨论中先前介绍的输入或输出配置之一。然后,程序将从混音器获取数据线路,等等。
以下是应用程序可以从AudioSystem
获得的一些资源:
AudioSystem
类提供了所有已安装混音器的列表。AudioSystem
获取线路,而无需明确处理混音器。AudioSystem
类提供了在音频文件和音频流之间进行转换的方法。它还可以报告音频文件的文件格式,并以不同的格式写入文件。Java音频API中的几个类提供有关关联接口的有用信息。例如,Mixer.Info
提供有关已安装混音器的详细信息,如混音器的供应商、名称、描述和版本。 Line.Info
获取特定线路的类。 Line.Info
的子类包括Port.Info
和DataLine.Info
,分别获取与特定端口和数据线相关的详细信息。每个类在下面的适当部分中进一步描述。重要的是不要混淆描述的Info
对象和它所描述的混音器或线路对象。
通常,使用Java音频API的程序需要首先获取一个音频混音器,或者至少获取一个混音器的一条线路,以便您可以将声音输入或输出到计算机中。您的程序可能需要特定类型的混音器,或者您可能想显示所有可用混音器的列表,以便用户可以选择一个。无论哪种情况,您都需要了解已安装的混音器类型。AudioSystem
提供了以下方法:
static Mixer.Info[] getMixerInfo()
该方法返回的每个Mixer.Info
对象标识已安装的一种混音器类型。(通常,系统最多只有一个给定类型的混音器。如果恰好有多个给定类型的混音器,则返回的数组仍然只有一个该类型的Mixer.Info
。)应用程序可以遍历Mixer.Info
对象,根据其需求找到合适的混音器。Mixer.Info
包括以下字符串以标识混音器类型:
这些是任意的字符串,因此需要特定混音器的应用程序必须知道预期结果,并知道将字符串与之进行比较。提供混音器的公司应在其文档中包含此信息。或者,更常见的是,应用程序将显示所有Mixer.Info
对象的字符串给用户,并让用户选择相应的混音器。
找到合适的混音器后,应用程序调用以下AudioSystem
方法获取所需的混音器:
static Mixer getMixer(Mixer.Info info)
如果您的程序需要具有某些功能的混音器,但不需要特定供应商制造的混音器呢?如果不能依赖用户知道应该选择哪个混音器呢?在这种情况下,Mixer.Info
对象中的信息将没有太大用处。相反,您可以通过调用getMixerInfo
返回的所有Mixer.Info
对象进行迭代,通过调用getMixer
获取每个混音器,并查询每个混音器的功能。例如,您可能需要一个可以同时将混合音频数据写入特定数量目标数据线的混音器。在这种情况下,您将使用此Mixer
方法查询每个混音器:
int getMaxLines(Line.Info info)
在这里,Line.Info
将指定一个TargetDataLine
。关于Line.Info
类的讨论将在下一节中进行。
有两种方法可以获取一行:
AudioSystem
对象获取AudioSystem
对象获取的混音器获取 假设您尚未获得混音器,并且您的程序只需要某种特定类型的行;混音器的详细信息对您并不重要。您可以使用AudioSystem
方法:
static Line getLine(Line.Info info)
这类似于先前讨论的getMixer
方法。与Mixer.Info
不同,用作参数的Line.Info
不存储用于指定所需行的文本信息。相反,它存储有关所需行类的信息。
Line.Info
是一个抽象类,因此您可以使用其子类之一(Port.Info
或DataLine.Info
)来获取一行。以下代码摘录使用DataLine.Info
子类获取和打开目标数据行:
TargetDataLine line; DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); // format是一个AudioFormat对象 if (!AudioSystem.isLineSupported(info)) { // 处理错误。 } // 获取并打开行。 try { line = (TargetDataLine) AudioSystem.getLine(info); line.open(format); } catch (LineUnavailableException ex) { // 处理错误。 //... }
此代码获取一个TargetDataLine
对象,除了其类和音频格式之外,没有指定任何其他属性。您可以使用类似的代码来获取其他类型的行。对于SourceDataLine
或Clip
,只需将该类替换为TargetDataLine
作为行变量的类,并在DataLine.Info
构造函数的第一个参数中也替换为该类。
对于Port
,您可以在以下代码中使用Port.Info
的静态实例:
if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) { try { line = (Port) AudioSystem.getLine( Port.Info.MICROPHONE); } }
注意使用方法isLineSupported
来检查混音器是否具有所需类型的线路。
请记住,源线是混音器的输入,即如果混音器表示音频输入设备,则是Port
对象;如果混音器表示音频输出设备,则是SourceDataLine
或Clip
对象。同样,目标线是混音器的输出:音频输出混音器是Port
对象,音频输入混音器是TargetDataLine
对象。那么如果混音器根本没有连接到任何外部硬件设备怎么办?例如,考虑一种从应用程序获取音频并将混合后的音频传递回应用程序的内部或仅软件的混音器。这种混音器的输入线路是SourceDataLine
或Clip
对象,输出线路是TargetDataLine
对象。
您还可以使用以下AudioSystem
方法了解任何已安装混音器所支持的特定类型的源线和目标线:
static Line.Info[] getSourceLineInfo(Line.Info info) static Line.Info[] getTargetLineInfo(Line.Info info)
请注意,这些方法返回的数组表示唯一类型的线路,而不一定是所有线路。例如,如果混音器的两条线路或两个不同混音器的线路具有相同的Line.Info
对象,则这两条线路将在返回的数组中由一个Line.Info
表示。
Mixer
接口包括上述源线和目标线的AudioSystem
访问方法的变体。这些Mixer
方法包括接受Line.Info
参数的方法,就像AudioSystem
的方法一样。然而,Mixer
还包括这些没有参数的变体:
Line.Info[] getSourceLineInfo() Line.Info[] getTargetLineInfo()
这些方法返回特定混音器的所有Line.Info
对象的数组。获得数组后,您可以遍历数组,在每个对象上调用Mixer
的getLine
方法以获取每个线路,然后调用Line
的open
方法为程序保留使用每个线路的权限。
前面的部分关于如何获取所需类型的线路,同样适用于端口。您可以通过将Port.Info
对象传递给AudioSystem
(或Mixer
)的getSourceLineInfo
和getTargetLineInfo
方法(接受Line.Info
参数)来获取所有源(即输入)和目标(即输出)端口。然后,您可以遍历返回的对象数组,并调用Mixer的getLine
方法获取每个端口。
然后,您可以通过调用Line
的open
方法来打开每个Port
。打开端口意味着您将其打开,也就是说,您允许声音通过端口进入或离开。同样,您可以关闭您不想声音通过的端口,因为有些端口可能在您获得它们之前已经打开。某些平台默认打开所有端口;或者用户或系统管理员可能使用另一个应用程序或操作系统软件选择打开或关闭某些端口。
警告:如果您想选择某个特定的端口并确保声音实际上正在通过该端口进入或离开,您可以按照上述描述打开该端口。然而,这可能被视为对用户不友好的行为!例如,用户可能已经关闭了扬声器端口,以免打扰她的同事。如果您的程序突然覆盖了她的意愿并开始播放音乐,她可能会非常生气。另一个例子是,用户可能希望确保计算机的麦克风不会在他不知情的情况下被打开,以避免窃听。一般来说,除非您的程序响应用户通过用户界面表达的意图,否则不建议打开或关闭端口。相反,请尊重用户或操作系统已经选择的设置。
在混音器正确工作之前,没有必要打开或关闭端口。例如,您可以开始将声音播放到音频输出混音器中,即使其所有输出端口都关闭。数据仍然流入混音器;播放不会被阻塞。用户只是听不到任何声音。只要用户打开一个输出端口,声音将通过该端口听到,从媒体播放已经达到的任何点开始。
此外,您不需要访问端口来了解混音器是否具有某些端口。例如,要了解某个混音器是否实际上是音频输出混音器,您可以调用getTargetLineInfo
来查看它是否具有输出端口。除非您想更改端口的设置(例如其打开或关闭状态,或者它们可能具有的任何控件的设置),否则没有理由访问端口本身。
Java Sound API包括一个AudioPermission
类,用于指示小程序(或在安全管理器下运行的应用程序)对采样音频系统具有哪些访问权限。对录制声音的权限是单独控制的。应谨慎授予此权限,以防止未经授权的窃听等安全风险。默认情况下,小程序和应用程序被授予以下权限:
一般来说,applet在安全管理器的监控下运行,不被允许录制声音。另一方面,应用程序不会自动安装安全管理器,因此能够录制声音。(然而,如果对于应用程序明确调用默认安全管理器,应用程序将不被允许录制声音。)
只要applets和应用程序被明确授权,即使在使用安全管理器运行时,它们也可以录制声音。
如果您的程序没有录制(或播放)声音的权限,在尝试打开线路时会抛出异常。在程序中无法解决此问题,除非捕获异常并向用户报告问题,因为权限不能通过API进行更改。(如果可以更改,它们将毫无意义,因为没有什么是安全的!)通常,权限在一个或多个策略配置文件中进行设置,用户或系统管理员可以使用文本编辑器或策略工具程序进行编辑。