Java Sound API 学习笔记

 
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》
 
 
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值