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的map
		Integer 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 exist
		if (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得到音效ID
		Integer 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如果成功会返回一个streamID
				int 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 effect
				return Cocos2dxSound.INVALID_SOUND_ID;
			}

			// only allow one playEffect at a time, or the semaphore will not work correctly
			synchronized(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 mEffecToPlayWhenLoadedArray
							for ( 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 do
									mEffecToPlayWhenLoadedArray.remove(info);

									break;
								}
							}
						} else {
							mStreamIdSyn = Cocos2dxSound.INVALID_SOUND_ID;
						}

						mSemaphore.release();
					}
				}
				*/
				mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(pPath, soundID.intValue(), pLoop));

				try {
					// wait OnloadedCompleteListener to set streamID
					this.mSemaphore.acquire();

					streamID = this.mStreamIdSyn;
				} catch(Exception e) {
					return Cocos2dxSound.INVALID_SOUND_ID;
				}
			}
		}

		return streamID;
	}
时间: 2024-10-13 05:28:47

cocos2dx 音频模块分析(4): 音效部分的相关文章

cocos2dx 音频模块分析(5): 音效部分

我们上一篇中分析的音效部分的预加载和播放函数,这一篇来分析下其他函数: 1. 暂停某个播放中的音效 //注意这里的nSoundId不是java端的soundID,而是streamID //不要被参数的名字迷惑了. void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) { pauseEffectJNI(nSoundId); } -->> void pauseEffectJNI(unsigned int nSoundId) { //

cocos2dx 音频模块分析(3): 背景音乐部分

cocos2dx 音频模块分析(3):背景音乐部分 我在(2)已经分析了背景音乐文件的预加载preloadBackgroundMusic和播放playBackgroundMusic两个函数, 这里接着分析,还是基于android平台: 1. //暂停函数,用于音乐的暂停 void SimpleAudioEngine::pauseBackgroundMusic() { //在SimpleAudioEngineJni.cpp源文件中定义 pauseBackgroundMusicJNI(); } //

cocos2dx 音频模块分析(2):背景音乐

cocos2dx 音频模块分析(2): 背景音乐部分 我在(1)已经分析了一些东西,这里接着分析,这一篇我们主要分析背景音乐文件的播放, 还是基于android平台: 1. 这里只是背景音乐的预加载,为什么要进行预加载呢? 主要是加载音乐文件是比较耗时的,如果我们没有预加载就直接播放也是可以的, 但是会有一定的延时,因为如果没有预加载,就直接播放,也是会先进行加载音乐文件, 然后进行播放. void SimpleAudioEngine::preloadBackgroundMusic(const

WebRTC源码分析:音频模块结构分析

一.概要介绍WebRTC的音频处理流程,见下图: webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输.上图中有三个Channel,每个Channel包含编解码和RTP/RTCP发送功能. 以一个Channel而言,应用程序中将包含三个活动线程,录音线程,音频接收线程和播放线程. 1)录音线程:负责麦克风音频的采集,见图中红色路径,采集到音频后,缓存到一定长度,进行音频处理,主要包括EC,AGC和NS等.然后送到Chann

cocos2dx基础篇(19)——音乐音效SimpleAudioEngine

[唠叨] 本节比较简单,主要讲讲cocos2dx引擎中的音乐音效. [致谢] http://gl.paea.cn/contents/f86d1f6e2a52e7ea.html [术语] 单例类:说的通俗一点,它就是一个全局静态类.第一次调用时会创建一个全局静态对象,整个游戏的运行过程中会一直存在,全局都可以访问. [SimpleAudioEngine] 音乐音效是每个游戏中不可或缺的部分,一个好的声音会给玩家留下深刻的印象,当一听到游戏的声音,就会不自觉得说出游戏的名称来.就像<中国好声音>一

css扁平化博客学习总结(一)模块分析

一.模块分析 1.每开发一个项目之前,首先要对项目进行一个大致规划,它到底要做什么功能,它有什么具体需求. 2.所以需要进行模块化分析,把这些东西具象化,把一个问题模块化,对需求有一个宏观的了解. 3.有一个模块的概念后,把模块进行细分,细分成更小的模块,然后逐一击破. 不进行模块分析的恶劣影响: 不做模块分析,一上手就想到哪儿做到哪儿,很容易一叶障目,不能看到全貌,你会发现做着做着就失控了.因为有些地方出现了矛盾,你会发现要修改的成本很高,还不如推倒重写. 新手很容易着眼于细节,首先不应该从细

学生信息管理系统(四)——模块分析

学生信息管理系统已经敲完了,也进行了第一次验收,结果不是太理想.之前的总结也没有及时发表.现在重新复习一遍,把它发表. 从今天开始,我们就进入了代码分析阶段.现在我们就来分析一下模块中的几个函数. Public Function ExecuteSQL(ByVal SQL As String, MsgString As String) As ADODB.Recordset 'executes SQL and returns Recordset Dim cnn As ADODB.Connection

cocos2d-x 源码分析 : control 源码分析 ( 控制类组件 controlButton)

源码版本来自3.1rc 转载请注明 cocos2d-x源码分析总目录 http://blog.csdn.net/u011225840/article/details/31743129 1.继承结构 control的设计整体感觉挺美的,在父类control定义了整个控制事件的基础以及管理,虽然其继承了Layer,但其本身和UI组件的实现并没有关联.在子类(controlButton,controlSwitch,controlStepper等中实现不同的UI组件).下面通过源码来分析control与

ABP之模块分析

ABP之模块分析 本篇作为我ABP介绍的第三篇文章,这次想讲下模块的,ABP文档已经有模块这方面的介绍,但是它只讲到如何使用模块,我想详细讲解下它模块的设计思路. ABP 框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块.在通常情况 下,一个程序集就可以看成是一个模块.在 ABP 框架中,一个模块通过一个类来定义,而这 个类要继承自 AbpModule. 其实它的设计思路很简单: 1.加载bin目录下的所有dll 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15