文档

Java™教程
隐藏目录
示例包概述
导航:声音

采样包概述

javax.sound.sampled 包主要涉及音频传输 - 也就是说,Java Sound API 专注于播放和捕获。Java Sound API 的主要任务是如何将格式化音频数据的字节移入和移出系统。这个任务涉及到打开音频输入和输出设备,并管理用于填充实时音频数据的缓冲区。它还可以将多个音频流混合为一个流(无论是输入还是输出)。当用户请求启动、暂停、恢复或停止声音流时,必须正确处理声音流的传输。

为了支持基本的音频输入和输出,Java Sound API 提供了各种音频数据格式之间的转换方法,并提供了读取和写入常见类型声音文件的方法。然而,它不试图成为一个全面的声音文件工具包。Java Sound API 的特定实现不必支持广泛的文件类型或数据格式转换。第三方服务提供商可以提供模块,以插入到现有的实现中,以支持其他文件类型和转换。

Java Sound API 可以以流式缓冲方式和非缓冲方式处理音频传输。这里的"流式"是指实时处理音频字节的通用方式,它不是指在特定格式下通过互联网发送音频的已知情况。换句话说,音频流只是一系列以大致相同的速度到达的音频字节,以便进行处理(播放、录制等)。字节的操作在所有数据到达之前就开始了。在流式模型中,特别是在音频输入而不是音频输出的情况下,您不一定事先知道声音的持续时间和何时到达结束。您只是一次处理一个音频数据缓冲区,直到操作停止。在音频输出(播放)的情况下,如果要播放的声音太大而无法一次性装入内存中,您还需要缓冲数据。换句话说,您将音频字节以块的形式传递给声音引擎,它会在正确的时间播放每个样本。提供了机制,使得很容易知道在每个块中传递多少数据。

Java Sound API 还允许在仅播放的情况下进行无缓冲传输,假设您已经拥有所有音频数据并且它不太大而无法装入内存。在这种情况下,应用程序无需缓冲音频,尽管如果需要,仍然可以使用缓冲的实时方法。相反,整个声音可以一次性预加载到内存中进行后续播放。因为所有声音数据都提前加载了,所以播放可以立即开始 - 例如,当用户点击一个开始按钮时。这与缓冲模型相比可以是一个优势,因为播放必须等待第一个缓冲区填充。此外,内存中的非缓冲模型允许轻松地循环或设置声音数据中的任意位置。

要使用Java Sound API播放或捕获声音,你至少需要三样东西:格式化的音频数据、混音器和线路。以下提供了对这些概念的概述。

什么是格式化的音频数据?

格式化的音频数据指的是以多种标准格式之一表示的声音。Java Sound API区分数据格式文件格式

数据格式

数据格式告诉您如何解释一系列“原始”采样音频数据的字节,例如已从声音文件中读取的样本或已从麦克风输入捕获的样本。例如,您可能需要知道一个样本包含多少个位(表示声音的最短瞬间的表示),同样地,您可能需要知道声音的采样率(样本应该多快地连续跟随彼此)。在设置播放或捕获时,您指定要捕获或播放的声音的数据格式。

在Java Sound API中,数据格式由一个AudioFormat对象表示,它包括以下属性:

PCM是一种声波的编码方式。Java Sound API包括两种使用线性量化幅度和有符号或无符号整数值的PCM编码。线性量化意味着每个样本中存储的数字与该瞬间的原始声音压力成正比(除了任何失真),并与振动着该瞬间声音的扬声器或鼓膜的位移成正比。例如,音乐CD使用线性PCM编码的声音。Mu-law编码和a-law编码是常见的非线性编码,它们提供了音频数据的更压缩版本;这些编码通常用于电话或语音录音。非线性编码通过使用非线性函数将原始声音的幅度映射到存储值,可以为安静的声音提供更多的幅度分辨率,而对大声音则较少。

一个帧包含特定时间点上所有通道的数据。对于PCM编码的数据,帧仅仅是给定时间点上所有通道的同时采样集合,没有任何额外信息。在这种情况下,帧速率等于采样率,帧大小以字节为单位是通道数乘以位数的样本大小,再除以字节中的位数。

对于其他类型的编码,一个帧可能包含除样本之外的附加信息,并且帧速率可能与采样率完全不同。例如,考虑MP3(MPEG-1音频第三层)编码,在当前版本的Java Sound API中没有明确提到,但可以由Java Sound API的实现或第三方服务提供商支持。在MP3中,每个帧包含一系列样本的压缩数据束,而不仅仅是每个通道的一个样本。因为每个帧封装了一整个样本序列,所以帧速率比采样率慢。帧还包含一个头部。尽管有头部,但帧的字节大小比等量的PCM帧的字节大小要小。(毕竟,MP3的目的是比PCM数据更紧凑。)对于这种编码,采样率和采样大小指的是编码后的声音最终在传递给数字到模拟转换器(DAC)之前将被转换为的PCM数据。

文件格式

文件格式指定了声音文件的结构,包括文件中原始音频数据的格式以及可以存储在文件中的其他信息。声音文件有各种标准品种,例如WAVE(也称为WAV,通常与PC关联),AIFF(通常与Macintosh关联)和AU(通常与UNIX系统关联)。不同类型的声音文件具有不同的结构。例如,它们可能在文件的“头部”中有不同的数据排列方式。头部包含描述性信息,通常在文件的实际音频样本之前,尽管一些文件格式允许连续的描述性和音频数据的“块”。头部包括一个规定了用于存储声音文件中音频的数据格式的规范。这些类型的任何声音文件都可以包含各种数据格式(尽管通常在给定文件中只有一个数据格式),并且可以在具有不同文件格式的文件中使用相同的数据格式。

在Java Sound API中,文件格式由一个AudioFileFormat对象表示,其中包含:

AudioSystem类提供了读取和写入不同文件格式的音频以及在不同数据格式之间进行转换的方法。其中一些方法可以通过一种称为AudioInputStream的流来访问文件的内容。 AudioInputStreamInputStream类的子类,它封装了可以按顺序读取的一系列字节。 AudioInputStream类添加了字节音频数据格式的信息(由AudioFormat对象表示)到其超类中。通过将声音文件作为AudioInputStream读取,您可以立即访问样本,而无需担心声音文件的结构(头文件,块等)。单个方法调用即可提供有关数据格式和文件类型的所有信息。

什么是混音器?

许多用于声音的应用程序编程接口(API)都使用了音频设备的概念。设备通常是物理输入/输出设备的软件接口。例如,声音输入设备可能表示声卡的输入功能,包括麦克风输入、线级模拟输入和可能的数字音频输入。

在Java Sound API中,设备由Mixer对象表示。混音器的目的是处理一个或多个音频输入流和一个或多个音频输出流。在典型情况下,它实际上将多个输入流混合成一个输出流。 Mixer对象可以表示物理设备(如声卡)的声音混音能力,该设备可能需要将计算机上的声音从各种输入混合在一起,或者将应用程序产生的声音发送到输出设备。

另外,Mixer对象可以表示完全通过软件实现的声音混音能力,而无需与物理设备进行任何内部接口。

在Java Sound API中,声卡上的麦克风输入本身不被视为设备(即混音器),而是混音器的一个端口。端口通常提供一个音频流进入或离开混音器(尽管该流可以是多通道的,如立体声)。混音器可能有多个此类端口。例如,表示声卡输出功能的混音器可能将多个音频流混合在一起,然后将混合信号发送到连接到混音器的任何或所有各种输出端口。这些输出端口可以是(例如)耳机插孔、内置扬声器或线级输出。

要理解Java Sound API中混音器的概念,有助于想象一个物理的混音台,比如在现场音乐会和录音室中使用的那种。

下面的上下文描述了这个图像。

物理混音台

一个物理混音器有多个"条带"(也称为"片段"),每个条带代表一个音频信号通过混音器进行处理的路径。条带上有旋钮和其他控制器,可以控制该条带中信号的音量和声像定位(在立体声图像中的位置)。此外,混音器可能还有一个用于混响等效果的独立总线,并且该总线可以连接到内部或外部的混响设备。每个条带都有一个电位器,用于控制该条带的信号在混响混音中的比例。然后将混响的("湿的")混音与条带的"干的"信号混合在一起。物理混音器将最终的混合音发送到输出总线,通常连接到录音机(或基于磁盘的录音系统)和/或扬声器。

想象一下正在以立体声录制的现场音乐会。来自舞台上许多麦克风和电乐器的电缆(或无线连接)被插入混音台的输入端口。每个输入进入混音器的一个单独的条带,如图所示。音频工程师决定增益、声像和混响控制的设置。所有条带和混响单元的输出被混合成两个声道。这两个声道通过混音台的两个输出进入连接到立体声录音机输入的电缆中。这两个声道也可能通过放大器发送到音乐厅中的扬声器,这取决于音乐的类型和音乐厅的大小。

现在想象一下录音室,每个乐器或歌手都被录制到多轨录音机的一个单独轨道上。在所有乐器和歌手都被录制之后,录音工程师进行"混音",将所有录制的轨道合并成一个可以在光盘上分发的双声道(立体声)录音。在这种情况下,混音台的每个条带的输入不是麦克风,而是多轨录音的一个轨道。工程师可以再次使用条带上的控制器来决定每个轨道的音量、声像和混响量。混音器的输出再次进入立体声录音机和立体声扬声器,就像现场音乐会的例子一样。

这两个例子说明了混音器的两种不同用途:捕获多个输入通道,将它们组合成较少的轨道并保存混合结果,或者在混音时播放多个轨道并将它们混合成较少的轨道。

在Java Sound API中,混音器同样可以用于输入(捕获音频)或输出(播放音频)。在输入的情况下,混音器从一个或多个输入端口获取音频进行混音的是一个或多个输入端口。混音器将捕获和混合的音频流发送到其目标,这是一个带有缓冲区的对象,应用程序可以从中检索混合的音频数据。在音频输出的情况下,情况则相反。混音器的音频源是一个或多个包含缓冲区的对象,应用程序可以将其声音数据写入其中;混音器的目标是一个或多个输出端口。

什么是线路?

在理解Java音频API中的“线路”概念时,物理混音台的隐喻也是有用的。

线路是数字音频“管道”的一个元素,即将音频移入或移出系统的路径。通常,线路是进入或离开混音器的路径(尽管从技术上讲,混音器本身也是一种线路)。

音频输入和输出端口就是线路。这些与物理混音台连接的麦克风和扬声器类似。另一种线路是应用程序通过该线路从混音器获取输入音频或将输出音频发送到混音器的数据路径。这些数据路径类似于与物理混音台连接的多轨录音机的轨道。

Java音频API中的线路与物理混音器的线路之间的一个区别是,通过Java音频API中的线路流动的音频数据可以是单声道或多声道(例如,立体声)。相比之下,物理混音器的每个输入和输出通常都是单声道的声音。要从物理混音器获取两个或更多声道的输出,通常需要使用两个或多个物理输出(至少在模拟声音的情况下;数字输出接口通常是多声道的)。在Java音频API中,线路中的通道数由当前流经线路的数据的AudioFormat指定。

现在让我们来看一些具体类型的线路和混音器。下图显示了可能是Java音频API实现的简单音频输出系统中不同类型的线路:

以下上下文描述了此图。

音频输出的线路可能的配置

在此示例中,应用程序已经获得了音频输入混音器的一些可用输入:一个或多个剪辑源数据线。剪辑是混音器输入(一种线路),您可以在播放之前将音频数据加载到其中;源数据线是接受实时音频数据流的混音器输入。应用程序将音频数据从声音文件预加载到剪辑中。然后,它将其他音频数据以缓冲区为单位推送到源数据线中。混音器从所有这些线路读取数据,每条线路可能都有自己的混响、增益和平衡控制,并将干净的音频信号与湿度(混响)混合。混音器将最终输出传送到一个或多个输出端口,如扬声器、耳机插孔和线路输出插孔。

尽管图中的各种线路被描绘为单独的矩形,但它们都是混音器所拥有的,并且可以被视为混音器的组成部分。混响、增益和平衡矩形表示的是混音器对通过线路流动的数据应用的处理控制(而不是线路本身)。

请注意,这只是API支持的可能混音器的一个示例。不是所有音频配置都具备所示的所有功能。个别源数据线可能不支持声道平移,混音器可能不实现混响等等。

一个简单的音频输入系统可能类似于:

以下上下文描述了此图

音频输入线的可能配置

在这里,数据从一个或多个输入端口流入混音器,通常是麦克风或线路输入插孔。应用增益和声道平移,并通过混音器的目标数据线将捕获的数据传递给应用程序。目标数据线是混音器的输出,包含流式输入声音的混合物。最简单的混音器只有一个目标数据线,但有些混音器可以同时将捕获的数据传递给多个目标数据线。

线接口层次结构

现在我们已经从功能图片中看到了线和混音器的一些信息,让我们从稍微编程的角度来讨论它们。几种线类型由基本Line接口的子接口定义。接口层次结构如下所示。

以下上下文描述了此图

线接口层次结构

基本接口Line描述了所有线都具备的最小功能:

我们现在将研究Line接口的子接口。

Ports是用于将音频输入或输出到音频设备的简单线路。正如前面提到的,一些常见类型的端口包括麦克风、线路输入、CD-ROM驱动器、扬声器、耳机和线路输出。

Mixer接口表示混音器,当然,它可以表示硬件设备或软件设备。Mixer接口提供了获取混音器线路的方法。这些线路包括源线路,用于将音频传送到混音器,以及目标线路,混音器将混合后的音频传送到这些线路。对于音频输入混音器,源线路是输入端口,例如麦克风输入,目标线路是TargetDataLines(下面描述了该类),用于将音频传送到应用程序。另一方面,对于音频输出混音器,源线路是ClipsSourceDataLines(下面描述了该类),应用程序将音频数据传送到这些线路,而目标线路是输出端口,例如扬声器。

Mixer被定义为具有一个或多个源线路和一个或多个目标线路。请注意,这个定义意味着混音器实际上不需要混合数据;它可能只有一个单独的源线路。Mixer API旨在涵盖各种设备,但典型情况下支持混合。

Mixer接口支持同步;也就是说,您可以指定混音器的两个或多个线路被视为同步组。然后,您可以通过向组中的任何线路发送单个消息来启动、停止或关闭所有这些数据线路,而不是必须逐个控制每条线路。通过支持此功能的混音器,您可以在线路之间获得样本精确的同步。

通用的Line接口不提供启动和停止播放或录制的方法。为此,您需要一个数据线路。DataLine接口提供了除Line之外的以下额外的与媒体相关的功能:

TargetDataLine从混音器接收音频数据。通常,混音器已经从麦克风等端口捕获音频数据;在将数据放入目标数据线的缓冲区之前,它可能会处理或混合此捕获的音频数据。 TargetDataLine接口提供了从目标数据线的缓冲区读取数据以及确定当前可供读取的数据量的方法。

SourceDataLine接收用于播放的音频数据。它提供了向源数据线的缓冲区写入数据以进行播放的方法,并确定线路在不阻塞的情况下准备接收多少数据。

Clip是一个数据线,可以在播放之前将音频数据加载到其中。由于数据是预加载而不是流式传输,因此在播放之前已知片段的持续时间,并且可以选择媒体中的任何起始位置。片段可以循环播放,这意味着在播放时,两个指定循环点之间的所有数据将重复指定的次数或无限次。

本节介绍了采样音频API的大部分重要接口和类。后续的章节将展示如何在应用程序中访问和使用这些对象。

 


上一页:目录
下一页:访问音频系统资源