Java Sound API是javaSE平台提供底层的(low-level)处理声音接口。
例外,java也提供了简单的实用的高层媒体接口(higher-level) - JMF(Java Media Framework)。
Java Sound API 将需要处理的数字音频分为:simpled-audio和midi,
分别提供Package来处理它们:
javax.sound.simpled
javax.sound.midi
同时SOUND API还提供了第三方的扩展接口:
javax.sound.simpled.spi
javax.sound.midi.spi
*注:spi : service provider interface
Sampled Audio
采样音频(simpled-audio)不仅包含从模拟信号采样来的数字音频,还包括电脑合成的。
称作digital-audio更为合适。
为了能够让设备播放采样声音,程序需要处理 audio input, output device, audio data buffers。
还有混音处理(mix multiple streams of audio into one stream)。
SOUND API 可以使用3种方式传输声音数据:stream, buffered fashion, in-memory unbuffered fashion。
第三种方式适合数据量不大,能够一次载入的所有数据的情形。这样,声音的响应较快,循环和随机定位也会很简单。
使用SOUND API播放声音至少需要3样东西:
l
formatted audio data,
l
a mixer,
l
a line.
Mixer
调音台
technically the Mixer itself is also a kind of Line
Line
音频数据管道。
Clip extends Line
将需要播放的音频数据装载进来。
preloads audio data from a sound file into clips
A Clip is a data line into which audio data can be loaded prior to playback. Because the data is
pre-loaded rather than streamed, the clip's duration is known before playback, and you can choose any
starting position in the media. Clips can be looped, meaning that upon playback, all the data between two
specified loop points will repeat a specified number of times, or indefinitely.
SourceDataLine extends Line
accept real-time stream of audio data
feed audio to the Mixer
A SourceDataLine receives audio data for playback. It provides methods for writing data to the
source data line's buffer for playback, and for determining how much data the line is prepared to receive
without blocking.
TargetDataLine
A TargetDataLine receives audio data from a mixer. Commonly, the mixer has captured audio data
from a port such as a microphone; it might process or mix this captured audio before placing the data in
the target data line's buffer. The TargetDataLine interface provides methods for reading the data
from the target data line's buffer and for determining how much data is currently available for reading.
Port extends Line
simple Line
Line 接口的继承关系图

AudioSystem
AudioSystem
提供音频资源的访问服务。
通过
AudioSystem
,可以知道什么样的资源可以被识别。
可从AudioSystem获得的资源:
l
Mixers, AudioSystem
类可以提供一张已经安装的
Mixer
列表
l
Lines
l
Format conversions
l
Files and streams
Mixer的获得
Mixer.Info
AudioSystem.getMixerInfo():Mixer.Info
可以获得一张
Mixer
信息列表。
每个
Mixer.Info
包含如下关键信息:
l
Name
l
Version
l
Vendor
l
Description
我机器上的Mixer列表,WinXP,JDK_1.4.2
[INFO 0]
INFO.NAME:Java Sound Audio Engine
INFO.VERSION:1.0
INFO.VERDOR:Sun Microsystems
INFO.DESCRIPTION:Software mixer and synthesizer
[INFO 1]
INFO.NAME:Microsoft ?ù??????÷
INFO.VERSION:Unknown Version
INFO.VERDOR:Unknown Vendor
INFO.DESCRIPTION:No details available
[INFO 2]
INFO.NAME:Realtek AC97 Audio
INFO.VERSION:Unknown Version
INFO.VERDOR:Unknown Vendor
INFO.DESCRIPTION:No details available
[INFO 3]
INFO.NAME:Realtek AC97 Audio
INFO.VERSION:5.10
INFO.VERDOR:Unknown Vendor
INFO.DESCRIPTION:Unknown Description
获取Mixer
AudioSystem.getMixer( MixerInfo ):Mixer
如果只从
Mixer.Info
提供的信息无法确定需要的
Mixer,
不妨创建出所有的
Mixer
,使用时检查它们的能力,使用合适那个。
例如,你可能需要一个
Mixer
,能够将混音好的数据同时写入一定数目的目标数据管道(
TargetDataLine
)
.
使用
Mixer.getMaxLines(Line.Info info):int
来了解
Mixer
的输出能力。
info
就是指定的
TargetDataLine
获得指定类型Line
2
种方法获得
Line
l
直接由
AudioSystem
获得
,AudioSystem.getLine( Line.Info ):Line
l
由
Mixer
获得
从AudioSystem直接获得Line
static Line AudioSystem.getLine( Line.Info )
不同于
Mixer.Info
,
Line.Info
不是文本信息,而是
Line
的类信息。
Line.Info
是抽象类,所以使用它的子类
DataLine.Info,Port.Info
。
下面是通过
Line.Info
获得
Line
的示例:
TargetDataLine line;
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
format); // format is an AudioFormat object
if (!AudioSystem.isLineSupported(info)) {
// Handle the error.
}
// Obtain and open the line.
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
} catch (LineUnavailableException ex) {
// Handle the error.
//...
}
Port.Info
定义一系列静态的
Port.Info
对象
,MICROPHONE,SPEAKER,etc.
从Mixer获得Line
getSourceDataLine()
getTargetDataLine()
getLine()
AudioSystem对象模型

AudioPermission
音频资源访问许可。
利用JAVA-SOUND-API播放声音
可以使用
2
种
Line
来播放声音
,Clip,SourceDataLine
。
Clip
一次载入需要播放的声音资源,而
SourceDataLine
以流的方式传输声音数据。
使用Clip
当使用
getLine()
获得
Clip
后,还要保证其他的程序在你播放前不能获取它,调用
open()
方法独占它:
void open( AudioInputStream stream );
void open( AudioFormat, byte[] data, int offset, int bufferSize );
Clip
默认从音频的开头开始播放,除非使用
setFramePosition(),setMicroSecondPosition()
设定其他位置。
Clip.start()
播放,
Clip.stop()
暂停。
getLevel(),
获得声音高度。
isActive(),Clip
是否正在播放。
使用SourceDataLine
open(AudioFormat),
打开
source dataLine,
但不指定数据,使用默认的
buffer size
。
open(AudioFormat, bufferSize),
指定
buffer size.
合理设置
buffer size,
保证开始载入的延时能够被接受,又尽量减少
IO
访问。
open()
之后
,
调用
start()
容许
SourceDataLine
一有数据就开始播放
,
使用
write()
不停的输入数据。
void write( byte[] b, int offset, int length );
SourceDataLine
开始播放后,向
Mixer
传输数据,当
Mixer
开始向
target
传输数据时,
SourceDataLine
产生一个
START
事件。
这是
SourceDataLine
被认为是活动的
(active)
。
isRunning()
表明
Line
是否
start()
了。
isActive()
表明
Line
是否开始播放。
write()
方法向
buffer size
中写入数据,如果
buffer
已满,还剩下一些数据,该方法会阻塞;否则,是不阻塞的。
可以使用
DataLine.available()
知道
buffer size
还能写入多少数据。
事实上,可以另开线程去使用
write()
,而不用考虑阻塞的问题。
drain()
方法在所有数据播放完后返回。
所有在写完数据后,调用
drain()
,到它返回时再是否
Line
。
line.write(b, offset, numBytesToWrite);
//this is the final invocation of write
line.drain();
line.stop();
line.close();
line = null;
flush()
清空
buffer
中的剩余数据,
Line
在
stop
时才能调用。
有如下情形
,Line
会产生
STOP
事件:
l
调用
drain()
l
调用
stop()
l
调用
flush()
l
输出完旧的数据,而新的数据未到时。
STOP
事件意味着
isActive()
返回
false.
start()
调用之后,
isRunning()
都会返回
true,
知道
stop()
被调用。它不是依据
STOP
事件产生返回值的。
而
isActive()
是依据
START
和
STOP
事件的。
监视Line的状态
使用
LineListener
响应
Line
的事件。
void Line.addLineListener( LineListener );
当调用
open(),close(),start(),stop()
会产生
OPEN,CLOSE,START,STOP
事件。
多个Line 同步播放
有些
Mixer
提供方便的同步功能,对一组
Lines
使用
open(),close(),start(),stop()
,保证它们的同步。
可以使用如下方法,检查
Mixer
是否支持同步功能:
boolean isSynchronizationSupported( Line[] lines, boolean maintainSync )
第二个参数表明同步精度,是采样同步,还是只是
start(),stop()
保持同步,并不维护播放过程同步。
AudioFormat
音频采样的格式,不是音频文件的格式。
l
编码技术,一般都是
PCM( pulse code modulation )
l
声道数目(
1
,单声道;
2
,双声道;等等)
l
采样频率
sample rate
l
样本的位数
number of bits per sample
l
帧速率
Frame rate
l
Frame Size in bytes
l
Byte Order( big-endian or little-endian )
AudioFileFormat:
音频文件格式。
The file type( WAV,AIFF,etc )
The file length in bytes
The length, in frames, of the audio data contained in the file
An AudioFormat that specifies data format of the audio data in the file
AudioInputStream extends InputStream
无须考虑文件的格式,就能操作
Samples
。
读取音频文件
AudioSystem
提供
2
中方法读取音频文件:
l
根据音频文件中音频数据的格式信息
l
使用一个指定了音频数据格式的流
使用如下方法获得音频文件中音频数据的格式信息:
static AudioFileFormat getAudioFileFormat(File);
static AudioFileFormat getAudioFileFormat(InputStream);
static AudioFileFormat getAudioFileFormat(URL);
利用如下方法获得第二种方法提到的音频数据流:
static AudioInputStream getAudioInputStream(File)
static AudioInputStream getAudioInputStream(InputStream)
static AudioInputStream getAudioInputStream(URL)
读取音频文件数据的步骤:
1)
获得
AudioInputStream
对象
2)
创建一个字节数组,存放一次读入的数据块
3)
不断地从
audio
流中读入数据,播放或处理数据。
示例代码如下:
int totalFramesRead = 0;
File fileIn = new File(somePathName);
// somePathName is a pre-existing string whose value was
// based on a user selection.
try {
AudioInputStream audioInputStream =
AudioSystem.getAudioInputStream(fileIn);
int bytesPerFrame =
audioInputStream.getFormat().getFrameSize();
// Set an arbitrary buffer size of 1024 frames.
int numBytes = 1024 * bytesPerFrame;
byte[] audioBytes = new byte[numBytes];
try {
int numBytesRead = 0;
int numFramesRead = 0;
// Try to read numBytes bytes from the file.
while ((numBytesRead =
audioInputStream.read(audioBytes)) != -1) {
// Calculate the number of frames actually read.
numFramesRead = numBytesRead / bytesPerFrame;
totalFramesRead += numFramesRead;
// Here, do something useful
// with the audio data that's
// now in the audioBytes array...
}
} catch (Exception ex) {
// Handle the error...
}
} catch (Exception e) {
// Handle the error...
}
写音频文件
通过下列方法知道
AudioSystem
支持哪些音频文件格式写入:
static boolean isFileTypeSupported( AudioFileFormat.Type,AudioInputStream );
static AudioFileFormat.Type[] getAudioFileTypes();
static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream);
利用
AudioSystem.write()
方法向文件写入指定格式的音频数据:
static int write( AudioInputStream, AudioFileFormat.Type, File );
文件或数据格式转换
“Java Sound Programmer Guide” – chapter 7
Audio File Format Convertion
Audio Data Format Convertion
PCM
PCM
脉冲编码调制是
Pulse Code Modulation
的缩写。
PCM
通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。
PCM
是数字音频中最佳的保真水准,近似看成“无损”编码。
PCM
编码的优点是音质好,缺点是数据量大。
JAVA SOUND API
对于其它编码格式,在播放前都会转换成
PCM
格式。
DAC:digital-to-analog converter,数模转换器
Decibel:分贝。pl.decibels
PAN:声象,该通道信号在左右音箱之间的立体声位置。
GAIN:增益
REVERB:数字混响。
Acoustics:声学
资源
《Java Sound programmer guide》