【Java Sound】(二)访问音频系统资源
Java Sound
摘自:The Java™ Tutorials,翻译为机翻+少量修正
(二)访问音频系统资源
Java Sound API采用了灵活的系统配置方法。可以在计算机上安装不同种类的音频设备(mixers)。该API没怎么设想(makes few assumptions?)有什么已安装的设备以及其有什么功能。相反,它提供了让系统报告可用音频组件的方法以及让程序访问它们的方法。
以下各节说明你的程序如何了解计算机上已安装了哪些采样音频资源,以及如何获取对可用资源的访问。其中,资源包括混合器和混合器所拥有的各种类型的线路。
AudioSystem类
AudioSystem
类充当音频组件(audio components)的交流中心,音频组件包括来自第三方供应商内置服务和单独安装的服务。AudioSystem
用作访问这些已安装的采样音频资源的应用程序的入口点。您可以查询AudioSystem
来了解已安装了哪些资源,然后可以访问它们。例如,应用程序可能会通过询问AudioSystem
类是否存在具有特定配置的混合器来开始,例如在前面的讨论中已说明的输入或输出配置之一。然后,程序将从混合器中获取数据线,依此类推。
以下是应用程序可以从AudioSystem
中获得的一些资源:
- Mixers,混音器——系统通常安装了多个混音器。通常至少有一个用于音频输入,一个用于音频输出。可能还有一些混音器没有I / O端口,而是接受来自应用程序的音频,并将混合的音频传递回该程序。本
AudioSystem
类提供所有已安装的混音器的列表。 - Lines,线路——即使每条线路都与一个混合器相关联,应用程序也可以直接从
AudioSystem
中获得一条线路,而无需明确地和混音器打交道。 - Format conversions,格式转换——应用程序可以使用格式转换将音频数据从一种格式转换为另一种格式。
- Files and streams,文件和流——
AudioSystem
类提供在音频文件和音频流之间进行转换的方法。它还可以报告声音文件的文件格式,并可以以不同的格式写入文件。
Information Objects,信息对象
Java Sound API中的几个类提供了关于相关接口的有用信息。例如, Mixer.Info
提供有关已安装的混音器的详细信息,例如混音器的供应商,名称,描述和版本。 Line.Info
获取特定线路的类。Line.Info
的子类包括 Port.Info
和DataLine.Info
,分别获取与特定端口和数据线路有关的详细信息。这些类中的每一个都将在下面的相应部分中进行进一步说明。重要的是不要将Info
对象与它所描述的混音器或线路对象混淆。
Getting a Mixer,获取一个混音器
通常,使用Java Sound API的程序需要做的第一件事是获取一个混音器,或者至少是一个混音器的一条线路,以便您可以将声音传入或传出计算机。您的程序可能需要一个特定类型的混音器,或者您可能希望显示所有可用混音器的列表,以便用户可以选择一个。无论哪种情况,您都需要了解安装了哪些类型的混音器。AudioSystem
提供以下方法:
static Mixer.Info[] getMixerInfo()
此方法返回的每个Mixer.Info
对象都标识一种已安装的混音器。(通常,系统最多只有一个给定类型的混合器。如果碰巧有不止一种给定类型的混合器,则返回的数组仍然只有Mixer.Info
该类型的一个。)应用程序可以遍历Mixer.Info
对象,根据需要来找到一个合适的混音器。Mixer.Info
包括以下字符串,以确定一种混音器:
- Name,名称
- Version,版本
- Vendor,供应商
- Description,描述
这些是可以随意设定内容的字符串,因此需要特定混合器的应用程序必须知道期望的内容,和要去比对内容的字符串(难翻译, 给原文:what to expect and what to compare the strings to.)。提供混音器的公司应在其文档中包含此信息。替代地,并且可能更典型的情况是,应用程序将向用户显示所有Mixer.Info
对象的字符串,并让用户选择相应的混合器。
找到合适的混合器后,应用程序将调用以下AudioSystem
的方法以获得所需的混音器Mixer:
static Mixer getMixer(Mixer.Info info)
如果您的程序需要具有某些功能的混音器,而不需要特定供应商生产的特定混音器,该怎么办?而且,如果您不认为用户知道应该选择哪种混音器的时候呢?在这种情况下,Mixer.Info
对象中的信息将没有太大用处。相反,您可以遍历getMixerInfo
返回的所有Mixer.Info
对象,通过调用getMixer
为每个Info
对象获取一个混合器,并向每个混合器查询其功能。例如,您可能需要一个混音器,它可以将其混合音频数据同时写入到一定数量的目标数据线。在这种情况下,您将使用此Mixer
的方法查询每个混音器:
int getMaxLines(Line.Info info)
在这里, Line.Info
将指定一个TargetDataLine
。的Line.Info
类将在下节讨论。
获取所需类型的线路line
有两种方法可以获得一条line线路:
- 直接从
AudioSystem
对象中获得 - 从您已经从
AudioSystem
对象获得的混合器中获得
直接从AudioSystem获得线路
假设您尚未获得混合器,并且您的程序是一个简单的程序,实际上只需要某种类型的线路line;调音台的详细信息对您而言并不重要。您可以使用以下AudioSystem
的方法:
static Line getLine(Line.Info info)
与前面讨论的getMixer
方法类似。与Mixer.Info
不同的是 , 用作参数的Line.Info
不会存储文本信息来指定所需的行。相反,它存储有关所需线路类的信息。
Line.Info
是一个抽象类,因此您可以使用其子类之一( Port.Info
或 DataLine.Info
)获取一条线路line。以下的节选代码使用DataLine.Info
子类获取并打开目标数据线路:
TargetDataLine line;
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
format); // format是一个AudioFormat类型的对象
if (!AudioSystem.isLineSupported(info)) {
// 处理错误.
}
try { // 获取并打开线路line.
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
} catch (LineUnavailableException ex) {
// 处理错误.
//...
}
此代码获取一个 TargetDataLine
对象,而不指定其类和音频格式以外的任何属性。您可以使用类似的代码来获取其他类型的行。对于SourceDataLine
类或Clip
类,只需将line变量的TargetDataLine
类替换为SourceDataLine
或Clip
,并在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)
请注意,这些方法中的每一个返回的数组元素都表示线路的唯一种类,而不一定是所有的线路。(不太会翻译,给原文:Note that the array returned by each of these methods indicates unique types of lines, not necessarily all the lines.)例如,如果混音器的两条线或两条不同混音器的线具有相同的Line.Info
对象,则这两条线在返回的数组中将仅由一个Line.Info
表示。
从混音器Mixer获得线路
Mixer
接口包括上述AudioSystem
用于访问源线和目标线的方法的变体。这些Mixer
方法包括采用Line.Info
作为参数的方法,就像AudioSystem
的方法一样。但是,Mixer
还包括以下变体,它们不带任何参数:
Line.Info[] getSourceLineInfo()
Line.Info[] getTargetLineInfo()
这些方法返回特定混合器的所有Line.Info
对象的数组。一旦获得数组,就可以遍历它们,调用Mixer
的 getLine
方法来获取每一条线路,然后调用Line
的 open
方法来在你的程序中使用各条线路。
选择输入和输出端口
上一节有关如何获得所需类型的线的方法,也适用于端口以及其他类型的线。您可以通过将一个Port.Info
对象传递给AudioSystem
(或Mixer
)的,可以接受一个Line.Info
作为参数的getSourceLineInfo
和getTargetLineInfo
方法,来获取所有的源端口(即输入端口)和目标端口(即输出端口)。然后,您遍历返回的对象数组,并调用Mixer的getLine
方法来获取每个端口。
然后可以通过调用Line
的 open
方法打开每个Port
。打开端口意味着您打开了端口的开关——也就是说,您允许声音进入或流出端口。同样,您可以关闭不希望声音传播的端口,因为某些端口可能在您获得它们之前就已经打开。某些平台默认情况下将所有端口保留为打开状态;或者用户或系统管理员可能已使用其他应用程序或操作系统软件选择了将某些端口打开或关闭。
**警告:**如果要选择某个端口并确保声音确实正在传入或传出该端口,则可以按照说明打开该端口。但是,这可以视为对用户有害的行为!例如,用户可能会关闭扬声器端口,以免打扰她的同事。如果您的程序突然破坏了她的愿望并开始大肆歌唱,她会很不高兴。举另外一个例子,用户可能希望确保自己的计算机的麦克风永远不会在不知情的情况下打开,以避免窃听。通常,建议不要打开或关闭端口,除非您的程序响应用户的意图(通过用户界面表示)。相反,请遵守用户或操作系统已选择的设置。
所连接的混音器正常运行之前,无需打开或关闭端口。例如,即使所有输出端口都已关闭,您也可以开始将声音回放到音频输出混音器中。数据仍然会流入混合器,播放不会被阻止,用户只是什么也听不到。用户一旦打开输出端口,便会从该端口上的声音开始听到声音,该声音将从媒体播放到的某处开始。
另外,您无需访问端口即可了解混音器是否具有某些端口。例如,要了解某个混音器是否是音频输出混音器,可以调用getTargetLineInfo
以查看它是否具有输出端口。除非您要更改端口设置(例如,端口的打开或关闭状态或它们可能具有的任何控件controls的设置),否则没有理由访问端口本身。
音频资源使用许可
Java Sound API包含一个 AudioPermission
类,该类指示小程序(或运行有安全管理器的应用程序)可以对采样音频系统进行哪种访问。录制声音的权限是单独控制的。应当谨慎授予此权限,以帮助防止诸如未经授权的窃听之类的安全风险。默认情况下,小程序和应用程序被授予以下权限:
- 一个运行在小程序安全管理器下的小程序可以播放音频,但不能录制音频。
- 没有安全管理器的应用程序可以播放和录制音频。
- 使用默认安全管理器运行的应用程序可以播放但不能录制音频。
通常,小程序在安全管理器的监督下运行,并且不允许录制声音。另一方面,应用程序不会自动安装安全管理器,这样是能够录制声音的。(但是,如果为应用程序显式调用了默认安全管理器,则不允许该应用程序录制声音。)
即使已被授予显式权限,小应用程序和应用程序也可以在与安全管理器一起运行时记录声音。
如果您的程序无权录制(或播放)声音,则在尝试打开一条线路时会引发异常。除了捕获异常并将问题报告给用户以外,您在程序中无能为力,因为无法通过API来更改权限。(如果可以,那它们将毫无意义,因为这样的话将没有什么安全的方法了!)通常,权限是在一个或多个策略配置文件中设置的,用户或系统管理员可以使用文本编辑器或Policy Tool程序对其进行编辑。