cocos2dx 音頻模組分析(4): 音效部分,cocos2dx音效
cocos2dx 音頻模組分析(4): 音效部分
我們上面幾篇分析了cocos2dx音頻模組的音樂部分,從這篇開始,我們分析下音效部分:1、//預先載入音效檔案:pszFilePath 音效檔案名稱void SimpleAudioEngine::preloadEffect(const char* pszFilePath){ //擷取音效檔案的全路徑,如果是apk包裡的路徑,則不包含assets/ std::string fullPath = getFullPathWithoutAssetsPrefix(pszFilePath); preloadEffectJNI(fullPath.c_str());}--->>>// 這裡通過jni調用java端的方法void preloadEffectJNI(const char *path) { // void preloadEffect(String) JniMethodInfo methodInfo; if (! getStaticMethodInfo(methodInfo, "preloadEffect", "(Ljava/lang/String;)V")) { return ; } jstring stringArg = methodInfo.env->NewStringUTF(path); methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, stringArg); methodInfo.env->DeleteLocalRef(stringArg); methodInfo.env->DeleteLocalRef(methodInfo.classID); }----->>>//Cocos2dxHelper類中的方法:public static void preloadEffect(final String path) { //Cocos2dxSound類是專門處理音效的類Cocos2dxHelper.sCocos2dSound.preloadEffect(path);}--->>>//Cocos2dxSound類的方法:public int preloadEffect(final String pPath) { //private final HashMap<String, Integer> mPathSoundIDMap = new HashMap<String, Integer>();//這個是音效路徑對應音效ID的mapInteger soundID = this.mPathSoundIDMap.get(pPath);if (soundID == null) { //soundID = this.createSoundIDFromAsset(pPath);// save value just in case if file is really loaded// 如果createSoundIDFromAsset函數調用成功,則添加到mPathSoundIDMap中。if (soundID != Cocos2dxSound.INVALID_SOUND_ID) {this.mPathSoundIDMap.put(pPath, soundID);}}return soundID;}----->>>>根據我們傳入的音效檔案路徑,載入音效public int createSoundIDFromAsset(final String pPath) {int soundID = Cocos2dxSound.INVALID_SOUND_ID;try { //根據傳入的路徑不同,做不同處理,一個是絕對路徑一個是包裡的路徑,載入音效檔案//The SoundPool class manages and plays audio resources for applications.//private SoundPool mSoundPool;音效緩衝池if (pPath.startsWith("/")) {soundID = this.mSoundPool.load(pPath, 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 existif (soundID == 0) {soundID = Cocos2dxSound.INVALID_SOUND_ID;}return soundID;}2、播放音效檔案。pszFilePath:音效檔案名稱;bLoop 是否迴圈播放unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop){ std::string fullPath = getFullPathWithoutAssetsPrefix(pszFilePath); return playEffectJNI(fullPath.c_str(), bLoop);}----->>>playEffectJNI: unsigned int playEffectJNI(const char* path, bool bLoop) { // int playEffect(String) JniMethodInfo methodInfo; int ret = 0; if (! getStaticMethodInfo(methodInfo, "playEffect", "(Ljava/lang/String;Z)I")) { return ret; } jstring stringArg = methodInfo.env->NewStringUTF(path); ret = methodInfo.env->CallStaticIntMethod(methodInfo.classID, methodInfo.methodID, stringArg, bLoop); methodInfo.env->DeleteLocalRef(stringArg); methodInfo.env->DeleteLocalRef(methodInfo.classID); return (unsigned int)ret; }------>>>>playEffect:public int playEffect(final String pPath, final boolean pLoop) { //從mPathSoundIDMap中,根據音效path得到音效IDInteger soundID = this.mPathSoundIDMap.get(pPath);int streamID = Cocos2dxSound.INVALID_STREAM_ID;if (soundID != null) {// play sound// 如果音效ID存在,則表明我們已經積極式載入過這個音效檔案,則調用mSoundPool.play直接播放/*private int doPlayEffect(final String pPath, final int soundId, final boolean pLoop) {// play sound// Play a sound from a sound ID.// return non-zero streamID if successful, zero if failed如果成功會返回一個streamIDint streamID = this.mSoundPool.play(soundId, this.mLeftVolume, this.mRightVolume, Cocos2dxSound.SOUND_PRIORITY, pLoop ? -1 : 0, Cocos2dxSound.SOUND_RATE);// record stream id// 記錄上面調用mSoundPool.play返回的streamID,至於為什麼需要這樣,看下面源碼的說明:// sound path and stream ids map// a file may be played many times at the same time// so there is an array map to a file path// private final HashMap<String, ArrayList<Integer>> mPathStreamIDsMap = new HashMap<String, ArrayList<Integer>>();ArrayList<Integer> streamIDs = this.mPathStreamIDsMap.get(pPath);if (streamIDs == null) {streamIDs = new ArrayList<Integer>();this.mPathStreamIDsMap.put(pPath, streamIDs);}streamIDs.add(streamID);return streamID;}*/streamID = this.doPlayEffect(pPath, soundID.intValue(), pLoop);} else {// the effect is not prepared,如果音效沒有積極式載入,則需要先載入soundID = this.preloadEffect(pPath); //載入音效檔案if (soundID == Cocos2dxSound.INVALID_SOUND_ID) {// can not preload effectreturn Cocos2dxSound.INVALID_SOUND_ID;}// only allow one playEffect at a time, or the semaphore will not work correctlysynchronized(this.mSoundPool) {// add this effect into mEffecToPlayWhenLoadedArray, and it will be played when loaded completely// 這個應該和mSoundPool載入音效檔案有關,我也不是很明白/*不過在初始化時設定了一個:this.mSoundPool.setOnLoadCompleteListener(new OnLoadCompletedListener());//載入完成回呼函數,應該和這個有關,這裡我們就不關心了。 只需要知道如果我們提前載入音效檔案//也可以直接調用play函數,在調用play函數時會調用載入函數,並放入載入隊列中,載入完成後進行播放。//這樣做會有一些延時,所以我們還是最好先載入,然後在播放。這個延時只在第一次播放時有,以後就不會了,//不過最好還是先載入。*//*@Override//載入完成回呼函數。public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {if (status == 0){// only play effect that are in mEffecToPlayWhenLoadedArrayfor ( SoundInfoForLoadedCompleted info : mEffecToPlayWhenLoadedArray) {if (sampleId == info.soundID) {// set the stream id which will be returned by playEffect()// 載入完成後調用doPlayEffect進行播放,並從mEffecToPlayWhenLoadedArray載入列表中// 移除。mStreamIdSyn = doPlayEffect(info.path, info.soundID, info.isLoop);// remove it from array, because we will break here// so it is safe to domEffecToPlayWhenLoadedArray.remove(info);break;}}} else {mStreamIdSyn = Cocos2dxSound.INVALID_SOUND_ID;}mSemaphore.release();}}*/mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(pPath, soundID.intValue(), pLoop));try {// wait OnloadedCompleteListener to set streamIDthis.mSemaphore.acquire();streamID = this.mStreamIdSyn;} catch(Exception e) {return Cocos2dxSound.INVALID_SOUND_ID;}}}return streamID;}