访问音频系统资源

原文: https://docs.oracle.com/javase/tutorial/sound/accessing.html

Java Sound API 采用灵活的方法进行系统配置。可以在计算机上安装不同种类的音频设备(混音器)。 API 几乎没有假设已安装的设备及其功能。相反,它提供了系统报告可用音频组件的方法,以及程序访问它们的方法。

以下部分显示了您的程序如何了解计算机上安装的采样音频资源,以及如何访问可用资源。除其他外,资源包括混合器和混合器拥有的各种类型的线路。

AudioSystem 类

AudioSystem 类充当音频组件的交换所,包括内置服务和来自第三方提供商的单独安装的服务。 AudioSystem作为应用程序的入口点,用于访问这些已安装的采样音频资源。您可以查询AudioSystem以了解已安装的资源类型,然后您可以获取对它们的访问权限。例如,应用程序可能首先询问AudioSystem类是否存在具有特定配置的混频器,例如前面讨论线路中所示的输入或输出配置之一。然后,从混合器,程序将获得数据线,等等。

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

  • 混合器 - 系统通常安装多个混合器。通常至少有一个用于音频输入,一个用于音频输出。可能还有混音器没有 I / O 端口,而是接受来自应用程序的音频并将混合音频传送回程序。 AudioSystem类提供所有已安装混音器的列表。
  • 行 - 即使每行都与混音器相关联,应用程序也可以直接从AudioSystem获取一行,而无需明确处理混音器。
  • 格式转换 - 应用程序可以使用格式转换将音频数据从一种格式转换为另一种格式。
  • 文件和流 - AudioSystem类提供了在音频文件和音频流之间进行转换的方法。它还可以报告声音文件的文件格式,并可以以不同的格式写入文件。

信息对象

Java Sound API 中的几个类提供了有关相关接口的有用信息。例如, Mixer.Info 提供有关已安装的混音器的详细信息,例如调音台的供应商,名称,说明和版本。 Line.Info 获取特定行的类。 Line.Info的子类包括 Port.InfoDataLine.Info ,它们分别获得与特定端口和数据线相关的细节。这些类中的每一个都在下面的适当部分中进一步描述。重要的是不要将Info对象与它描述的混音器或线对象混淆。

得到一个混音器

通常,使用 Java Sound API 的程序需要做的第一件事就是获得一个混音器,或至少一行混音器,以便您可以将声音输入或输出计算机。您的程序可能需要特定类型的混音器,或者您可能希望显示所有可用混音器的列表,以便用户可以选择一个。在任何一种情况下,您都需要了解安装了哪种混合器。 AudioSystem提供以下方法:

  1. static Mixer.Info[] getMixerInfo()

此方法返回的每个 Mixer.Info 对象都标识了一种安装的混音器。 (通常系统最多只有一个给定类型的混合器。如果碰巧有多个给定类型,返回的数组仍然只有一个Mixer.Info用于该类型。)应用程序可以迭代Mixer.Info根据需要找到合适的对象。 Mixer.Info包含以下字符串以标识混音器的类型:

  • 名称
  • 供应商
  • 描述

这些是任意字符串,因此需要特定混音器的应用程序必须知道期望什么以及比较字符串的内容。提供调音台的公司应在其文档中包含此信息。或者,也许更典型的是,应用程序将向用户显示所有Mixer.Info对象的字符串,并让用户选择相应的混音器。

一旦找到合适的混音器,应用程序就会调用以下AudioSystem方法来获得所需的混音器:

  1. static Mixer getMixer(Mixer.Info info)

如果您的程序需要具有某些功能的混音器,但它不需要特定供应商生产的特定混音器,该怎么办?如果你不能依赖于用户知道应该选择哪个调音台怎么办?在这种情况下,Mixer.Info对象中的信息将没有多大用处。相反,您可以迭代getMixerInfo返回的所有Mixer.Info对象,通过调用getMixer为每个对象获取混音器,并查询每个混音器的功能。例如,您可能需要一台混音器,可以将其混合音频数据同时写入一定数量的目标数据线。在这种情况下,您将使用此Mixer方法查询每个混音器:

  1. int getMaxLines(Line.Info info)

这里, Line.Info 指定TargetDataLineLine.Info类将在下一节中讨论。

获得所需类型的线

获得一条线有两种方法:

  • 直接来自 AudioSystem 对象
  • 从已从AudioSystem对象获得的混音器

直接从 AudioSystem 获取一条线

我们假设你没有得到一个混音器,你的程序是一个简单的程序,真的只需要某种行;调音台的细节对你来说无关紧要。您可以使用AudioSystem方法:

  1. static Line getLine(Line.Info info)

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

Line.Info是一个抽象类,所以你使用它的一个子类( Port.InfoDataLine.Info )来获得一条线。以下代码摘录使用DataLine.Info子类来获取和打开目标数据行:

  1. TargetDataLine line;
  2. DataLine.Info info = new DataLine.Info(TargetDataLine.class,
  3. format); // format is an AudioFormat object
  4. if (!AudioSystem.isLineSupported(info)) {
  5. // Handle the error.
  6. }
  7. // Obtain and open the line.
  8. try {
  9. line = (TargetDataLine) AudioSystem.getLine(info);
  10. line.open(format);
  11. } catch (LineUnavailableException ex) {
  12. // Handle the error.
  13. //...
  14. }

此代码获取 TargetDataLine 对象,而不指定除其类及其音频格式之外的任何属性。您可以使用类似代码来获取其他类型的行。对于SourceDataLineClip,只需将TargetDataLine的类替换为行变量的类,也替换为DataLine.Info构造器的第一个参数。

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

  1. if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
  2. try {
  3. line = (Port) AudioSystem.getLine(
  4. Port.Info.MICROPHONE);
  5. }
  6. }

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

回想一下,源线是混音器的输入,即,如果混音器代表音频输入设备,则为Port对象;如果混音器代表音频输入设备,则为SourceDataLineClip对象音频输出设备。类似地,目标线是混合器的输出:用于音频输出混合器的Port对象和用于音频输入混合器的TargetDataLine对象。如果调音台根本没有连接到任何外部硬件设备怎么办?例如,考虑一个内部或仅软件混音器,它从应用程序获取音频并将其混合音频传回程序。这种混频器的输入线为SourceDataLineClip对象,输出线为TargetDataLine对象。

您还可以使用以下AudioSystem方法了解有关任何已安装的混音器支持的指定类型的源和目标行的更多信息:

  1. static Line.Info[] getSourceLineInfo(Line.Info info)
  2. static Line.Info[] getTargetLineInfo(Line.Info info)

请注意,每种方法返回的数组都表示唯一类型的行,不一定是所有行。例如,如果两个混音器的行或两行不同的混音器具有相同的Line.Info对象,则两行将仅由返回数组中的一个Line.Info表示。

从调音台获取一条线

Mixer接口包括上述源线和目标线的AudioSystem访问方法的变体。这些Mixer方法包括采用Line.Info参数的方法,就像AudioSystem's方法一样。但是,Mixer还包括这些不带参数的变体:

  1. Line.Info[] getSourceLineInfo()
  2. Line.Info[] getTargetLineInfo()

这些方法返回特定混频器的所有Line.Info对象的数组。获得数组后,可以迭代它们,调用Mixer's getLine方法获取每一行,然后按Line's open方法保留对程序使用每一行。

选择输入和输出端口

上一节,关于如何获得所需类型的行,适用于端口以及其他类型的行。您可以通过将Port.Info对象传递给AudioSystem(或Mixer)方法getSourceLineInfogetTargetLineInfo来获取所有源(即输入)和目标(即输出)端口 COD5]论点。然后迭代返回的对象数组并调用 Mixer 的getLine方法来获取每个端口。

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

警告:如果要选择某个端口并确保声音实际进出端口,可以按照说明打开端口。但是,这可以被视为用户敌对行为!例如,用户可能关闭扬声器端口,以免打扰她的同事。如果你的节目突然超出了她的意愿并开始发出声音,她会非常沮丧。作为另一个例子,用户可能想要确保他的计算机的麦克风在他不知情的情况下从未打开,以避免窃听。通常,建议不要打开或关闭端口,除非您的程序响应用户的意图,如通过用户界面表达的那样。而是,尊重用户或操作系统已选择的设置。

没有必要在它所连接的调音台正常工作之前打开或关闭一个端口。例如,即使所有输出端口都关闭,您也可以开始将声音播放到音频输出混音器中。数据仍然流入混音器;播放未被阻止。用户根本听不到任何声音。一旦用户打开输出端口,声音就会通过该端口发出声音,从播放已经到达的媒体中的任何位置开始。

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

使用音频资源的权限

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

  • 与 applet 安全管理器一起运行的 applet 可以播放音频,但不能录制音频。
  • 没有安全管理器运行的应用程序可以播放和录制音频。
  • 使用默认安全管理器运行的应用程序可以播放音频,但不能录制音频。

通常,applet 在安全管理器的监督下运行,不允许录制声音。另一方面,应用程序不会自动安装安全管理器,并且能够录制声音。 (但是,如果为应用程序显式调用默认安全管理器,则不允许应用程序录制声音。)

applet 和应用程序即使在安全管理器运行时也可以录制声音,如果他们已被授予这样做的明确许可。

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