三星S2 i9100机子声音驱动有bug,导致SoundPool播放音效过多的时候崩溃。子弹连续射击的音效在这台机子上几乎是必杀技。
cocos2d-x有解决这个问题,使用了OpenSL,这个是类似OpenGL的一种声音标准,android2.3以上的系统有支持。 非i9100的设备使用SoundPool,i9100设备使用OpenSL播放音效。
cocos2d-x的声音播放引擎CocosDenshion尚有瑕疵,OpenSL读取文件的时候使用了AssetsManager,但是不支持SD卡的音效文件读取。而我的游戏的实现是这样的,所有资源打包到pak文件中,播放声音的时候如果SD卡没有对应文件则把声音拷贝到SD卡,然后播放SD卡的声音文件。 显然cocos2d-x不兼容这种方式。
修改方式很简单,OpenSLEngine.cpp文件中修改getFileDescriptor函数:
int getFileDescriptor(const char * filename, off_t & start, off_t & length, bool inapk)
{
if (!inapk) {
FILE* fp = fopen(filename, "rb");
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fseek(fp, 0, SEEK_SET);
start = 0;
return fileno(fp);
} else {
JniMethodInfo methodInfo;
if (! getStaticMethodInfo(methodInfo, ASSET_MANAGER_GETTER, "()Landroid/content/res/AssetManager;"))
{
methodInfo.env->DeleteLocalRef(methodInfo.classID);
return FILE_NOT_FOUND;
}
jobject assetManager = methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
AAssetManager* (*AAssetManager_fromJava)(JNIEnv* env, jobject assetManager);
AAssetManager_fromJava = (AAssetManager* (*)(JNIEnv* env, jobject assetManager))
dlsym(s_pAndroidHandle, "AAssetManager_fromJava");
AAssetManager* mgr = AAssetManager_fromJava(methodInfo.env, assetManager);
assert(NULL != mgr);
AAsset* (*AAssetManager_open)(AAssetManager* mgr, const char* filename, int mode);
AAssetManager_open = (AAsset* (*)(AAssetManager* mgr, const char* filename, int mode))
dlsym(s_pAndroidHandle, "AAssetManager_open");
AAsset* Asset = AAssetManager_open(mgr, filename, AASSET_MODE_UNKNOWN);
if (NULL == Asset)
{
LOGD("file not found! Stop preload file: %s", filename);
return FILE_NOT_FOUND;
}
// open asset as file descriptor
int (*AAsset_openFileDescriptor)(AAsset* asset, off_t* outStart, off_t* outLength);
AAsset_openFileDescriptor = (int (*)(AAsset* asset, off_t* outStart, off_t* outLength))
dlsym(s_pAndroidHandle, "AAsset_openFileDescriptor");
int fd = AAsset_openFileDescriptor(Asset, &start, &length);
assert(0 <= fd);
void (*AAsset_close)(AAsset* asset);
AAsset_close = (void (*)(AAsset* asset))
dlsym(s_pAndroidHandle, "AAsset_close");
AAsset_close(Asset);
return fd;
}
}
如代码所示,如果资源不是apk包内的资源,则使用fopen的方式获取文件句柄fd。