详细步骤
例行说明
- 本着最小代价/资源化原则,使用
java
自带的音频播放器(位于javax.sound.sampled
包内),格式上仅限于 wav 音频(MP3 翻车不支持,且对 wav 频率&位 有范围限制)这里博主使用 44100 Hz, 16 bit 的格式 - 所以与其说是选择了 wav,倒不如说是选择了播放类
而整体呈现出来的效果就是 音效不行,音质全损 ╮( ̄﹏ ̄)╭ - 当然博主也对其他格式也进行过简单测试,比较有感觉的是 MP3 格式,听起来更加悦耳,而不像 wav 那样低沉,也不知道是格式转换问题还是咋滴,表示一脸懵,望指点!
- 不过在阅读官方文档的过程中,发现位于
javax.sound.sampled
之上的有个javax.sound.midi
的包,同样也是做音频播放的,所不同的是后者调的是系统的声音,后期可能考虑更改,具体效果如何,有待验证
具体步骤
- 造个音乐播放器
MusicPlayer
,使用线程带动,方便并发 MyButton
每按一次,播放一个音频new Thread(new MusicPlayer(getType(), wavPath)).start();
也就是V0.0
所说的按一个键,发一个声- 音频命名出错,纠正方式请见 Option1
代码分析
MusicPlayer
- 第一次把整个类全贴上来,代码上已经加上中文注释,合理来说应该可以看得懂
- 整体而言,通过音频数据流获取音频数据,再怼到播放器上让其播放,播放完再想办法把它关了
public class MusicPlayer extends Thread {
/*
* 对音量高低的调整
*/
private static final float LEFT_VALUE = -3.0f;
private static final float RIGHT_VALUE = +3.0f;
private String musicPath;
private Clip clip;
private AudioInputStream audioInputStream = null;
private BufferedInputStream buffer = null;
private char musicType;
MusicPlayer(char musicType, String musicPath) {
this.musicType = musicType;
this.musicPath = musicPath;
}
@Override
public void run() {
if(musicPath == null) { //如果找不到这个音频就不播放了
return;
}
/*
* 通过 buffer 数据流把音频怼进来
*/
try {
buffer = new BufferedInputStream(new FileInputStream(musicPath));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
/*
* buffer 转换为音频数据流,使用 Clip 打开音频通道,为播放做好准备
*/
try {
audioInputStream = AudioSystem.getAudioInputStream(buffer);
clip = AudioSystem.getClip();
clip.open(audioInputStream);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
/*
* 音量控制器
*/
FloatControl gainControl =
(FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
/*
* 音量设置
*/
if(musicType == KeyboardPiano.L) {
gainControl.setValue(LEFT_VALUE); //set +- dB
} else if(musicType == KeyboardPiano.R) {
gainControl.setValue(RIGHT_VALUE);
}
/*
* 音频播放
*/
clip.start();
/*
* 播放完毕关闭线程 & 释放资源
* 想是这样想啦,然而并没有什么卵用。。。
*/
try {
Thread.sleep(3000);
if(!this.isInterrupted()) {
this.interrupt();
}
this.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这里就不传视频了,就差不多
V0.0
里 Demo 演示的效果
-
Attention:
clip.start();
后的代码块几乎无效,Clip
是 Daemon Thread (守护线程),不关闭的话线程一直耗在那直到程序退出,但是,即使加上clip.close()
关闭播放类也收效甚微(具体原因是Clip
的自身 BUG)
反正只要线程里的资源不释放,再怎么interrupt
, 再怎么close
都没戏,不信可以试试
而资源没有释放导致一个最严重的后果就是内存泄露(准确来说是堆内存泄露)
什么意思?一首曲子弹下来,内存飙到 1000+ M,而且只增不减,谁受得了。。。 -
JConsole: Heap Memory Usage
看不懂,没关系,任务管理器拿出来瞅瞅
- Task Manager: Memory Usage
音频实际上就是一串
byte
数据流,存储在byte[]
中
经过后期内存分析,发现这块资源无论如何都释放不了,但最终还是得到了极大的改善,具体情况以后分析内存泄露时自然会解释清楚
Options
Option1
- 音频命名错误,导致弹奏的时候不着调,博主已经通过
FileTmp.java
文件对音频交换命名
读者只需把 wav/ 文件夹下载替换本地的即可,实际上并没有改变音频本身,只是更改名称而已
相关链接
后记
- 至此,项目开发到这里已经有了基本的雏形了,后期的版本要么是代码调整,要么是 Debug, 而且到现在还处于第一阶段的开发,后面还跟着
V1.7
&V1.8
&V1.9
的系列版本,离完结撒花还有很长的路要走
莫哭,一起走;莫愁,路未尽,心尚留。