文档

Java™ 教程
隐藏目录
访问音频系统资源
路径:声音

访问音频系统资源

Java音频API采用了一种灵活的系统配置方法。计算机上可以安装不同类型的音频设备(混音器)。API对已安装的设备及其功能几乎没有假设。相反,它提供了系统报告可用音频组件的方法,并为您的程序访问这些组件提供了途径。

下面的章节展示了您的程序如何了解计算机上已安装的采样音频资源,以及如何访问可用资源。资源包括混音器和混音器拥有的各种类型的线路。

AudioSystem类

AudioSystem类作为音频组件的集散地,包括内置服务和来自第三方提供者的单独安装的服务。 AudioSystem作为应用程序访问这些已安装的采样音频资源的入口点。您可以查询AudioSystem了解已安装的资源类型,然后获取对它们的访问权限。例如,应用程序可以首先询问AudioSystem类是否有具有某种配置的混音器,例如在线路讨论中先前介绍的输入或输出配置之一。然后,程序将从混音器获取数据线路,等等。

以下是应用程序可以从AudioSystem获得的一些资源:

信息对象

Java音频API中的几个类提供有关关联接口的有用信息。例如,Mixer.Info提供有关已安装混音器的详细信息,如混音器的供应商、名称、描述和版本。 Line.Info获取特定线路的类。 Line.Info的子类包括Port.InfoDataLine.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方法:

static Line getLine(Line.Info info)

这类似于先前讨论的getMixer方法。与Mixer.Info不同,用作参数的Line.Info不存储用于指定所需行的文本信息。相反,它存储有关所需行类的信息。

Line.Info是一个抽象类,因此您可以使用其子类之一(Port.InfoDataLine.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对象,除了其类和音频格式之外,没有指定任何其他属性。您可以使用类似的代码来获取其他类型的行。对于SourceDataLineClip,只需将该类替换为TargetDataLine作为行变量的类,并在DataLine.Info构造函数的第一个参数中也替换为该类。

对于Port,您可以在以下代码中使用Port.Info的静态实例:

if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
    try {
        line = (Port) AudioSystem.getLine(
            Port.Info.MICROPHONE);
    }
}

注意使用方法isLineSupported来检查混音器是否具有所需类型的线路。

请记住,源线是混音器的输入,即如果混音器表示音频输入设备,则是Port对象;如果混音器表示音频输出设备,则是SourceDataLineClip对象。同样,目标线是混音器的输出:音频输出混音器是Port对象,音频输入混音器是TargetDataLine对象。那么如果混音器根本没有连接到任何外部硬件设备怎么办?例如,考虑一种从应用程序获取音频并将混合后的音频传递回应用程序的内部或仅软件的混音器。这种混音器的输入线路是SourceDataLineClip对象,输出线路是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对象的数组。获得数组后,您可以遍历数组,在每个对象上调用MixergetLine方法以获取每个线路,然后调用Lineopen方法为程序保留使用每个线路的权限。

选择输入和输出端口

前面的部分关于如何获取所需类型的线路,同样适用于端口。您可以通过将Port.Info对象传递给AudioSystem(或Mixer)的getSourceLineInfogetTargetLineInfo方法(接受Line.Info参数)来获取所有源(即输入)和目标(即输出)端口。然后,您可以遍历返回的对象数组,并调用Mixer的getLine方法获取每个端口。

然后,您可以通过调用Lineopen方法来打开每个Port。打开端口意味着您将其打开,也就是说,您允许声音通过端口进入或离开。同样,您可以关闭您不想声音通过的端口,因为有些端口可能在您获得它们之前已经打开。某些平台默认打开所有端口;或者用户或系统管理员可能使用另一个应用程序或操作系统软件选择打开或关闭某些端口。

警告:如果您想选择某个特定的端口并确保声音实际上正在通过该端口进入或离开,您可以按照上述描述打开该端口。然而,这可能被视为对用户不友好的行为!例如,用户可能已经关闭了扬声器端口,以免打扰她的同事。如果您的程序突然覆盖了她的意愿并开始播放音乐,她可能会非常生气。另一个例子是,用户可能希望确保计算机的麦克风不会在他不知情的情况下被打开,以避免窃听。一般来说,除非您的程序响应用户通过用户界面表达的意图,否则不建议打开或关闭端口。相反,请尊重用户或操作系统已经选择的设置。

在混音器正确工作之前,没有必要打开或关闭端口。例如,您可以开始将声音播放到音频输出混音器中,即使其所有输出端口都关闭。数据仍然流入混音器;播放不会被阻塞。用户只是听不到任何声音。只要用户打开一个输出端口,声音将通过该端口听到,从媒体播放已经达到的任何点开始。

此外,您不需要访问端口来了解混音器是否具有某些端口。例如,要了解某个混音器是否实际上是音频输出混音器,您可以调用getTargetLineInfo来查看它是否具有输出端口。除非您想更改端口的设置(例如其打开或关闭状态,或者它们可能具有的任何控件的设置),否则没有理由访问端口本身。

使用音频资源的权限

Java Sound API包括一个AudioPermission类,用于指示小程序(或在安全管理器下运行的应用程序)对采样音频系统具有哪些访问权限。对录制声音的权限是单独控制的。应谨慎授予此权限,以防止未经授权的窃听等安全风险。默认情况下,小程序和应用程序被授予以下权限:

一般来说,applet在安全管理器的监控下运行,不被允许录制声音。另一方面,应用程序不会自动安装安全管理器,因此能够录制声音。(然而,如果对于应用程序明确调用默认安全管理器,应用程序将不被允许录制声音。)

只要applets和应用程序被明确授权,即使在使用安全管理器运行时,它们也可以录制声音。

如果您的程序没有录制(或播放)声音的权限,在尝试打开线路时会抛出异常。在程序中无法解决此问题,除非捕获异常并向用户报告问题,因为权限不能通过API进行更改。(如果可以更改,它们将毫无意义,因为没有什么是安全的!)通常,权限在一个或多个策略配置文件中进行设置,用户或系统管理员可以使用文本编辑器或策略工具程序进行编辑。


上一页: Sampled Package 概述
下一页: 播放音频