4.音频模块(Audio)
音频模块编程从来都是一个复杂的话题。这里不打算用到一些高级复杂的音频处理手段,主要是播放一些背景音乐。在书写代码前,让我们了解一下音频的基础知识。
采样率:定义了每秒从连续信号中提取并组成离散信号的采样个数,采样率越高音质越好,单位用赫兹(Hz)来表示,CD一般是44.1KHz。对于每个采样系统会分配一定存储位(bit数)来表达声波的声波振幅状态,称之为采样分辨率或采样精度,每增加1个bit,表达声波振幅的状态数就翻一翻,并且增加6db的动态范围态,1个2bit的数码音频系统表达千种状态,即12db的动态范围,以此类推。如16bit能够表达65536种状态,24bit可以表达多达16777216种状态。动态范围是指声音从最弱到最强的变化范围,人耳的听觉范围通常是20HZ~20KHZ。高的采样率意味着更多的存储空间。比如60s的声音,采样率8KHz、8bits,大约0.5M,采样率44KHz、16bits,超过5M,普通的3分钟的流行歌曲,将会超过15M。
为了即不降低质量有不太占据空间,很多比较好的压缩方法被提出来。比如MP3s 和OGGs格式就是网络中比较流行的压缩格式。
可以看到3min的歌曲占了不少空间。当我们播放游戏的后台音乐时,我们可以把音频流化而不是预先加载到内存。通常背景音乐只有一个,因此只需要到磁盘加载一次即可。
对于一些短的音效,比如爆炸声和枪的射击声,情况有所不同。这些短的音效经常会同时被调用多次,从磁盘对每个实例流化这些音效不是一个好的办法。幸运的是,短的音效并不占用太多的内存空间,因此只需把这些音效提前读入到内存即可,然后可以直接地同时播放这些音效。
因此我们的代码需要提供如下功能:
我们需要一种方法加载音频文件,用于流化播放(Music)和内存播放(Sound),同时提供控制播放功能。
相应的接口有三个,Audio、Music和Sound,代码如下。
Audio接口Audio.java
package com.badlogic.androidgames.framework;
public interface Audio {
public Music newMusic(String filename);
public Sound newSound(String filename);
}
Audio接口创建新的Music和 Sound实例。一个Music实例表示一个流音频文件,一个Sound实例表示一个保存在内存中的短的音效。方法 Audio.newMusic()和Audio.newSound()都是以文件名作为参数并抛出IOException以防文件加载失败(例如文件不存在或者文件损坏等情况)。
Music接口Music.jva
package com.badlogic.androidgames.framework;
public interface Music {
public void play();
public void stop();
public void pause();
public void setLooping(boolean looping);
public void setVolume(float volume);
public boolean isPlaying();
public boolean isStopped();
public boolean isLooping();
public void dispose();
}
Music接口有点复杂,包含了播放音乐流、暂定和停止、循环播放、音量控制(从0到1的浮点数)方法。当然,里面还有一些getter方法,用来获取当前音乐实例的状态。当我们不再需要Music 实例时, 我们可以销毁它(dispose方法),这会关闭系统资源,即流化的音频文件。
Sound接口Sound.java
package com.badlogic.androidgames.framework;
public interface Sound {
public void play(float volume);
public void dispose();
}
Sound接口比较简单,只包含play()和dispose()方法。前者以指定的音量为输入参数,我们可以在任何我们需要的时候播放音效。后者在我们不许Sound实例时,我们需要销毁它以释放它占用的内存空间。
PS: 欢迎关注公众号"Devin说",会不定期更新Java相关技术知识。