首先我们看下在Android平台下Libgdx的sound的音效是通过SoundPool来实现播放的,下面是Android下的Audio的实现,我们可以看到SoundPool。从下面的代码可以看出,我们在使用sound时SoundPool会进行加载,但是其实异步的方式加载的,可能在使用时sound还未加载完成,导致立即使用可能会无声,所以我做了个优化用CountDownLatch来等待加载完成,所以我们如果使用AssetsManager来加载音效时,如碰到第一次加载无声,可能就是这个原因造成的,我们可以进同步等待,也可以将其放入到加载队列的头部让他优先加载。
public AndroidAudio(Context context, AndroidApplicationConfiguration config) {
if (!config.disableAudio) {
soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
if (context instanceof GameActivity) {
commonSoundPool = new SoundPool(3
, AudioManager.STREAM_MUSIC, 0);
}
manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (context instanceof Activity) {
((Activity) context).setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
} else {
soundPool = null;
commonSoundPool = null;
manager = null;
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Sound newSound(FileHandle file) {
if (soundPool == null) {
throw new GdxRuntimeException("Android audio is not enabled by the application config.");
}
AndroidFileHandle aHandle = (AndroidFileHandle) file;
if (aHandle.type() == FileType.Internal) {
try {
final AssetFileDescriptor descriptor = aHandle.getAssetFileDescriptor();
final AndroidSound sound = new AndroidSound(soundPool, manager
, soundPool.load(descriptor, 1));
descriptor.close();
return sound;
} catch (IOException ex) {
throw new GdxRuntimeException("Error loading audio file: " + file
+ "\nNote: Internal audio files must be placed in the assets directory.", ex);
}
} else {
try {
if (aHandle.file().getAbsolutePath().contains(File.separator + "common" + File.separator)) {
return new AndroidSound(null != commonSoundPool ? commonSoundPool : soundPool, manager
, (null != commonSoundPool ? commonSoundPool : soundPool).load(aHandle.file().getAbsolutePath()
, 1));
} else {
if (Thread.currentThread().getName().equals("AsynchExecutor-Thread") && !( //判断是不是使用AssetsManager加载,如果是启动同步等待模式。
aHandle.file().getAbsolutePath().contains(File.separator + "p1" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p2" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p3" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p4" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p5" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p6" + File.separator)
|| aHandle.file().getAbsolutePath().contains(File.separator + "p7" + File.separator))) {
final CountDownLatch latch = new CountDownLatch(1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(final SoundPool soundPool, final int sampleId, final int status) {
if (sampleId == AndroidAudio.this.sampleId) {
latch.countDown();
}
}
});
sampleId = soundPool.load(aHandle.file().getAbsolutePath(), 1);
if (aHandle.file().getAbsolutePath().contains("g195")) {
latch.await(300, TimeUnit.MILLISECONDS);
} else {
latch.await(1500, TimeUnit.MILLISECONDS);
}
return new AndroidSound(soundPool, manager, sampleId);
} else {
sampleId = soundPool.load(aHandle.file().getAbsolutePath(), 1);
return new AndroidSound(soundPool, manager, sampleId);
}
}
} catch (Exception ex) {
try {
if (!aHandle.file().exists()) {
final Intent intent = new Intent("com.mytian.game.exception");
MGardenApplication.instance.sendBroadcast(intent);
}
} catch (Exception e) {
}
throw new GdxRuntimeException("Error loading audio file: " + file, ex);
}
}
}
Libgdx在多次重复播放一个sound后会导致其他的sound播放失败,无声,看异常Log发现是AudioTrack创建失败导致播放失败,其实原因是系统能够创建的AudioTrack是有限的,而Libgdx播放完sound没有及时的释放其导致后面的sound无法创建。(IOS同理)
final class AndroidSound implements Sound {
final SoundPool soundPool;
final AudioManager manager;
final int soundId;
final IntArray streamIds = new IntArray(2); // 未修改前是8,导致同一个sound占用多个AudioTrack,你可以根据你的游戏业务控制这个参数的大小,做到播放完成后及时释放。
AndroidSound(SoundPool pool, AudioManager manager, int soundId) {
this.soundPool = pool;
this.manager = manager;
this.soundId = soundId;
}
@Override
public long play(float volume) {
if (streamIds.size == 2) { // 以前是==8时才会从里面释放sound,所以会导致一个sound有8个缓存的Audiotrack
try {
soundPool.stop(streamIds.pop());
} catch (Exception e) {
}
}
final int streamId = soundPool.play(soundId, volume, volume
, 1, 0, 1);
if (streamId == 0) return -1;
streamIds.insert(0, streamId);
return streamId;
}