android音频处理
音频基础概念
基础知识可以参考以下几篇文章:
音频,PCM,采样率及android的AudioRecord与AudioTrack 资料整理
标准MP4(H.264视频编码和AAC音频编码)
多媒体文件格式全解说(上)--音视频声音振幅:
Android 音频技术开发总结
JSSRC的使用
- github下载代码:
- 因为源代码中用到了java的一个audio库,这个需要自己在gradle中键入文件依赖,但是直接放在app或者其他模块中会有一个潜在问题:as2.2以后版本需要jdk1.8才可以,会遇到兼容错误,解决方法是将这个JSSRC作为一个java的lib库引入,也就是作为一个独立mudule,该module特点是gradle中需要如下配置:
apply plugin: 'java'
buildscript {
tasks.withType(JavaCompile) {
// sourceCompatibility = JavaVersion.VERSION_1_7
// targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile files('/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/rt.jar')
}
然后app模块依赖这个module就可以了。
SoundPool
1. 官方Api翻译
The SoundPool class manages and plays audio resources for applications.
SoundPool为app管理和播放音频。
A SoundPool is a collection of samples that can be loaded into memory from a resource inside the APK or from a file in the file system. The SoundPool library uses the MediaPlayer service to decode the audio into a raw 16-bit PCM mono or stereo stream. This allows applications to ship with compressed streams without having to suffer the CPU load and latency of decompressing during playback.
一个SoundPool对象是一系列样本的集合,这里的样本来源于apk包内的资源或者是文件系统里的文件,可以被加载到内存中。SoundPool的库利用MediaPlayer服务将音频解码成一个16-bit单声道或者立体声的PCM流,这使得app可以直接用压缩数据流同时摆脱CPU加载数据的压力和播放时重解压的延迟。
In addition to low-latency playback, SoundPool can also manage the number of audio streams being rendered at once. When the SoundPool object is constructed, the maxStreams parameter sets the maximum number of streams that can be played at a time from this single SoundPool. SoundPool tracks the number of active streams. If the maximum number of streams is exceeded, SoundPool will automatically stop a previously playing stream based first on priority and then by age within that priority. Limiting the maximum number of streams helps to cap CPU loading and reducing the likelihood that audio mixing will impact visuals or UI performance.
除了播放时的低延迟,SoundPool还可以管理同一时间下渲染的音频流的数量。创建SoundPool对象时,参数maxStreams可以设置同一时刻这个单一SoundPool同事播放的流的最大数目。SoundPool跟踪活动流的最大数目。如果超过了流的最大设置数目,SoundPool将会自动停止前一个正在播放的音频流。前一个到底是哪一个的标准首先是优先级然后是此优先级的时间先后。限制最大音频流数目有助于cpu加载的性能,降低音频混音对视觉或者UI性能上影响的概率。
Sounds can be looped by setting a non-zero loop value. A value of -1 causes the sound to loop forever. In this case, the application must explicitly call the stop() function to stop the sound. Any other non-zero value will cause the sound to repeat the specified number of times, e.g. a value of 3 causes the sound to play a total of 4 times.
声音可以通过设置一个非零的loop值来控制循环播放次数。-1代表一直循环播放,此时,app必须显式的调用stop()函数停止声音播放。任意的非零值都会使声音重复播放指定的次数。比如,设置为3则声音一共会播放4次,3指的是循环次数。
The playback rate can also be changed. A playback rate of 1.0 causes the sound to play at its original frequency (resampled, if necessary, to the hardware output frequency). A playback rate of 2.0 causes the sound to play at twice its original frequency, and a playback rate of 0.5 causes it to play at half its original frequency. The playback rate range is 0.5 to 2.0.
播放速度也可以控制。1.0的播放速度标识声音按照原来的频率播放(当然,如果有必要的话,会重采样为硬件支持的输出频率)。2.0的速度会按照原始速度的两倍播放,0.5的速度则会按照原始速度的一半播放。速度的可调范围为[0.5, 2.0]。
Priority runs low to high, i.e. higher numbers are higher priority. Priority is used when a call to play() would cause the number of active streams to exceed the value established by the maxStreams parameter when the SoundPool was created. In this case, the stream allocator will stop the lowest priority stream. If there are multiple streams with the same low priority, it will choose the oldest stream to stop. In the case where the priority of the new stream is lower than all the active streams, the new sound will not play and the play() function will return a streamID of zero.
优先级由低到高分布,也就是越大的数表示越高的优先级。创建SoundPool时会指定一个最大音频流的参数,当play()方法的调用导致当前活动音频流的数目超过这个最大参数的时候,优先级就会被应用。此时,流分配器会停止最低优先级的流。如果最低优先级的流有多个,那就停止最早创建的那个(最老的那个)。如果最新的流的优先级比所有活动的流的优先级都低,那么这个最新的音频流将不会被播放,同时paly()函数将会返回一个值为0的streamId。
Let's examine a typical use case: A game consists of several levels of play. For each level, there is a set of unique sounds that are used only by that level. In this case, the game logic should create a new SoundPool object when the first level is loaded. The level data itself might contain the list of sounds to be used by this level. The loading logic iterates through the list of sounds calling the appropriate SoundPool.load() function. This should typically be done early in the process to allow time for decompressing the audio to raw PCM format before they are needed for playback.
来看一个典型的使用场景:一个游戏含有很多层级播放场景。对每个级别,都有一个唯一的声音集合只供该级别使用。此时,在第一个场景加载的时候,游戏逻辑应该创建一个新的SoundPool对象。级别数据本身可以包含它自己要使用的声音列表。加载逻辑通过声音列表调用合适的SoundPool.load()函数迭代进行。一般这些load在程序中会先做以保证在播放时有时间将音频解压成原始PCM格式的数据。
Once the sounds are loaded and play has started, the application can trigger sounds by calling SoundPool.play(). Playing streams can be paused or resumed, and the application can also alter the pitch by adjusting the playback rate in real-time for doppler or synthesis effects.
一旦声音的加载和游戏开始了,app可以通过调用SoundPool.play()触发声音播放。播放的流可以暂停或者恢复,app也可以通过实时调节播放速度来修改音高以达到多普勒或者合成效果。
Note that since streams can be stopped due to resource constraints, the streamID is a reference to a particular instance of a stream. If the stream is stopped to allow a higher priority stream to play, the stream is no longer be valid. However, the application is allowed to call methods on the streamID without error. This may help simplify program logic since the application need not concern itself with the stream lifecycle.
注意,声音流可能由于资源约束问题而停止,streamID是指向一个流的特殊的引用。如果为了播放高优先级的流而停止了一个流,那么这个流将不再有效。但是,app可以调用这个streamID上的方法而不会报错。这样的可以简化程序的逻辑,app不必自己去管理流的生命周期。
In our example, when the player has completed the level, the game logic should call SoundPool.release() to release all the native resources in use and then set the SoundPool reference to null. If the player starts another level, a new SoundPool is created, sounds are loaded, and play resumes.
在我们的例子中,当玩家已将完成了本级别,游戏逻辑应该调用SoundPool.release()去释放使用中所有的native资源并将SoundPool的引用指向null。如果玩家开始了另一个级别,将会创建一个新的SoundPool,声音被加载,并恢复播放。
2. 相关博客
SoundPool适合短且对反应速度比较高的情况(游戏音效或按键声等),文件大小一般控制在几十K到几百K,最好不超过1M,可以与MediaPlayer同时播放,SoundPool也可以同时播放多个声音;最终编解码实现与MediaPlayer相同;
MediaPlayer只能同时播放一个声音,加载文件有一定的时间,适合文件比较大,响应时间要是不是非常高的场景;
二、Android中使用声音池SoundPool。
对于对SoundPool还不了解的童鞋,我先附上一段网上摘要下来的资料,因为之前查的资料忘了附上原作者了,如有需要联系我加上吧,先引用一下吧。
在Android开发中我们经常使用MediaPlayer来播放音频文件,但是MediaPlayer存在一些不足,例如:资源占用量较高、延迟时间较长、不支持多个音频 同时播放等。这些缺点决定了MediaPlayer在某些场合的使用情况不会很理想,例如在对时间精准度要求相对较高的游戏开发中。
下面说说SoundPool的特点:
SoundPool载入音乐文件使用了独立的线程,不会阻塞UI主线程的操作。但是这里如果音效文件过大没有载入完成,我们调用play方法时可能产生严 重的后果,这里Android SDK提供了一个SoundPool.OnLoadCompleteListener类来帮助我们了解媒体文件是否载入完成,我们重载 onLoadComplete(SoundPool soundPool, int sampleId, int status) 方法即可获得。
从上面的onLoadComplete方法可以看出该类有很多参数,比如类似id,是的SoundPool在load时可以处理多个媒体一次初始化并放入内存中,这里效率比MediaPlayer高了很多。
SoundPool类支持同时播放多个音效,这对于游戏来说是十分必要的,而MediaPlayer类是同步执行的只能一个文件一个文件的播放。
最后贴上SoundPool的简单使用方法:
SoundPool soundPool = new SoundPool(5,AudioManager.STREAM_SYSTEM,0);
soundPool.load(mContext,R.raw.raws,1);
soundPool.play(1,1, 1, 0, 0, 1);
如上就是一个简单的播放了,其中各个参数的意义大家可以自行上网查找,具体说说上述步骤的不足之处。
刚开始我使用的时候一直出不来声音,后来使用Debug一步一步调试,却能走通,声音也出来了,想想二者运行唯一不同的是调试的时候我很缓慢的走,难道是加载需要时间?后来研究得到确认,确实是load()这个方法需要加载时间,不能立即播放,也许你也遇到这问题,解决办法是开启一个线程,在线程中sleep一秒,然后在进行play方法的调用,完美解决。当然,如果你的声音播放不是需要立即执行,而是需要点击某个按钮之类的,那就不会出现这问题了。