上一节 解决了读文件的问题,游戏也跑起来了,可是音效和背景音乐,死活播放不出来。回想一下,的确没有考虑到游戏的音效问题。让我们再折腾一下,播放zip里的音效问题。cocos-x 安卓的背景音乐处理交给了Cocos2dxMusic.java,音效处理交给了Cocos2dxSound.java。别的我们不看,就关注声音资源加载的地方。
先分析一下Cocos2dxMusic.java 的声音资源加载方法
private MediaPlayer createMediaplayer(final String pPath) ;
这个方法就是啦。大概就是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。
/**
* create mediaplayer for music
*
* @param pPath
* the pPath relative to assets
* @return
*/
private MediaPlayer createMediaplayer(final String pPath) {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
//加载sd卡里的声音资源
if (pPath.startsWith("/")) {
final FileInputStream fis = new FileInputStream(pPath);
mediaPlayer.setDataSource(fis.getFD());
fis.close();
} else {
//加载apk里的声音资源
final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
}
mediaPlayer.prepare();
mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
} catch (final Exception e) {
mediaPlayer = null;
Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
}
return mediaPlayer;
}
在分析一下Cocos2dxSound.java的声音资源加载方法
->public int playEffect(final String pPath, final boolean pLoop)
->public int preloadEffect(final String pPath)
->public int createSoundIDFromAsset(final String pPath)
也是是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。
public int createSoundIDFromAsset(final String pPath) {
int soundID = Cocos2dxSound.INVALID_SOUND_ID;
try {
if (pPath.startsWith("/")) {
//加载sd卡声音资源
soundID = this.mSoundPool.load(pPath, 0);
} else {
//加载apk里的声音资源
soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
}
} catch (final Exception e) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
}
// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
if (soundID == 0) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
}
return soundID;
}
回想起来,我们已经有fullpath ,我们要像读取向下面的资源
fullpath = /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
方法1:直接读取zip里的声音流 让播放器们播放
InputStream 这样的东西java 程序猿们太熟悉不过了,何况是ZipInputStream。我们先将对应的文件用ZipInputStream 读出,然后直接让MediaPlayer 或SoundPool 加载播放流不就可以了吗?可是童话都是骗人的。突然想起邓超演的《美人鱼》,我实在是想不通。好好的白富美在身边不要,偏偏去要一条上半身是人,下半身是鱼的人鱼。没有接口下半辈子能幸福吗!!!实在是想不通。在这个问题上,也是遇到了这样的人鱼。MediaPlayer 和 SoundPool 都没有直接播放流的方法 ,MediaPlayer的setDataSource方法。SoundPool的load方法 都没有播放流的接口。下半辈子不幸福,感觉不会再爱了。好吧换第二个方法
方法2:将zip对应的声音文件临时解压到sd卡里, 然后返回fullpath给 Cocos2dxMusic.java或Cocos2dxSound.java 的加载方法。
新欢没有接口,我们找旧爱。旧爱的接口就是要个声音文件的fullpath吗,太容易满足了。让我们动手改改
Cocos2dxMusic.java 的 createMediaplayer
/**
* create mediaplayer for music
*
* @param pPath
* the pPath relative to assets
* @return
*/
private MediaPlayer createMediaplayer(final String pPath) {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
if (pPath.startsWith("/")) {//改了这里
String ppPath = PathUtils.getZipFilePath(pPath);
final FileInputStream fis = new FileInputStream(ppPath);
mediaPlayer.setDataSource(fis.getFD());
fis.close();
} else {
final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
}
mediaPlayer.prepare();
mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
} catch (final Exception e) {
mediaPlayer = null;
Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
}
return mediaPlayer;
}
Cocos2dxSound.java 的createSoundIDFromAsset
public int createSoundIDFromAsset(final String pPath) {
int soundID = Cocos2dxSound.INVALID_SOUND_ID;
try {
if (pPath.startsWith("/")) {//改了下面的
String ppPath = PathUtils.getZipFilePath(pPath);
soundID = this.mSoundPool.load(ppPath, 0);
} else {
soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
}
} catch (final Exception e) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
}
// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
if (soundID == 0) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
}
return soundID;
}
解压的方法 getZipFilePath
/***
* 获得路径 也许是zip里面的的路径
* @param pPath
* @return
* @throws ZipException
* @throws IOException
*/
public static String getZipFilePath(String pPath) throws ZipException, IOException {
String zipfilepath = "";
String filename = "";
int index = pPath.indexOf("#");
String ppPath = pPath;
//是否在zip里
if(index != -1){
zipfilepath = pPath.substring(0,index);
filename = pPath.substring(index+2);
Log.d("RecordManager","zipfilepath:"+ zipfilepath + "--filename:" + filename);
String filesavename = filename.replaceAll("/","_");
ppPath = PathUtils.getTempPath() +filesavename+".temp";
File filetemp = new File(ppPath);
//是否有临时解压文件 避免重复解压
if(!filetemp.exists()){
filetemp.createNewFile();
ZipFile file = new ZipFile(zipfilepath);
FileHeader fileHeader = file.getFileHeader(filename);
net.lingala.zip4j.io.ZipInputStream zipInputStream = file.getInputStream(fileHeader);
FileOutputStream fo = new FileOutputStream(ppPath);
byte[] b = new byte[4096];
int readLine = -1;
while ((readLine = zipInputStream.read(b)) != -1) {
fo.write(b,0,readLine);
}
fo.close();
zipInputStream.close();
}
}
return ppPath;
}
注:我用了开源的zip操作库 zip4j ,我git上有提交 。
好啦。我们这就跑起来。欧啦!声音出来了。这个方案凑合着用,如有更高明的方法,请回复我。
解压方法 c++版
static std::string getZipFilePath(const std::string& fullPath,const std::string& saveDir);
static unsigned char* getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize);
#include "support/zip_support/ZipUtils.h"
#include "platform/CCCommon.h"
#include "support/zip_support/unzip.h"
#include "platform/CCFileUtils.h"
/**
* fullpath 音效的绝对路径
* saveDir 解压到的地方
* return 解压后的资源返回的路径
**/
std::string JNItools::getZipFilePath(const std::string& fullPath,const std::string& saveDir){
unsigned char * pBuffer = NULL;
unsigned long pSize = 0;
std::string savepath = fullPath;
std::string pszFileNameTemp = "";
std::string pszZipFilePath = "";
size_t pos = fullPath.find_last_of("#");
if (pos != std::string::npos)
{
// file_path = /storage/emulated/0/DonutABC/unitRes/game_22.zip
pszZipFilePath = fullPath.substr(0, pos);
// file = res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
pszFileNameTemp = fullPath.substr(pos+2);
// CCLOG("pszZipFilePath:%s,pszFileNameTemp:%s",pszZipFilePath.c_str(),pszFileNameTemp.c_str());
//替换‘/'
std::string filename = pszFileNameTemp;
std::string old_value = "/";
std::string new_value = "_";
for(string::size_type pos(0); pos!=string::npos; pos+=new_value.length()){
if((pos=filename.find(old_value,pos))!=string::npos)
filename.replace(pos,old_value.length(),new_value);
else
break;
}
// CCLOG("filename:%s",filename.c_str());
savepath = saveDir + filename + ".temp";
const char* output = savepath.c_str();
CCLOG("filename:%s",output);
//文件如果存在就不解压了
int i = access(savepath.c_str(), 0);
CCLOG("filename:%d",i);
if( i == -1){
pBuffer = JNItools::getFileDataFromZip(pszZipFilePath.c_str(),pszFileNameTemp.c_str(),&pSize);
FILE *savefile = fopen(output, "wb");
fwrite(pBuffer, 1, (size_t)pSize, savefile);
fflush(savefile);
fclose(savefile);
delete pBuffer;
}
}
return savepath;
}
/**
* 解压zip 获得资源数据
**/
unsigned char* JNItools::getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize)
{
unsigned char * pBuffer = NULL;
unzFile pFile = NULL;
*pSize = 0;
do
{
// CCLOG("1");
CC_BREAK_IF(!pszZipFilePath || !pszFileName);
// CCLOG("11");
CC_BREAK_IF(strlen(pszZipFilePath) == 0);
// CCLOG("1111");
pFile = unzOpen(pszZipFilePath);
CC_BREAK_IF(!pFile);
// CCLOG("2");
int nRet = unzLocateFile(pFile, pszFileName, 1);
CC_BREAK_IF(UNZ_OK != nRet);
// CCLOG("3");
char szFilePathA[260];
unz_file_info FileInfo;
nRet = unzGetCurrentFileInfo(pFile, &FileInfo, szFilePathA, sizeof(szFilePathA), NULL, 0, NULL, 0);
CC_BREAK_IF(UNZ_OK != nRet);
// CCLOG("4");
nRet = unzOpenCurrentFile(pFile);
CC_BREAK_IF(UNZ_OK != nRet);
// CCLOG("5");
pBuffer = new unsigned char[FileInfo.uncompressed_size];
int CC_UNUSED nSize = unzReadCurrentFile(pFile, pBuffer, FileInfo.uncompressed_size);
CCAssert(nSize == 0 || nSize == (int)FileInfo.uncompressed_size, "the file size is wrong");
// CCLOG("6");
*pSize = FileInfo.uncompressed_size;
unzCloseCurrentFile(pFile);
} while (0);
if (pFile)
{
unzClose(pFile);
}
return pBuffer;
}
调方法
public static native String getZipFilePath(String pPath,String saveDir);
/**
* jni 调用
*/
jstring Java_org_cocos2dx_lib_PathUtils_getZipFilePath(JNIEnv* env, jobject thiz,jstring path,jstring savedir)
{
// CCLOG("filename:%s","Java_org_cocos2dx_lib_PathUtils_getZipFilePath");
std::string char_path = JniHelper::jstring2string(path);
std::string char_savedir = JniHelper::jstring2string(savedir);
std::string str_path = JNItools::getZipFilePath(char_path,char_savedir);
// CCLOG("----getPath:%s",str_path.c_str());
env->DeleteLocalRef(path);
env->DeleteLocalRef(savedir);
return (env)->NewStringUTF(str_path.c_str());
}
public int createSoundIDFromAsset(final String pPath) {
int soundID = Cocos2dxSound.INVALID_SOUND_ID;
try {
if (pPath.startsWith("/")) {
String ppPath = PathUtils.getZipFilePath(pPath,PathUtils.getTempPath());
soundID = this.mSoundPool.load(ppPath, 0);
} else {
soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
}
} catch (final Exception e) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
}
// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
if (soundID == 0) {
soundID = Cocos2dxSound.INVALID_SOUND_ID;
}
return soundID;
}
Cocos2dxMusic.java 修改
/**
* create mediaplayer for music
*
* @param pPath
* the pPath relative to assets
* @return
*/
private MediaPlayer createMediaplayer(final String pPath) {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
if (pPath.startsWith("/")) {
String ppPath = PathUtils.getZipFilePath(pPath,PathUtils.getTempPath());
final FileInputStream fis = new FileInputStream(ppPath);
mediaPlayer.setDataSource(fis.getFD());
fis.close();
} else {
final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
}
mediaPlayer.prepare();
mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
} catch (final Exception e) {
mediaPlayer = null;
Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
}
return mediaPlayer;
}
整体来看效率没什么提高,就是不用 zip4j 解压了。
还是老样子。下载完整的改动
本文出处
http://blog.youkuaiyun.com/frabbit_on_fire/article/details/51538300