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
将需要播放的音频数据装载进来。
divloads 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
div-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 divpared 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.InfoLine.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播放声音
 
可以使用2Line来播放声音,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中的剩余数据,Linestop时才能调用。
 
有如下情形,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 div-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:声学

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()